The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * Copyright (C) 2008 Search Solution Corporation. All rights reserved by Search Solution.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

/*
 * porting.c - Functions supporting platform porting
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h>

#if defined(WINDOWS)
#include <tchar.h>
#include <float.h>
#include <io.h>
#include <conio.h>
#include <math.h>
#elif defined(AIX)
#define _BOOL
#include <unistd.h>
#include <curses.h>
#else
#include <unistd.h>
#include <curses.h>
#endif

#include "porting.h"

#if !defined(HAVE_ASPRINTF)
#include <stdarg.h>
#endif

#if !defined(CAS_BROKER) && !defined(CAS_CCI)
#include "storage_common.h"
#endif

#if defined(AIX) && !defined(DONT_HOOK_MALLOC)
#undef malloc
void *
aix_malloc (size_t size)
{
  /* malloc 0 size memory will be failed in AIX */
  if (size == 0)
    {
      size = 1;
    }
  return malloc (size);
}

#define malloc(a) aix_malloc(a)
#endif

#if defined (WINDOWS)
/*
 * poll() -
 *    return: return poll result
 *    fds(in): socket descriptors to wait
 *    nfds(in): number of descriptors
 *    timeout(in): timeout in milliseconds
 */
int
poll (struct pollfd *fds, nfds_t nfds, int timeout)
{
  struct timeval to, *tp;
  fd_set rset, wset, eset;
  fd_set *rp, *wp, *ep;
  unsigned long int i;
  int r, max_fd;

  tp = NULL;
  if (timeout >= 0)
    {
      to.tv_sec = timeout / 1000;
      to.tv_usec = (timeout % 1000) * 1000;
      tp = &to;
    }

  FD_ZERO (&rset);
  FD_ZERO (&wset);
  FD_ZERO (&eset);
  rp = wp = ep = NULL;
  max_fd = 0;

  for (i = 0; i < nfds; i++)
    {
      if (fds[i].events & POLLIN)
	{
	  if (rp == NULL)
	    {
	      rp = &rset;
	    }
	  FD_SET (fds[i].fd, rp);
	  max_fd = MAX (fds[i].fd, max_fd);
	}
      if (fds[i].events & POLLOUT)
	{
	  if (wp == NULL)
	    {
	      wp = &wset;
	    }
	  FD_SET (fds[i].fd, wp);
	  max_fd = MAX (fds[i].fd, max_fd);
	}
      if (fds[i].events & POLLPRI)
	{
	  if (ep == NULL)
	    {
	      ep = &eset;
	    }
	  FD_SET (fds[i].fd, ep);
	  max_fd = MAX (fds[i].fd, max_fd);
	}
    }

  r = select (max_fd + 1, rp, wp, ep, tp);
  for (i = 0; i < nfds; i++)
    {
      fds[i].revents = 0;
      if ((fds[i].events & POLLIN) && FD_ISSET (fds[i].fd, rp))
	{
	  fds[i].revents |= POLLIN;
	}
      if ((fds[i].events & POLLOUT) && FD_ISSET (fds[i].fd, wp))
	{
	  fds[i].revents |= POLLOUT;
	}
      if ((fds[i].events & POLLPRI) && FD_ISSET (fds[i].fd, ep))
	{
	  fds[i].revents |= POLLPRI;
	}
    }

  return r;
}

/*
 * gettimeofday - Windows port of Unix gettimeofday()
 *   return: none
 *   tp(out): where time is stored
 *   tzp(in): unused
 */
int
gettimeofday (struct timeval *tp, void *tzp)
{
#if 1				/* _ftime() version */
  struct _timeb tm;
  _ftime (&tm);
  tp->tv_sec = (long) tm.time;
  tp->tv_usec = (long) tm.millitm * 1000;
  return 0;
#else /* GetSystemTimeAsFileTime version */
  FILETIME ft;
  unsigned __int64 tmpres = 0;
  static int tzflag;

  GetSystemTimeAsFileTime (&ft);

  tmpres |= ft.dwHighDateTime;
  tmpres <<= 32;
  tmpres |= ft.dwLowDateTime;

  tmpres -= DELTA_EPOCH_IN_MICROSECS;

  tmpres /= 10;

  tv->tv_sec = (tmpres / 1000000UL);
  tv->tv_usec = (tmpres % 1000000UL);

  return 0;
#endif
}

#define LOCKING_SIZE 2000
/*
 * lockf() - lockf() WINDOWS implementation
 *   return: 0 if success, -1 otherwise
 *   fd(in): file descriptor
 *   cmd(in): locking command to perform
 *   size(in): number of bytes
 */
int
lockf (int fd, int cmd, long size)
{
  switch (cmd)
    {
    case F_ULOCK:
      return (_locking (fd, _LK_UNLCK, (size ? size : LOCKING_SIZE)));

    case F_LOCK:
      return (_locking (fd, _LK_LOCK, (size ? size : LOCKING_SIZE)));

    case F_TLOCK:
      return (_locking (fd, _LK_NBLCK, (size ? size : LOCKING_SIZE)));

    case F_TEST:
      /* not implemented on WINDOWS */
      return (-1);

    default:
      errno = EINVAL;
      return (-1);
    }
}

#if !defined(CAS_BROKER) && !defined(CAS_CCI)
#include "environment_variable.h"
/*
 * cuserid - returns a pointer to a string containing a user name
 *                    associated with the effective user ID of the process
 *   return: string
 *   string(in):
 *
 * Note: Changed to allow the user name to be specified using an environment
 *       variable.  This is primarily so that abortdb can have something
 *       meaningful when it displays connection info.
 */
char *
cuserid (char *string)
{
  const char *env;

  /* make 'em supply a buffer */
  if (string != NULL)
    {
      env = getenv ("USERNAME");
      if (env == NULL)
	{
	  strcpy (string, "noname");
	}
      else
	{
	  strlcpy (string, env, L_cuserid);
	}

      return string;
    }

  return string;
}
#endif

int
getlogin_r (char *buf, size_t bufsize)
{
  return GetUserName (buf, &bufsize);
}

#if 0
/*
 * umask - This is a stub for umask()
 *   return:
 *   mask(in):
 *
 *  Note: It belongs in the os unit and should be moved there.
 */
int
umask (int mask)
{
  return (0);
}
#endif

/*
 * fsync - This is a stub for fsync()
 *   return:
 *   fd(in):
 *
 * Note: It belongs in the os unit and should be moved there.
 */
int
fsync (int filedes)
{
  return 0;
}

/*
 * pathconf -
 *   return:
 *   path(in):
 *   name(in):
 *
 * Note:
 */
long
pathconf (char *path, int name)
{

  long namelen;
  long filesysflags;

  switch (name)
    {
    case _PC_PATH_MAX:
      /*
       * NT and OS/2 file systems claim to be able to handle 255 char
       * file names.  But none of the system calls seem to be able to
       * handle a path of more than 255 chars + 1 NULL.  Nor does there
       * appear to be a system function to return the real max length.
       * MAX_PATH is defined in stdlib.h on the NT system.
       */
      return ((MAX_PATH - 1));

    case _PC_NAME_MAX:
      if (GetVolumeInformation
	  (NULL, NULL, 0, NULL, (LPDWORD) & namelen, (LPDWORD) & filesysflags,
	   NULL, 0))
	{
	  /* WARNING!, for "old" DOS style file systems, namelen will be 12
	   * right now, totaling the 8 bytes for name with the 3 bytes for
	   * for extension plus a dot.  This ISN'T what the caller wants,
	   * It really wants the maximum size of an unqualified pathname.
	   * I'm not sure what this works out to be under the new file system.
	   * We probably need to make a similar adjustment but hopefully
	   * we'll have more breathing room.
	   */
	  if (namelen == 12)
	    namelen = 8;

	  return (namelen);
	}
      else
	{
	  return (8);		/* Length of MSDOS file name */
	}

    case _PC_NO_TRUNC:
      return (TRUE);

    default:
      return (-1);
    }
}

#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2
#define SIGABRT_BIT 1
#define SIGFPE_BIT 2
#define SIGILL_BIT 4
#define SIGINT_BIT 8
#define SIGSEGV_BIT 16
#define SIGTERM_BIT 64

/*
 * sigfillset -
 *   return:
 *   set(in/out):
 */
int
sigfillset (sigset_t * set)
{
  if (set)
    {
      set->mask = 0;
      return (0);
    }
  else
    {
      return (-1);
    }
}

/* satic function for sigprocmask */
static int setmask (sigset_t * set, sigset_t * oldset);
static int block_signals (sigset_t * set, sigset_t * oldset);
static int unblock_signals (sigset_t * set, sigset_t * oldset);
static void sync_mask (sigset_t * set);

/*
 * setmask -
 *   return:
 *   set(in/out):
 *   oldset(out):
 */
static int
setmask (sigset_t * set, sigset_t * oldset)
{
  sigset_t tmp;
  unsigned int test;

  if (set)
    {
      test = set->mask;
    }
  else
    {
      test = -1;
    }

  tmp.mask = set->mask;

  tmp.abrt_state =
    signal (SIGABRT, (tmp.mask |= SIGABRT_BIT) ? SIG_IGN : SIG_DFL);
  if (tmp.abrt_state < 0)
    goto whoops;
  if (!set)
    (void) signal (SIGABRT, tmp.abrt_state);

  tmp.fpe_state =
    signal (SIGFPE, (tmp.mask |= SIGFPE_BIT) ? SIG_IGN : SIG_DFL);
  if (tmp.fpe_state < 0)
    goto whoops;
  if (!set)
    (void) signal (SIGFPE, tmp.fpe_state);

  tmp.ill_state =
    signal (SIGILL, (tmp.mask |= SIGILL_BIT) ? SIG_IGN : SIG_DFL);
  if (tmp.ill_state < 0)
    goto whoops;
  if (!set)
    (void) signal (SIGILL, tmp.ill_state);

  tmp.int_state =
    signal (SIGINT, (tmp.mask |= SIGINT_BIT) ? SIG_IGN : SIG_DFL);
  if (tmp.int_state < 0)
    goto whoops;
  if (!set)
    (void) signal (SIGINT, tmp.int_state);

  tmp.sev_state =
    signal (SIGSEGV, (tmp.mask |= SIGSEGV_BIT) ? SIG_IGN : SIG_DFL);
  if (tmp.sev_state < 0)
    goto whoops;
  if (!set)
    (void) signal (SIGSEGV, tmp.sev_state);

  tmp.term_state =
    signal (SIGTERM, (tmp.mask |= SIGTERM_BIT) ? SIG_IGN : SIG_DFL);
  if (tmp.term_state < 0)
    goto whoops;
  if (!set)
    (void) signal (SIGTERM, tmp.term_state);

  if (oldset)
    {
      oldset->term_state = tmp.term_state;
      oldset->sev_state = tmp.sev_state;
      oldset->int_state = tmp.int_state;
      oldset->ill_state = tmp.ill_state;
      oldset->fpe_state = tmp.fpe_state;
      oldset->abrt_state = tmp.abrt_state;
      sync_mask (oldset);
    }

  return (0);

whoops:
  /*
   * I'm supposed to restore the signals to the original
   * state if something fails, but I'm blowing it off for now.
   */

  return (-1);
}

/*
 * block_signals -
 *   return:
 *   set(in/out):
 *   oldset(out):
 */
static int
block_signals (sigset_t * set, sigset_t * oldset)
{
  sigset_t tmp;
  unsigned int test;

  if (set)
    {
      test = set->mask;
    }
  else
    {
      test = -1;
    }

  tmp.mask = 0;

  if (test & SIGABRT_BIT)
    {
      tmp.mask |= SIGABRT_BIT;
      tmp.abrt_state = signal (SIGABRT, SIG_IGN);
      if (tmp.abrt_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGABRT, tmp.abrt_state);
    }

  if (test & SIGFPE_BIT)
    {
      tmp.mask |= SIGFPE_BIT;
      tmp.fpe_state = signal (SIGFPE, SIG_IGN);
      if (tmp.fpe_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGFPE, tmp.fpe_state);
    }

  if (test & SIGILL_BIT)
    {
      tmp.mask |= SIGILL_BIT;
      tmp.ill_state = signal (SIGILL, SIG_IGN);
      if (tmp.ill_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGILL, tmp.ill_state);
    }

  if (test & SIGINT_BIT)
    {
      tmp.mask |= SIGINT_BIT;
      tmp.int_state = signal (SIGINT, SIG_IGN);
      if (tmp.int_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGINT, tmp.int_state);
    }

  if (test & SIGSEGV_BIT)
    {
      tmp.mask |= SIGSEGV_BIT;
      tmp.sev_state = signal (SIGSEGV, SIG_IGN);
      if (tmp.sev_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGSEGV, tmp.sev_state);
    }

  if (test & SIGTERM_BIT)
    {
      tmp.mask |= SIGTERM_BIT;
      tmp.term_state = signal (SIGTERM, SIG_IGN);
      if (tmp.term_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGTERM, tmp.term_state);
    }

  if (oldset)
    {
      oldset->term_state = tmp.term_state;
      oldset->sev_state = tmp.sev_state;
      oldset->int_state = tmp.int_state;
      oldset->ill_state = tmp.ill_state;
      oldset->fpe_state = tmp.fpe_state;
      oldset->abrt_state = tmp.abrt_state;
      sync_mask (oldset);
    }

  return (0);

whoops:
  /*
   * I'm supposed to restore the signals to the original
   * state if something fails, but I'm blowing it off for now.
   */

  return (-1);
}

/*
 * unblock_signals -
 *   return:
 *   set(in/out):
 *   oldset(out):
 */
static int
unblock_signals (sigset_t * set, sigset_t * oldset)
{
  sigset_t tmp;
  unsigned int test;

  if (set)
    {
      test = set->mask;
    }
  else
    {
      test = -1;
    }

  tmp.mask = 0;

  if (test & SIGABRT_BIT)
    {
      tmp.mask |= SIGABRT_BIT;
      tmp.abrt_state = signal (SIGABRT, set->abrt_state);
      if (tmp.abrt_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGABRT, tmp.abrt_state);
    }

  if (test & SIGFPE_BIT)
    {
      tmp.mask |= SIGFPE_BIT;
      tmp.fpe_state = signal (SIGFPE, set->fpe_state);
      if (tmp.fpe_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGFPE, tmp.fpe_state);
    }

  if (test & SIGILL_BIT)
    {
      tmp.mask |= SIGILL_BIT;
      tmp.ill_state = signal (SIGILL, set->ill_state);
      if (tmp.ill_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGILL, tmp.ill_state);
    }

  if (test & SIGINT_BIT)
    {
      tmp.mask |= SIGINT_BIT;
      tmp.int_state = signal (SIGINT, set->int_state);
      if (tmp.int_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGINT, tmp.int_state);
    }

  if (test & SIGSEGV_BIT)
    {
      tmp.mask |= SIGSEGV_BIT;
      tmp.sev_state = signal (SIGSEGV, set->sev_state);
      if (tmp.sev_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGSEGV, tmp.sev_state);
    }

  if (test & SIGTERM_BIT)
    {
      tmp.mask |= SIGTERM_BIT;
      tmp.term_state = signal (SIGTERM, set->term_state);
      if (tmp.term_state < 0)
	goto whoops;
      if (!set)
	(void) signal (SIGTERM, tmp.term_state);
    }

  if (oldset)
    {
      oldset->term_state = tmp.term_state;
      oldset->sev_state = tmp.sev_state;
      oldset->int_state = tmp.int_state;
      oldset->ill_state = tmp.ill_state;
      oldset->fpe_state = tmp.fpe_state;
      oldset->abrt_state = tmp.abrt_state;
      sync_mask (oldset);
    }

  return (0);

whoops:
  /*
   * I'm supposed to restore the signals to the original
   * state if something fails, but I'm blowing it off for now.
   */

  return (-1);
}

/*
 * sync_mask -
 *   return:
 *   set(in/out):
 */
static void
sync_mask (sigset_t * set)
{
  set->mask |= (set->term_state == SIG_IGN) ? SIGTERM_BIT : set->mask;
  set->mask |= (set->sev_state == SIG_IGN) ? SIGSEGV_BIT : set->mask;
  set->mask |= (set->int_state == SIG_IGN) ? SIGINT_BIT : set->mask;
  set->mask |= (set->ill_state == SIG_IGN) ? SIGILL_BIT : set->mask;
  set->mask |= (set->fpe_state == SIG_IGN) ? SIGFPE_BIT : set->mask;
  set->mask |= (set->abrt_state == SIG_IGN) ? SIGABRT_BIT : set->mask;
}

/*
 * sigprocmask -
 *   return:
 *   how(in):
 *   set(in/out):
 *   oldset(out):
 *
 * Note:
 */
int
sigprocmask (int how, sigset_t * set, sigset_t * oldset)
{
  switch (how)
    {
    case SIG_BLOCK:
      return (block_signals (set, oldset));

    case SIG_UNBLOCK:
      return (unblock_signals (set, oldset));

    case SIG_SETMASK:
      return (setmask (set, oldset));
    }

  return (-1);
}

/*
 * getpagesize -
 *   return:
 */
DWORD
getpagesize ()
{
  static DWORD NT_PageSize = 0;
  SYSTEM_INFO sysinfo;

  if (NT_PageSize == 0)
    {
      GetSystemInfo (&sysinfo);
      NT_PageSize = sysinfo.dwPageSize;
    }
  return (NT_PageSize);
}

#if 0
/*
 * stat - Windows port of Unix stat()
 *   return: 0 or -1
 *   path(in): file path
 *   buffer(in): struct _stat
 */
int
stat (const char *path, struct stat *buf)
{
  struct _stat _buf;
  int rc;

  rc = _stat (path, &_buf);
  if (buf)
    *buf = _buf;
  return rc;
}
#endif

/*
 * pc_init()
 *   return: none
 */
void
pc_init (void)
{
  unsigned int fpbits;

  fpbits = _EM_OVERFLOW | _EM_UNDERFLOW | _EM_ZERODIVIDE;
  (void) _control87 (fpbits, fpbits);
}

/*
 * pc_final()
 *   return: none
 */
void
pc_final (void)
{
}

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * lock_region() - lock/unlock region of a file
 *   return: 0 if success, -1 otherwise
 *   fd(in): file descriptor
 *   cmd(in): locking command to perform
 *   offset(in): start offset
 *   size(in): number of bytes
 */
int
lock_region (int fd, int cmd, long offset, long size)
{
  if (lseek (fd, offset, SEEK_SET) != offset)
    {
      return -1;
    }
  return lockf (fd, cmd, size);
}
#endif /* ENABLE_UNUSED_FUNCTION */

#if !defined(CAS_BROKER) && !defined(CAS_CCI)
/* free_space -
 *   return:
 *   path(in):
 *
 * Note:
 *   This function is designed to be compatible with both wide character
 *   and single byte character strings.  Hence, the use of tchar.h.
 *   The definition of 'UNICODE' during compilation determines that TCHAR
 *   becomes 'wchar_t' and not 'char'.  If so, we assume that 'path' is
 *   already a wide character type.
 */
int
free_space (const char *path)
{
  ULARGE_INTEGER freebytes_user, total_bytes, freebytes_system;
  TCHAR disk[PATH_MAX];
  TCHAR *temp = NULL;

  /* If there is a : then change c:\foo\bar to c:\ */
  _tcsncpy (disk, (TCHAR *) path, PATH_MAX);

  temp = _tcschr (disk, __TEXT (':'));
  if (temp)
    {
      ++temp;			/* move past the colon */
      if (*temp == __TEXT ('\\') || *temp == __TEXT ('/'))
	{
	  ++temp;
	}
      *temp = __TEXT ('\0');	/* terminate the string */
    }

  /* if there's no colon use the root of local dir by passing a NULL */
  if (!GetDiskFreeSpaceEx ((temp) ? disk : NULL,
			   &freebytes_user, &total_bytes, &freebytes_system))
    {
      return (-1);
    }
  else
    {
      return ((int) (freebytes_user.QuadPart / IO_PAGESIZE));
    }
}
#endif

#endif /* WINDOWS */

#if !defined(HAVE_STRDUP)
/*
 * strdup() - duplicate a string
 *   return: returns a pointer to the duplicated string
 *   str(in): string
 */
char *
strdup (const char *str)
{
  char *sdup;

  assert (str != NULL);

  size_t len = strlen (str) + 1;
  sdup = (char *) malloc (len);
  if (sdup != NULL)
    {
      memcpy (sdup, str, len);
    }

  return sdup;
}
#endif /* !HAVE_STRDUP */

#if !defined(HAVE_VASPRINTF)
#if defined(WINDOWS)
int
vasprintf (char **ptr, const char *format, va_list ap)
{
  int len;

  len = _vscprintf_p (format, ap) + 1;
  *ptr = (char *) malloc (len * sizeof (char));
  if (!*ptr)
    {
      return -1;
    }

  return _vsprintf_p (*ptr, len, format, ap);
}
#else
int
vasprintf (char **ptr, const char *format, va_list ap)
{
  va_list ap_copy;
  char *buffer = NULL;
  int count;

  va_copy (ap_copy, ap);
  count = vsnprintf (NULL, 0, format, ap);
  if (count >= 0)
    {
      buffer = (char *) malloc (count + 1);
      if (buffer != NULL)
	{
	  count = vsnprintf (buffer, count + 1, format, ap_copy);
	  if (count < 0)
	    {
	      free (buffer);
	    }
	  else
	    {
	      *ptr = buffer;
	    }
	}
      else
	{
	  count = -1;
	}
    }
  va_end (ap_copy);

  return count;
}
#endif
#endif /* !HAVE_VASPRINTF */

#if !defined(HAVE_ASPRINTF)
int
asprintf (char **ptr, const char *format, ...)
{
  va_list ap;
  int ret;

  *ptr = NULL;

  va_start (ap, format);
  ret = vasprintf (ptr, format, ap);
  va_end (ap);

  return ret;
}
#endif /* !HAVE_ASPRINTF */

int
cub_dirname_r (const char *path, char *pathbuf, size_t buflen)
{
  const char *endp;
  ptrdiff_t len;

  if (buflen < 2)
    return (errno = ERANGE);

  /* Empty or NULL string gets treated as "." */
  if (path == NULL || *path == '\0')
    {
      pathbuf[0] = PATH_CURRENT;
      pathbuf[1] = '\0';
      return 1;
    }

  /* Strip trailing slashes */
  endp = path + strlen (path) - 1;
  while (endp > path && *endp == PATH_SEPARATOR)
    endp--;

  /* Find the start of the dir */
  while (endp > path && *endp != PATH_SEPARATOR)
    endp--;

  /* Either the dir is "/" or there are no slashes */
  if (endp == path)
    {
      if (*endp == PATH_SEPARATOR)
	pathbuf[0] = PATH_SEPARATOR;
      else
	pathbuf[0] = PATH_CURRENT;
      pathbuf[1] = '\0';
      return 1;
    }
  else
    {
      do
	{
	  endp--;
	}
      while (endp > path && *endp == PATH_SEPARATOR);
    }

  len = (ptrdiff_t) (endp - path) + 1;
  if (len + 1 > PATH_MAX)
    {
      return (errno = ENAMETOOLONG);
    }
  if (len + 1 > (int) buflen)
    {
      return (errno = ERANGE);
    }
  (void) strncpy (pathbuf, path, len);
  pathbuf[len] = '\0';
  return (int) len;
}

#if !defined(HAVE_DIRNAME)
char *
dirname (const char *path)
{
  static char *bname = NULL;

  if (bname == NULL)
    {
      bname = (char *) malloc (PATH_MAX);
      if (bname == NULL)
	return (NULL);
    }

  return (cub_dirname_r (path, bname, PATH_MAX) < 0) ? NULL : bname;
}
#endif /* !HAVE_DIRNAME */

int
basename_r (const char *path, char *pathbuf, size_t buflen)
{
  const char *endp, *startp;
  ptrdiff_t len;

  if (buflen < 2)
    return (errno = ERANGE);

  /* Empty or NULL string gets treated as "." */
  if (path == NULL || *path == '\0')
    {
      pathbuf[0] = PATH_CURRENT;
      pathbuf[1] = '\0';
      return 1;
    }

  /* Strip trailing slashes */
  endp = path + strlen (path) - 1;
  while (endp > path && *endp == PATH_SEPARATOR)
    endp--;

  /* All slashes becomes "/" */
  if (endp == path && *endp == PATH_SEPARATOR)
    {
      pathbuf[0] = PATH_SEPARATOR;
      pathbuf[1] = '\0';
      return 1;
    }

  /* Find the start of the base */
  startp = endp;
  while (startp > path && *(startp - 1) != PATH_SEPARATOR)
    startp--;

  len = (ptrdiff_t) (endp - startp) + 1;
  if (len + 1 > PATH_MAX)
    {
      return (errno = ENAMETOOLONG);
    }
  if (len + 1 > (int) buflen)
    {
      return (errno = ERANGE);
    }
  (void) strncpy (pathbuf, startp, len);
  pathbuf[len] = '\0';
  return (int) len;
}

#if !defined(HAVE_BASENAME)
char *
basename (const char *path)
{
  static char *bname = NULL;

  if (bname == NULL)
    {
      bname = (char *) malloc (PATH_MAX);
      if (bname == NULL)
	return (NULL);
    }

  return (basename_r (path, bname, PATH_MAX) < 0) ? NULL : bname;
}
#endif /* !HAVE_BASENAME */

#if defined (ENABLE_UNUSED_FUNCTION)
int
utona (unsigned int u, char *s, size_t n)
{
  char nbuf[10], *p, *t;

  if (s == NULL || n == 0)
    {
      return 0;
    }
  if (n == 1)
    {
      *s = '\0';
      return 1;
    }

  p = nbuf;
  do
    {
      *p++ = u % 10 + '0';
    }
  while ((u /= 10) > 0);
  p--;

  t = s;
  do
    {
      *t++ = *p--;
    }
  while (p >= nbuf && --n > 1);
  *t++ = '\0';

  return (t - s);
}

int
itona (int i, char *s, size_t n)
{
  if (s == NULL || n == 0)
    {
      return 0;
    }
  if (n == 1)
    {
      *s = '\0';
      return 1;
    }

  if (i < 0)
    {
      *s++ = '-';
      n--;
      return utona (-i, s, n) + 1;
    }
  else
    {
      return utona (i, s, n);
    }
}
#endif /* ENABLE_UNUSED_FUNCTION */

char *
stristr (const char *s, const char *find)
{
  char c, sc;
  size_t len;

  if ((c = *find++) != '0')
    {
      len = strlen (find);
      do
	{
	  do
	    {
	      if ((sc = *s++) == '\0')
		{
		  return NULL;
		}
	    }
	  while (toupper (sc) != toupper (c));
	}
      while (strncasecmp (s, find, len) != 0);
      s--;
    }
  return (char *) s;
}

#if !defined(CAS_BROKER) && !defined(CAS_CCI)
/*
 * wrapper for cuserid() function
 */
char *
getuserid (char *string, int size)
{
  if (cuserid (string) == NULL)
    {
      return NULL;
    }
  else
    {
      string[size - 1] = '\0';
      return string;
    }
}
#endif

/*
 * wrapper for OS dependent operations
 */
/*
 * os_rename_file() - rename a file
 *   return: 0 on success, otherwise -1
 *   src_path(in): source path
 *   dest_path(in): destination path
 */
int
os_rename_file (const char *src_path, const char *dest_path)
{
#if defined(WINDOWS)
  /* NOTE: Windows 95 and 98 do not support MoveFileEx */
  if (MoveFileEx (src_path, dest_path,
		  MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
    {
      return 0;
    }
  else
    {
      return -1;
    }
  /* TODO:
   *   Windows 95/98 does not replace the file if it already exists.
   *   (void) _unlink (dest_path);
   *   return rename (src_path, dest_path);
   */
#else
  return rename (src_path, dest_path);
#endif /* WINDOWS */
}

#include <signal.h>
/*
 * os_set_signal_handler() - sets the signal handler
 *   return: Old signal handler which can be used to restore
 *           If it fails, it returns SIG_ERR
 *   signo(in): specifies the signal except SIGKILL and/or SIGSTOP
 *   sig_handler(in): Function to handle the above signal or SIG_DFL, SIG_IGN
 *
 * Note: We would like the signals to work as follow:
 *   - Multiple signals should not get lost; the system should queue them
 *   - Signals must be reliable. The signal handler should not need to
 *     reestablish itself like in the old days of Unix
 *   - The signal hander remains installed after a signal has been delivered
 *   - If a caught signal occurs during certain system calls terminating
 *     the call prematurely, the call is automatically restarted
 *   - If SIG_DFL is given, the default action is reinstaled
 *   - If SIG_IGN is given as sig_handler, the signal is subsequently ignored
 *     and pending instances of the signal are discarded
 */
SIGNAL_HANDLER_FUNCTION
os_set_signal_handler (const int sig_no, SIGNAL_HANDLER_FUNCTION sig_handler)
{
#if defined(WINDOWS)
  return signal (sig_no, sig_handler);
#else /* WINDOWS */
  struct sigaction act;
  struct sigaction oact;

  act.sa_handler = sig_handler;
  act.sa_flags = 0;

  if (sigemptyset (&act.sa_mask) < 0)
    {
      return (SIG_ERR);
    }

  switch (sig_no)
    {
    case SIGALRM:
#if defined(SA_INTERRUPT)
      act.sa_flags |= SA_INTERRUPT;	/* disable other interrupts */
#endif /* SA_INTERRUPT */
      break;
    default:
#if defined(SA_RESTART)
      act.sa_flags |= SA_RESTART;	/* making certain system calls
					   restartable across signals */
#endif /* SA_RESTART */
      break;
    }

  if (sigaction (sig_no, &act, &oact) < 0)
    {
      return (SIG_ERR);
    }

  return (oact.sa_handler);
#endif /* WINDOWS */
}

/*
 * os_send_signal() - send the signal to ourselves
 *   return: none
 *   signo(in): signal number to send
 */
void
os_send_signal (const int sig_no)
{
#if defined(WINDOWS)
  raise (sig_no);
#else /* WINDOWS */
  kill (getpid (), sig_no);
#endif /* WINDOWS */
}

#if defined(WINDOWS)
#if !defined(HAVE_STRSEP)
char *
strsep (char **stringp, const char *delim)
{
  char *p, *token;

  if (*stringp == NULL)
    return NULL;

  token = *stringp;

  p = strstr (*stringp, delim);
  if (p == NULL)
    {
      *stringp = NULL;
    }
  else
    {
      *p = '\0';
      *stringp = p + strlen (delim);
    }

  return token;
}
#endif

/*
 * getpass() - get a password
 *   return: password string
 *   prompt(in): prompt message string
 */
char *
getpass (const char *prompt)
{
  size_t pwlen = 0;
  int c;
  static char password_buffer[80];

  fprintf (stdout, prompt);

  while (1)
    {
      c = getch ();
      if (c == '\r' || c == '\n')
	break;
      if (c == '\b')
	{			/* backspace */
	  if (pwlen > 0)
	    pwlen--;
	  continue;
	}
      if (pwlen < sizeof (password_buffer) - 1)
	password_buffer[pwlen++] = c;
    }
  password_buffer[pwlen] = '\0';
  return password_buffer;
}
#endif /* WINDOWS */


#if defined(WINDOWS)

int
setenv (const char *name, const char *val, int overwrite)
{
  errno_t ret;

  if (!overwrite)
    {
      char *ptr = getenv (name);
      if (ptr != NULL)
	{
	  return -1;
	}
    }

  ret = _putenv_s (name, val);
  if (ret == EINVAL)
    {
      return -1;
    }

  return 0;
}

int
cub_vsnprintf (char *buffer, size_t count, const char *format, va_list argptr)
{
  int len = _vscprintf_p (format, argptr) + 1;

  if (len > (int) count)
    {
      char *cp = malloc (len);
      if (cp == NULL)
	{
	  return -1;
	}

      len = _vsprintf_p (cp, len, format, argptr);
      if (len < 0)
	{
	  free (cp);
	  return len;
	}

      memcpy (buffer, cp, count - 1);
      buffer[count - 1] = 0;

      free (cp);
      return (int) count;
    }

  return _vsprintf_p (buffer, count, format, argptr);
}

double
round (double d)
{
  return d >= 0 ? floor (d + 0.5) : ceil (d - 0.5);
}

int
pthread_mutex_init (pthread_mutex_t * mutex, pthread_mutexattr_t * attr)
{
  if (mutex->csp == &mutex->cs)
    {
      /* already inited */
      assert (0);
      return 0;
    }

  mutex->csp = &mutex->cs;
  InitializeCriticalSection (mutex->csp);

  return 0;
}

int
pthread_mutex_destroy (pthread_mutex_t * mutex)
{
  if (mutex->csp != &mutex->cs)
    {
      if (mutex->csp == NULL)	/* inited by PTHREAD_MUTEX_INITIALIZER */
	{
	  return 0;
	}

      /* invalid destroy */
      assert (0);
      mutex->csp = NULL;
      return 0;
    }

  DeleteCriticalSection (mutex->csp);
  mutex->csp = NULL;
  return 0;
}

int
pthread_mutexattr_init (pthread_mutexattr_t * attr)
{
  return 0;
}

int
pthread_mutexattr_settype (pthread_mutexattr_t * attr, int type)
{
  return 0;
}

int
pthread_mutexattr_destroy (pthread_mutexattr_t * attr)
{
  return 0;
}


pthread_mutex_t css_Internal_mutex_for_mutex_initialize =
  PTHREAD_MUTEX_INITIALIZER;

void
port_win_mutex_init_and_lock (pthread_mutex_t * mutex)
{
  if (css_Internal_mutex_for_mutex_initialize.csp !=
      &css_Internal_mutex_for_mutex_initialize.cs)
    {
      pthread_mutex_init (&css_Internal_mutex_for_mutex_initialize, NULL);
    }

  EnterCriticalSection (css_Internal_mutex_for_mutex_initialize.csp);
  if (mutex->csp != &mutex->cs)
    {
      /*
       * below assert means that lock without pthread_mutex_init
       * or PTHREAD_MUTEX_INITIALIZER
       */
      assert (mutex->csp == NULL);
      pthread_mutex_init (mutex, NULL);
    }
  LeaveCriticalSection (css_Internal_mutex_for_mutex_initialize.csp);

  EnterCriticalSection (mutex->csp);
}

int
port_win_mutex_init_and_trylock (pthread_mutex_t * mutex)
{
  bool r;

  if (css_Internal_mutex_for_mutex_initialize.csp !=
      &css_Internal_mutex_for_mutex_initialize.cs)
    {
      pthread_mutex_init (&css_Internal_mutex_for_mutex_initialize, NULL);
    }

  EnterCriticalSection (css_Internal_mutex_for_mutex_initialize.csp);
  if (mutex->csp != &mutex->cs)
    {
      /*
       * below assert means that trylock without pthread_mutex_init
       * or PTHREAD_MUTEX_INITIALIZER
       */
      assert (mutex->csp == NULL);
      pthread_mutex_init (mutex, NULL);
    }
  LeaveCriticalSection (css_Internal_mutex_for_mutex_initialize.csp);

  r = TryEnterCriticalSection (mutex->csp);
  if (mutex->csp->RecursionCount > 1)
    {
      LeaveCriticalSection (mutex->csp);
      return EBUSY;
    }

  return r ? 0 : EBUSY;
}


typedef void (WINAPI * InitializeConditionVariable_t) (CONDITION_VARIABLE *);
typedef bool (WINAPI * SleepConditionVariableCS_t) (CONDITION_VARIABLE *,
						    CRITICAL_SECTION *,
						    DWORD dwMilliseconds);

typedef void (WINAPI * WakeAllConditionVariable_t) (CONDITION_VARIABLE *);
typedef void (WINAPI * WakeConditionVariable_t) (CONDITION_VARIABLE *);

InitializeConditionVariable_t fp_InitializeConditionVariable;
SleepConditionVariableCS_t fp_SleepConditionVariableCS;
WakeAllConditionVariable_t fp_WakeAllConditionVariable;
WakeConditionVariable_t fp_WakeConditionVariable;

static bool have_CONDITION_VARIABLE = false;


static void
check_CONDITION_VARIABLE (void)
{
  HMODULE kernel32 = GetModuleHandle ("kernel32");

  have_CONDITION_VARIABLE = true;
  fp_InitializeConditionVariable = (InitializeConditionVariable_t)
    GetProcAddress (kernel32, "InitializeConditionVariable");
  if (fp_InitializeConditionVariable == NULL)
    {
      have_CONDITION_VARIABLE = false;
      return;
    }

  fp_SleepConditionVariableCS = (SleepConditionVariableCS_t)
    GetProcAddress (kernel32, "SleepConditionVariableCS");
  fp_WakeAllConditionVariable = (WakeAllConditionVariable_t)
    GetProcAddress (kernel32, "WakeAllConditionVariable");
  fp_WakeConditionVariable = (WakeConditionVariable_t)
    GetProcAddress (kernel32, "WakeConditionVariable");
}

static int
timespec_to_msec (const struct timespec *abstime)
{
  int msec = 0;
  struct timeval tv;

  if (abstime == NULL)
    {
      return INFINITE;
    }

  gettimeofday (&tv, NULL);
  msec = (abstime->tv_sec - tv.tv_sec) * 1000;
  msec += (abstime->tv_nsec / 1000 - tv.tv_usec) / 1000;

  if (msec < 0)
    {
      msec = 0;
    }

  return msec;
}


/*
 * old (pre-vista) windows does not support CONDITION_VARIABLES
 * so, we need below custom pthread_cond modules for them
 */
static int
win_custom_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
{
  cond->initialized = true;
  cond->waiting = 0;
  InitializeCriticalSection (&cond->lock_waiting);

  cond->events[COND_SIGNAL] = CreateEvent (NULL, FALSE, FALSE, NULL);
  cond->events[COND_BROADCAST] = CreateEvent (NULL, TRUE, FALSE, NULL);
  cond->broadcast_block_event = CreateEvent (NULL, TRUE, TRUE, NULL);

  if (cond->events[COND_SIGNAL] == NULL ||
      cond->events[COND_BROADCAST] == NULL ||
      cond->broadcast_block_event == NULL)
    {
      return ENOMEM;
    }

  return 0;
}

static int
win_custom_cond_destroy (pthread_cond_t * cond)
{
  if (!cond->initialized)
    {
      return 0;
    }

  DeleteCriticalSection (&cond->lock_waiting);

  if (CloseHandle (cond->events[COND_SIGNAL]) == 0 ||
      CloseHandle (cond->events[COND_BROADCAST]) == 0 ||
      CloseHandle (cond->broadcast_block_event) == 0)
    {
      return EINVAL;
    }

  cond->initialized = false;
  return 0;
}

static int
win_custom_cond_timedwait (pthread_cond_t * cond, pthread_mutex_t * mutex,
			   struct timespec *abstime)
{
  int result;
  int msec;

  assert (cond->initialized == true);

  msec = timespec_to_msec (abstime);
  WaitForSingleObject (cond->broadcast_block_event, INFINITE);

  EnterCriticalSection (&cond->lock_waiting);
  cond->waiting++;
  LeaveCriticalSection (&cond->lock_waiting);

  LeaveCriticalSection (mutex->csp);
  result = WaitForMultipleObjects (2, cond->events, FALSE, msec);
  assert (result == WAIT_TIMEOUT || result <= 2);

  /*** THREAD UNSAFE AREA ***/

  EnterCriticalSection (&cond->lock_waiting);
  cond->waiting--;

  if (cond->waiting == 0)
    {
      ResetEvent (cond->events[COND_BROADCAST]);
      SetEvent (cond->broadcast_block_event);

      /* 
       * Remove additional signal if exists
       * (That's received in above THREAD UNSAFE AREA)
       */
      WaitForSingleObject (cond->events[COND_SIGNAL], 0);
    }

  LeaveCriticalSection (&cond->lock_waiting);
  EnterCriticalSection (mutex->csp);

  return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;
}

static int
win_custom_cond_signal (pthread_cond_t * cond)
{
  assert (cond->initialized == true);

  EnterCriticalSection (&cond->lock_waiting);

  if (cond->waiting > 0)
    {
      SetEvent (cond->events[COND_SIGNAL]);
    }

  LeaveCriticalSection (&cond->lock_waiting);

  return 0;
}

static int
win_custom_cond_broadcast (pthread_cond_t * cond)
{
  assert (cond->initialized == true);

  EnterCriticalSection (&cond->lock_waiting);

  if (cond->waiting > 0)
    {
      ResetEvent (cond->broadcast_block_event);
      SetEvent (cond->events[COND_BROADCAST]);
    }

  LeaveCriticalSection (&cond->lock_waiting);

  return 0;
}

int
pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
{
  static bool checked = false;
  if (checked == false)
    {
      check_CONDITION_VARIABLE ();
      checked = true;
    }

  if (have_CONDITION_VARIABLE)
    {
      fp_InitializeConditionVariable (&cond->native_cond);
      return 0;
    }

  return win_custom_cond_init (cond, attr);
}

int
pthread_cond_destroy (pthread_cond_t * cond)
{
  if (have_CONDITION_VARIABLE)
    {
      return 0;
    }

  return win_custom_cond_destroy (cond);
}

int
pthread_cond_broadcast (pthread_cond_t * cond)
{
  if (have_CONDITION_VARIABLE)
    {
      fp_WakeAllConditionVariable (&cond->native_cond);
      return 0;
    }

  return win_custom_cond_broadcast (cond);
}

int
pthread_cond_signal (pthread_cond_t * cond)
{
  if (have_CONDITION_VARIABLE)
    {
      fp_WakeConditionVariable (&cond->native_cond);
      return 0;
    }

  return win_custom_cond_signal (cond);
}

int
pthread_cond_timedwait (pthread_cond_t * cond, pthread_mutex_t * mutex,
			struct timespec *abstime)
{
  if (have_CONDITION_VARIABLE)
    {
      int msec = timespec_to_msec (abstime);
      if (fp_SleepConditionVariableCS (&cond->native_cond, mutex->csp, msec)
	  == false)
	{
	  return ETIMEDOUT;
	}

      return 0;
    }

  return win_custom_cond_timedwait (cond, mutex, abstime);
}

int
pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)
{
  return pthread_cond_timedwait (cond, mutex, NULL);
}


int
pthread_create (pthread_t * thread, const pthread_attr_t * attr,
		THREAD_RET_T (THREAD_CALLING_CONVENTION *
			      start_routine) (void *), void *arg)
{
  unsigned int tid;
  *thread = (pthread_t) _beginthreadex (NULL, 0, start_routine, arg, 0, &tid);
  return (*thread <= 0) ? -1 : 0;
}

void
pthread_exit (void *ptr)
{
  _endthreadex ((unsigned int) ptr);
}

pthread_t
pthread_self ()
{
  return GetCurrentThread ();
}

int
pthread_join (pthread_t thread, void **value_ptr)
{
  return WaitForSingleObject (thread, INFINITE);
}

int
pthread_key_create (pthread_key_t * key, void (*destructor) (void *))
{
  return (*key = TlsAlloc ()) != 0xFFFFFFFF ? 0 : -1;
}

int
pthread_key_delete (pthread_key_t key)
{
  return TlsFree (key) != 0 ? 0 : -1;
}

int
pthread_setspecific (pthread_key_t key, const void *value)
{
  return TlsSetValue (key, (LPVOID) value) != 0 ? 0 : -1;
}

void *
pthread_getspecific (pthread_key_t key)
{
  return TlsGetValue (key);
}

#if !defined(_WIN64)
/*
 * The following functions are used to provide atomic operations on
 * Windows 32bit OS. See the comment in porting.h for more information.
 */
UINT64
win32_compare_exchange64 (UINT64 volatile *val_ptr,
			  UINT64 swap_val, UINT64 cmp_val)
{
  /* *INDENT-OFF* */
  __asm
  {
      mov esi,[val_ptr]
      mov ebx, dword ptr[swap_val]
      mov ecx, dword ptr[swap_val + 4]
      mov eax, dword ptr[cmp_val]
      mov edx, dword ptr[cmp_val + 4]
      lock cmpxchg8b[esi]
  }
  /* *INDENT-ON* */
}

UINT64
win32_exchange_add64 (UINT64 volatile *ptr, UINT64 amount)
{
  UINT64 old;
  do
    {
      old = *ptr;
    }
  while (win32_compare_exchange64 (ptr, old + amount, old) != old);
  return old;
}

UINT64
win32_exchange64 (UINT64 volatile *ptr, UINT64 new_val)
{
  UINT64 old;
  do
    {
      old = *ptr;
    }
  while (win32_compare_exchange64 (ptr, new_val, old) != old);
  return old;
}
#endif /* _WIN64 */
#endif /* WINDOWS */


/*
 * port_open_memstream - make memory stream file handle if possible.
 *			 if not, make temporiry file handle.
 *   return: file handle
 *
 *   ptr (out): memory stream (or temp file name)
 *   sizeloc (out): stream size
 *
 *   NOTE: this function use memory allocation in it.
 *         so you should ensure that stream size is not too huge
 *         before you use this.
 */
FILE *
port_open_memstream (char **ptr, size_t * sizeloc)
{
#ifdef HAVE_OPEN_MEMSTREAM
  return open_memstream (ptr, sizeloc);
#else
  *ptr = tempnam (NULL, "cubrid_");
  return fopen (*ptr, "w+");
#endif
}


/*
 * port_close_memstream - flush file handle and close
 *
 *   fp (in): file handle to close
 *   ptr (in/out): memory stream (out) or temp file name (in)
 *   sizeloc (out): stream size
 *
 *   NOTE: you should call this function before refer ptr
 *         this function flush contents to ptr before close handle
 */
void
port_close_memstream (FILE * fp, char **ptr, size_t * sizeloc)
{
  char *buff = NULL;
  struct stat stat_buf;
  size_t n;

  fflush (fp);

  if (fp)
    {
#ifdef HAVE_OPEN_MEMSTREAM
      fclose (fp);
#else
      if (fstat (fileno (fp), &stat_buf) == 0)
	{
	  *sizeloc = stat_buf.st_size;

	  buff = malloc (*sizeloc + 1);
	  if (buff)
	    {
	      fseek (fp, 0, SEEK_SET);
	      n = fread (buff, 1, *sizeloc, fp);
	      buff[n] = '\0';
	      *sizeloc = n;
	    }
	}

      fclose (fp);
      /* tempname from port_open_memstream */
      unlink (*ptr);
      free (*ptr);

      /* set output */
      *ptr = buff;
#endif
    }
}

char *
trim (char *str)
{
  char *p;
  char *s;

  if (str == NULL)
    return (str);

  for (s = str;
       *s != '\0' && (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r');
       s++)
    ;
  if (*s == '\0')
    {
      *str = '\0';
      return (str);
    }

  /* *s must be a non-white char */
  for (p = s; *p != '\0'; p++)
    ;
  for (p--; *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'; p--)
    ;
  *++p = '\0';

  if (s != str)
    memmove (str, s, strlen (s) + 1);

  return (str);
}

int
port_str_to_int (int *ret_p, const char *str_p, int base)
{
  long int val;
  char *end_p;

  assert (ret_p != NULL);
  assert (str_p != NULL);

  *ret_p = 0;

  errno = 0;
  val = strtol (str_p, &end_p, base);

  if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
      || (errno != 0 && val == 0))
    {
      return -1;
    }

  if (end_p == str_p)
    {
      return -1;
    }

  if (*end_p != '\0')
    {
      return -1;
    }

  if (val < INT_MIN || val > INT_MAX)
    {
      return -1;
    }

  *ret_p = val;

  return 0;
}