/* ========================================================================== */
/*! \file
 * \brief Intended to implement missing or buggy POSIX functionality of OS
 *
 * Copyright (c) 2012-2016 by the developers. See the LICENSE file for details.
 */


/* ========================================================================== */
/* Include headers */

#include "posix.h"  /* Include this first because of feature test macros */

#include "main.h"


/* ========================================================================== */
/*! \addtogroup POSIX
 *
 * This subsystem should hold away operating system issues from the rest of the
 * program. If a C source file requires something that is not ISO C, it should
 * include "posix.h" as the first header file and this module should take care
 * to provide the required functionality.
 * This allows to write cleaner code (that normally would only be accepted by
 * much more recent operating systems) and nevertheless run it on old machines.
 *
 * This module is based on the POSIX.1-1990, POSIX.1b-1993, POSIX.1c-1996 and
 * BSD socket APIs, this means it expects that the operating system provides
 * these APIs.
 * Workarounds for operating system bugs should be implemented here.
 *
 * Version numbers for Single Unix Specification (X/Open XSI extension):
 * - SUS (Issue 4.2)
 * - SUSv2 (Issue 5)
 * - SUSv3 (Issue 6)
 * - SUSv4 (Issue 7)
 *
 * This module use the XSI extension for SUSv2 or SUSv3 respectively.
 *
 * \attention
 * The random number generation functions provided by this module may use weak
 * (or even trivial) algorithms and are not suited for cryptographic tasks!
 *
 * \attention
 * Note that POSIX.1-1990 has no C++ binding, therefore a C++ file is not
 * allowed to include "posix.h".
 *
 * The namespaces reserved for the XNS module of the OS are defined by SUSv2:
 * <br>
 * http://pubs.opengroup.org/onlinepubs/007908799/xns/namespace.html
 * <br>
 * For this module the "posix_" namespace is used. All elements provided by this
 * module have a "posix_" (or "POSIX_") prefix.
 *
 * Functions not defined in POSIX.1-1990, POSIX.1b-1993, POSIX.1c-1996 or SUSv2,
 * but defined in a later version should be implemented here in a POSIX
 * compliant way but with a "posix_" prefix.
 * For partial implementations the parts that are implemented should be POSIX
 * or SUS compliant respectively.
 *
 * Currently the following functions are completely implemented:
 * - \c posix_freeaddrinfo()
 * - \c posix_fsync()
 * - \c posix_getline()
 * - \c posix_poll()
 * - \c posix_random()
 * - \c posix_scandir()
 * - \c posix_srandom()
 * - \c posix_mkstemp()
 * - \c posix_gai_strerror()
 *
 * Currently the following functions are only partially implemented:
 * - \c posix_getaddrinfo()
 * - \c posix_snprintf()
 * - \c posix_newlocale()
 * - \c posix_freelocale()
 * - \c posix_strcasecmp_l()
 * - \c posix_strncasecmp_l()
 *
 * \attention
 * \c struct \c sockaddr_storage is broken on AIX if POSIX or SUS mode is used:
 * - The structure is not exposed at all if '_ALL_SOURCE' is not defined
 * - The member 'ss_family' required by SUSv2 and POSIX.1-2001 is missing
 * - At least on AIX 5.3 the structure is not large enough
 * Using this structure should be avoided. Using '_ALL_SOURCE' as workaround has
 * unwanted side effects and would solve only the first problem.
 */


/* ========================================================================== */
/* Memory managment debugging
 *
 * This is a simple wrapper for the default memory manager that can detect
 * invalid free() operations. It is always disabled by default.
 *
 * Only the parts of the program that use the memory manager API with "posix_"
 * prefix are checked.
 */

#if CFG_USE_MEMDEBUG
#  define MEMDEBUG_ARRAY_SIZE  (size_t) 100000
static posix_pthread_mutex_t  memdebug_mutex = POSIX_PTHREAD_MUTEX_INITIALIZER;
static void*  memdebug_array[MEMDEBUG_ARRAY_SIZE];
static size_t  memdebug_size[MEMDEBUG_ARRAY_SIZE];
static size_t  memdebug_num = 0;

/* Replacement for 'malloc()' */
void*  posix_malloc(size_t  len)
{
   void*  p = NULL;

   if(!posix_pthread_mutex_lock(&memdebug_mutex))
   {
      if(MEMDEBUG_ARRAY_SIZE <= memdebug_num + (size_t) 1)
      {
         print_error("POSIX: malloc(): Debug array overflow");
         return(NULL);
      }
      else
      {
         p = malloc(len);
         printf("%s: POSIX: MEMDBG: A:    %p\n", CFG_NAME, p);
         if(p)
         {
            memdebug_array[memdebug_num] = p;
            memdebug_size[memdebug_num] = len;
            ++memdebug_num;
         }
      }
      posix_pthread_mutex_unlock(&memdebug_mutex);
   }

   return(p);
}


/* Replacement for 'free()' */
void  posix_free(void*  p)
{
   size_t  i, ii;

   if(!posix_pthread_mutex_lock(&memdebug_mutex))
   {
      if(NULL != p)
      {
         printf("%s: POSIX: MEMDBG: F: %p\n", CFG_NAME, p);
         for(i = 0; i < memdebug_num; ++i)
         {
            if(memdebug_array[i] == p)  { break; }
         }
         if(i >= memdebug_num)
         {
            print_error("POSIX: free(): Invalid address (thread frozen)");
            while(1);
         }
         else
         {
            for(ii = i; ii < memdebug_num; ++ii)
            {
               memdebug_array[ii] = memdebug_array[ii + 1];
               memdebug_size[ii] = memdebug_size[ii + 1];
            }
            --memdebug_num;
         }
      }
      posix_pthread_mutex_unlock(&memdebug_mutex);
   }

   return(free(p));
}


/* Replacement for 'realloc()' */
void*  posix_realloc(void*  p, size_t  len)
{
   void*  q = posix_malloc(len);
   size_t  i;
   size_t  oldlen;

   if(!posix_pthread_mutex_lock(&memdebug_mutex))
   {
      if(NULL != p)
      {
         printf("%s: POSIX: MEMDBG: R: %p => %p\n", CFG_NAME, p, q);
         for(i = 0; i < memdebug_num; ++i)
         {
            if(memdebug_array[i] == p)  { break; }
         }
         if(i >= memdebug_num)
         {
            print_error("POSIX: realloc(): Invalid address (thread frozen)");
            while(1);
         }
         else if(NULL != q)
         {
            /* Never copy more data than contained in the old memory block */
            oldlen = memdebug_size[i];
            if(len > oldlen)  { len = oldlen; }
            memcpy(q, p, len);
         }
      }
      posix_pthread_mutex_unlock(&memdebug_mutex);
   }

   /* Check for error */
   if(NULL == q)  { print_error("POSIX: realloc(): Failed"); }
   else  { posix_free(p); }

   return(q);
}
#endif  /* CFG_USE_MEMDEBUG */


/* ========================================================================== */
/* Random numbers
 *
 * \attention
 * The POSIX RNG is not suitable for cryptographic purposes!
 */

#if !CFG_USE_XSI
static posix_pthread_mutex_t  random_mutex = POSIX_PTHREAD_MUTEX_INITIALIZER;
static long int  random_state = 1;

void  posix_srandom(unsigned int  seed)
{
   if(!posix_pthread_mutex_lock(&random_mutex))
   {
      random_state = (long int) (seed * 2U);
      posix_pthread_mutex_unlock(&random_mutex);
   }
}


long int  posix_random(void)
{
   long int  res = 0;

   if(!posix_pthread_mutex_lock(&random_mutex))
   {
      res = random_state;

      /*
       * Trivial increment implementation so that two sequential numbers can
       * never have the same value.
       */
      if(POSIX_LONG_MAX > random_state)  { ++random_state; }
      else  { random_state = 0; }
      posix_pthread_mutex_unlock(&random_mutex);
   }

   return(res);
}
#endif  /* !CFG_USE_XSI */


/* ========================================================================== */
/* File & directory handling */

#if !CFG_USE_FSC
/*
 * 'fsync()' is part of the POSIX.1-2001 FSC option and X/Open XSI extension.
 */
int  posix_fsync(int  fd)
{
   /* NOP implementation is allowed */
   return(0);
}
#endif  /* !CFG_USE_FSC */


/*
 * This implementation of 'scandir()' should be POSIX.1-2008 compliant.
 *
 * It is required that 'SSIZE_MAX' is at least 'INT_MAX' (must be checked by
 * build system).
 *
 * Note:
 * In theory, a system that provide threads should also provide 'readdir_r()',
 * a thread safe version of 'readdir()'. In reality this is not always the case.
 * In addition there may be a race condition that can lead to a buffer overflow:
 * http://womble.decadent.org.uk/readdir_r-advisory.html
 * Therefore we use our own function 'scandir_readentry()' to get the directory
 * entries.
 */
static posix_pthread_mutex_t  scandir_mutex = POSIX_PTHREAD_MUTEX_INITIALIZER;
static int  scandir_readentry(POSIX_DIR*  dirp, struct_posix_dirent**  entryp,
                              size_t*  len)
{
   int  result = -1;
   struct_posix_dirent*  e;

   if(!posix_pthread_mutex_lock(&scandir_mutex))
   {
      /* Lock UI mutex */
      if(ts_lock_ui())  { print_error("POSIX: Locking UI mutex failed"); }
      else
      {
         errno = 0;
         e = posix_readdir(dirp);
         if(NULL == e)
         {
            if(!errno)
            {
               /* No more entries in directory */
               *entryp = NULL;
               *len = 0;
               result = 0;
            }
         }
         else
         {
            /* Entry found, allocate local buffer */
            *len = offsetof(struct_posix_dirent, d_name) + strlen(e->d_name)
                            + (size_t) 1;
            *entryp = (struct_posix_dirent *) posix_malloc(*len);
            if(NULL != *entryp)
            {
               memcpy((void*) *entryp, (void*) e, *len);
               /* Force NUL termination at end of buffer */
               ((char *) *entryp)[*len - (size_t) 1] = 0;
               result = 0;
            }
         }
         /* Unlock UI mutex */
         if(ts_unlock_ui())  { print_error("POSIX: Unlocking UI mutex failed"); }
      }
      /*
       * In a multithreading environment the systems dirent buffer may be shared
       * between all threads. Therefore the mutex must stay locked until we have
       * copied the data to our thread local buffer.
       */
      posix_pthread_mutex_unlock(&scandir_mutex);
   }

   return(result);
}
#define SCANDIR_ENTRIES_MIN  (size_t) 32
int  posix_scandir(const char*  dir, struct_posix_dirent***  namelist,
                   int (*sel)(struct_posix_dirent*),
                   int (*compar)(const struct_posix_dirent**,
                                 const struct_posix_dirent**))
{
   int  result = -1;
   POSIX_DIR*  dirp;
   size_t  len, num = 0;
   size_t  max = SCANDIR_ENTRIES_MIN;
   struct_posix_dirent*  entryp = NULL;
   struct_posix_dirent**  entries;
   struct_posix_dirent**  p;

   entries = (struct_posix_dirent**) posix_malloc(sizeof(*entries) * max);
   if(NULL != entries)
   {
      /* Open directory 'dir' (and verify that it really is a directory) */
      dirp = posix_opendir(dir);
      if(NULL != dirp)
      {
         /* Read next directory entry */
         while (!scandir_readentry(dirp, &entryp, &len))
         {
            if(NULL == entryp)
            {
               /* EOD => Return number of directory entries */
               result = (int) num;
               break;
            }
            /* Apply select function if there is one provided */
            if(NULL != sel)  { if(!sel(entryp))  continue; }
            entries[num++] = entryp;
            if(num >= max)
            {
               /* Allocate exponentially increasing sized memory chunks */
               if(POSIX_INT_MAX / 2 >= (int) max)  { max *= (size_t) 2; }
               else
               {
                  errno = ENOMEM;
                  break;
               }
               p = (struct_posix_dirent**) posix_realloc((void*) entries,
                                                        sizeof(*entries) * max);
               if(NULL != p)  { entries = p; }
               else  break;
            }
         }
         posix_closedir(dirp);
         /*
          * A standard compliant 'closedir()' is allowed to fail with 'EINTR',
          * but the state of the directory structure is undefined in this case.
          * Therefore we ignore the return value because we can't call
          * 'closedir()' again and must hope that the system has released all
          * ressources.
          */
      }
      /* Sort entries in array if there is a compare function provided */
      if(NULL != compar)
      {
         qsort((void*) entries, num, sizeof(*entries),
               (int (*)(const void*, const void*)) compar);
      }
      *namelist = entries;
   }

   /* Check for error */
   if(-1 == result)
   {
      /* Free all memory we have allocated */
      while(num--)  { posix_free((void*) entries[num]); }
      posix_free((void*) entries);
   }

   return(result);
}


#if !CFG_USE_XSI
static unsigned long int  mkstemp_state = 0;
static posix_pthread_mutex_t  mkstemp_mutex = POSIX_PTHREAD_MUTEX_INITIALIZER;
int  posix_mkstemp(char*  template)
{
   const int  oflag = POSIX_O_RDWR | POSIX_O_CREAT | POSIX_O_EXCL;
   const posix_mode_t  mode = POSIX_S_IRUSR | POSIX_S_IWUSR;
   int  res = -1;
   unsigned long int  unique;
   int  error = 0;
   size_t  pos;
   char  uni[7];
   int  rv;

   if(!posix_pthread_mutex_lock(&mkstemp_mutex))
   {
      if(999999UL > mkstemp_state)  { ++mkstemp_state; }
      else  { mkstemp_state = 0; }
      unique = mkstemp_state;
      posix_pthread_mutex_unlock(&mkstemp_mutex);

      /* Check template */
      pos = strlen(template);
      if((size_t) 6 > pos)  { error = 1; }
      else
      {
         pos -= (size_t) 6;
         if(strcmp("XXXXXX", &template[pos]))  { error = 1; }
      }
      if(error)  { posix_errno = POSIX_EINVAL; }
      else
      {
         rv = posix_snprintf(uni, (size_t) 7, "%06lu", unique);
         if(6 == rv)
         {
            strncpy(&template[pos], uni, (size_t) 7);
            res = posix_open(template, oflag, mode);
         }
      }
   }

   return(res);
}
#endif  /* CFG_USE_XSI */


/* ========================================================================== */
/* NLS */

/*
 * Only the following parameter set is supported:
 *    category_mask  POSIX_LC_CTYPE_MASK
 *    locale         "POSIX"
 */
posix_locale_t  posix_newlocale(int  category_mask, const char*  locale,
                                posix_locale_t  base)
{
   posix_locale_t  res = 0;

   if(POSIX_LC_CTYPE_MASK != category_mask)   { posix_errno = POSIX_EINVAL; }
   else
   {
      if(strcmp(locale, "POSIX"))  { posix_errno = POSIX_ENOENT; }
      else  { res = 1; }
   }

   return(res);
}


void  posix_freelocale(posix_locale_t  locobj)
{
   /* NOP (this implementation doesn't allocate memory for locale objects) */

   return;
}


/* 'locale' must always have its 'LC_CTYPE' element set to "POSIX" */
int  posix_strncasecmp_l(const char*  s1, const char*  s2, size_t  n,
                         posix_locale_t  locale)
{
   int  res = 0;
   size_t  i;
   int  c1;
   int  c2;

   /* Compare in lower case */
   for(i = 0; i < n; ++i)
   {
      c1 = (int) (unsigned char) s1[i];
      c2 = (int) (unsigned char) s2[i];
      /* Convert capital letters to lower case */
      if(65 <= c1 && 90 >= c1)  { c1 = (c1 - 65) + 97; }
      if(65 <= c2 && 90 >= c2)  { c2 = (c2 - 65) + 97; }
      /* Check for end of strings and difference */
      res = c1 - c2;
      if(!c1 || !c2 || res)  { break; }
   }

   return(res);
}


int  posix_strcasecmp_l(const char*  s1, const char*  s2,
                        posix_locale_t  locale)
{
   return(posix_strncasecmp_l(s1, s2, POSIX_SIZE_MAX, locale));
}


/* ========================================================================== */
/* Standard I/O */

#if !CFG_USE_POSIX_API_200112 && !CFG_USE_XSI
/*
 * To avoid additional bloat, this implementation of 'snprintf()' contains only
 * the subset of features that are required. What is present should be SUSv2
 * compliant.
 *
 * This function is thread safe.
 *
 * Supported conversions in the format string \e f :
 *    %%   Literal percent character
 *    %s   char* (string with NUL termination)
 *    %d   signed int
 *    %u   unsigned int
 *    %x   unsigned int to hexadecimal (with small letters)
 *    %X   unsigned int to hexadecimal (with capital letters)
 *
 * The integer conversions support the following modifiers:
 *    0      Pad fields with minimum width using zeros instead of spaces
 *    [1-9]  Minimum field width
 *    l      Convert a long argument (signed long int, unsigned long int)
 *
 * If the return value is larger than \e n , the result was truncated to fit
 * into the buffer pointed to by \e s .
 *
 * \attention
 * The result string is only valid for locales that use the US-ASCII character
 * set (or a superset of it).
 *
 * \note
 * As a fallback, this function should be optimized for correctness, not speed.
 * Calling 'realloc()' on the buffer for every integer conversion is very
 * inefficient, but it's a safe solution for the problem how much buffer space
 * is required for a number specified with a size of "at least" something.
 */
#  define SNPRINTF_RESET(c) \
{ \
   cn = 0; \
   percent = c; \
   mod_l = 0; \
   mod_z = 0; \
   mod_w = 0; \
   lz = 1; \
   ds = 0; \
   conv = c; \
}
int  posix_snprintf(char*  s, size_t  n, const char*  f, ...)
{
   va_list  ap;                    /* Object for argument list handling */
   int  res;                       /* Result */
   int  err = 0;                   /* Error flag */
   int  err_no = POSIX_EINVAL;     /* Error code */

   int  conv;                      /* Conversion in progress flag */
   int  percent;                   /* Literal percent conversion flag */
   int  mod_l;                     /* Long modifier detected flag */
   int  mod_z;                     /* Leading zero modifier detected flag */
   int  mod_w;                     /* Minimum field width modifier if nonzero */
   int  hex_capital;

   const size_t  fn = strlen(f);   /* Length of format string */
   size_t  fi = 0;                 /* Index in format string */

   size_t  sn = 0;                 /* Length of target string (not truncated) */
   size_t  si = 0;                 /* Index in target buffer */

   const char*  c_s = "";          /* Pointer to string with conv. result */
   size_t  cn;                     /* Length of conversion result */
   size_t  ci;                     /* Index in conversion result */

   int  lz;                        /* Leading zero flag */
   int  ds;                        /* Deferred sign flag */
   size_t  exp;                    /* Exponent */

   long int  d;                    /* Parameter for signed int conversion */
   long int  d_p10 = 0;            /* Greatest power of 10 */
   long int  d_lim;                /* Limiting value */

   unsigned long int  u;           /* Parameter for unsigned int conversion */
   unsigned long int  u_p10 = 0;   /* Greatest power of 10 */
   unsigned long int  u_lim;       /* Limiting value */

   unsigned long int  u_p16 = 0;   /* Greatest power of 16 */
   unsigned long int  u_dig;       /* Digit value */

   char  b1[2];                    /* Buffer for single character string */
   char*  b = NULL;                /* Buffer for conversion results */
   size_t  bn = 0;                 /* Length of conversion buffer */

   long int  i;
   unsigned long int  ui;
   char  cc;

   /* Init state */
   SNPRINTF_RESET(0);
   va_start(ap, f);

   /* Do not accept zero buffer length */
   if(!n)
   {
      /* SUSv2 do not allow this, but C99 do => Reject it */
      print_error(
         "POSIX: snprintf(): Calling with zero buffer size is not portable");
      err_no = POSIX_EINVAL;
      err = -1;
   }
   /* Check for size limit */
   else if((unsigned int) POSIX_INT_MAX < n)
   {
      err_no = POSIX_EOVERFLOW;
      err = -1;
   }
   else
   {
      /* Init output to empty string after it was checked that n is nonzero */
      /* C99 allows (NULL == s) in this case what we should not dereference */
      s[0] = 0;
      /* Parse format string */
      while(fn > fi)
      {
         if (!conv)
         {
            /* Copy anything that is not a conversion specification literally */
            c_s = &f[fi];
            cn = strcspn(&f[fi], "%");
            fi += cn;
            conv = 1;
            percent = 0;
         }
         else
         {
            /* Conversion */
            cc = f[fi++];
            hex_capital = 0;
            switch(cc)
            {
               case '%':  /* Conversion marker */
               {
                  if(!percent)
                  {
                     /* Reset state for new conversion */
                     SNPRINTF_RESET(1);
                  }
                  else
                  {
                     /* Exec literal '%' conversion */
                     conv = 0;
                     b1[0] = '%';
                     b1[1] = 0;
                     c_s = b1;
                     cn = 1;
                  }
                  break;
               }
               case 'l':  /* Long modifier */
               {
                  mod_l = 1;
                  break;
               }
               case '0':  /* Leading zero modifier */
               {
                  mod_z = 1;
                  break;
               }
               case '1':  /* Minimum field width modifier */
               case '2':  /* Minimum field width modifier */
               case '3':  /* Minimum field width modifier */
               case '4':  /* Minimum field width modifier */
               case '5':  /* Minimum field width modifier */
               case '6':  /* Minimum field width modifier */
               case '7':  /* Minimum field width modifier */
               case '8':  /* Minimum field width modifier */
               case '9':  /* Minimum field width modifier */
               {
                  mod_w = (int) cc - 48;
                  break;
               }
               case 'd':  /* Exec signed integer conversion */
               {
                  conv = 0;
                  /* Calculate greatest power of 10 for signed integer */
                  if(mod_l)  { d_lim = POSIX_LONG_MAX; }
                  else  { d_lim = (long int) POSIX_INT_MAX; }
                  d_p10 = 10000L;  exp = 4;
                  while(1)
                  {
                     if(10 > d_lim / d_p10)  { break; }
                     d_p10 *= 10;  ++exp;
                  }
                  /* Maximum required size: Sign + exponent + 1 + termination */
                  bn = exp + (size_t) 3;
                  /* Allocate buffer for conversion result */
                  b = (char*) posix_realloc((void*) b, bn);
                  if (NULL == b)  { err_no = POSIX_ENOMEM;  err = 1;  break; }
                  /* Get signed value from stack */
                  if(mod_l)  { d = va_arg(ap, long int); }
                  else  { d = (long int) va_arg(ap, int); }
                  ci = 0;
                  /*
                   * Process sign
                   * Positive values are negated because on machines with 2's
                   * complement arithmetic the negative range is larger.
                   */
                  if(0L > d)
                  {
                     if(!mod_w)  { b[ci++] = '-'; }
                     else
                     {
                        --mod_w;
                        if (mod_z)  { b[ci++] = '-'; }
                        else  { ds = 1; }
                     }
                  }
                  else  { d = -d; }
                  /* Process digits */
                  i = -d_p10;
                  while(-1L >= i)
                  {
                     if(d <= i)
                     {
                        lz = 0;
                        if(ds)  { b[ci++] = '-';   ds = 0; }
                        b[ci++] = (char) (48L + d / i);
                        d -= d / i * i;
                     }
                     else if(lz)
                     {
                        if(!d && -1L == i)  { b[ci++] = '0'; }
                        else
                        {
                           /* Check minimum field width */
                           if(mod_w > exp)
                           {
                              if(!mod_z)  { b[ci++] = ' '; }
                              else  { b[ci++] = '0'; }
                           }
                        }
                     }
                     else  { b[ci++] = '0'; }
                     i /= 10L;  --exp;
                     if(ci >= bn)  { break; }
                  }
                  /* Sanity check */
                  if(ci >= bn)  { si = POSIX_SIZE_MAX;  err = 1;  break; }
                  /* Terminate conversion result */
                  b[ci] = 0;
                  c_s = b;
                  cn = ci;
                  break;
               }
               case 'u':  /* Exec unsigned integer conversion */
               {
                  conv = 0;
                  /* Calculate greatest power of 10 for unsigned integer */
                  if(mod_l)  { u_lim = POSIX_ULONG_MAX; }
                  else  { u_lim = (long int) POSIX_UINT_MAX; }
                  u_p10 = 10000UL;  exp = 4;
                  while(1)
                  {
                     if(10 > u_lim / u_p10)  { break; }
                     u_p10 *= 10;  ++exp;
                  }
                  /* Maximum required size: exponent + 1 + termination */
                  bn = exp + (size_t) 2;
                  /* Allocate buffer for conversion result */
                  b = (char*) posix_realloc((void*) b, bn);
                  if (NULL == b)  { err_no = POSIX_ENOMEM;  err = 1;  break; }
                  /* Get unsigned value from stack */
                  if(mod_l)  { u = va_arg(ap, unsigned long int); }
                  else  { u = (unsigned long int) va_arg(ap, unsigned int); }
                  ci = 0;
                  /* Process digits */
                  ui = u_p10;
                  while(1UL <= ui)
                  {
                     if(u >= ui)
                     {
                        lz = 0;
                        b[ci++] = (char) (48UL + u / ui);
                        u -= u / ui * ui;
                     }
                     else if(lz)
                     {
                        if(!u && 1UL == ui)  { b[ci++] = '0'; }
                        else
                        {
                           /* Check minimum field width */
                           if(mod_w > exp)
                           {
                              if(!mod_z)  { b[ci++] = ' '; }
                              else  { b[ci++] = '0'; }
                           }
                        }
                     }
                     else  { b[ci++] = '0'; }
                     ui /= 10UL;  --exp;
                     if(ci >= bn)  { break; }
                  }
                  /* Sanity check */
                  if(ci >= bn)  { si = POSIX_SIZE_MAX;  err = 1;  break; }
                  /* Terminate conversion result */
                  b[ci] = 0;
                  c_s = b;
                  cn = ci;
                  break;
               }
               case 'X':
               {
                  hex_capital = 1;
                  /* No break here is intended */
               }
               case 'x':  /* Exec unsigned integer conversion to hexadecimal */
               {
                  conv = 0;
                  /* Calculate greatest power of 16 for unsigned integer */
                  if(mod_l)  { u_lim = POSIX_ULONG_MAX; }
                  else  { u_lim = (long int) POSIX_UINT_MAX; }
                  u_p16 = 4096UL;  exp = 3;
                  while(1)
                  {
                     if(16 > u_lim / u_p16)  { break; }
                     u_p16 *= 16;  ++exp;
                  }
                  /* Maximum required size: exponent + 1 + termination */
                  bn = exp + (size_t) 2;
                  /* Allocate buffer for conversion result */
                  b = (char*) posix_realloc((void*) b, bn);
                  if (NULL == b)  { err_no = POSIX_ENOMEM;  err = 1;  break; }
                  /* Get unsigned value from stack */
                  if(mod_l)  { u = va_arg(ap, unsigned long int); }
                  else  { u = (unsigned long int) va_arg(ap, unsigned int); }
                  ci = 0;
                  /* Process digits */
                  ui = u_p16;
                  while(1UL <= ui)
                  {
                     if(u >= ui)
                     {
                        lz = 0;
                        u_dig = u / ui;
                        if(10UL > u_dig)  { b[ci++] = (char) (48UL + u_dig); }
                        else
                        {
                           u_dig -= 10;
                           if(hex_capital)  { b[ci++] = (char) (65UL + u_dig); }
                           else  { b[ci++] = (char) (97UL + u_dig); }
                        }
                        u -= u / ui * ui;
                     }
                     else if(lz)
                     {
                        if(!u && 1UL == ui)  { b[ci++] = '0'; }
                        else
                        {
                           /* Check minimum field width */
                           if(mod_w > exp)
                           {
                              if(!mod_z)  { b[ci++] = ' '; }
                              else  { b[ci++] = '0'; }
                           }
                        }
                     }
                     else  { b[ci++] = '0'; }
                     ui /= 16UL;  --exp;
                     if(ci >= bn)  { break; }
                  }
                  /* Sanity check */
                  if(ci >= bn)  { si = POSIX_SIZE_MAX;  err = 1;  break; }
                  /* Terminate conversion result */
                  b[ci] = 0;
                  c_s = b;
                  cn = ci;
                  break;
               }
               case 's':  /* Exec string conversion */
               {
                  if(mod_l)
                  {
                     print_error(
                        "POSIX: snprintf(): Invalid modifier in conversion");
                     err = 1;
                     break;
                  }
                  conv = 0;
                  c_s = va_arg(ap, char*);
                  cn = strlen(c_s);
                  break;
               }
               default:  /* Conversion type not supported */
               {
                  print_error(
                     "POSIX: snprintf(): Conversion type not supported");
                  /* Print a '?' to the output string */
                  b1[0] = '?';
                  b1[1] = 0;
                  c_s = b1;
                  cn = 1;
                  break;
               }
            }
            if(err)  break;
         }
         if(err)  break;

         /* Add up size of conversions */
         if(POSIX_SIZE_MAX - sn < cn)  { sn = POSIX_SIZE_MAX; }
         else  { sn += cn; }

         /* Copy conversion data to buffer */
         if(cn && si < n - 1)
         {
            if(n - 1 - si < cn)  { cn = n - 1 - si; }
            memcpy(&s[si], c_s, cn);
            si += cn;
            s[si] = 0;
         }
      }

      /* Sanity check */
      if (si >= n)
      {
         /* This should really never happen! */
         print_error("POSIX: snprintf(): AAARGHHH, buffer overflow detected!");
         /* Return error */
         err_no = POSIX_EOVERFLOW;
         res = -1;
      }

      posix_free((void*) b);
   }

   /* Set return value */
   if(!err)  { res = (int) sn; }
   else
   {
      posix_errno = err_no;
      res = -1;
   }

   va_end(ap);

   return(res);
}
#endif  /* !CFG_USE_POSIX_API_200112 && !CFG_USE_XSI */


/*
 * This implementation of 'getline()' should be POSIX.1-2008 compliant.
 * This function is thread safe if the underlaying 'fgetc()' is thread safe.
 */
#define GETLINE_BLOCKSIZE  (size_t) 128
posix_ssize_t  posix_getline(char**  lineptr, size_t*  n, FILE*  stream)
{
   posix_ssize_t  res = -1;
   size_t  len = *n;
   size_t  pos = 0;
   char*  memp;
   size_t  mems = GETLINE_BLOCKSIZE;
   int  rv = EOF;
   char  termreq = 0;

   /* Check whether parameters are valid */
   if(NULL == lineptr || NULL == n)  { posix_errno = POSIX_EINVAL; }
   else
   {
      /* Allocate line buffer if *lineptr is NULL */
      if (NULL == *lineptr)
      {
         *lineptr = (char*) posix_malloc(GETLINE_BLOCKSIZE);
         if(NULL == *lineptr)  { posix_errno = POSIX_ENOMEM; }
         else  { len = *n = GETLINE_BLOCKSIZE; }
      }

      /* Check for line buffer */
      if(NULL != *lineptr)
      {
         /* Limit usable buffer size if required */
         if((size_t) POSIX_SSIZE_MAX < len)  { len = (size_t) POSIX_SSIZE_MAX; }

         /* Read data */
         while((size_t) POSIX_SSIZE_MAX > pos && mems)
         {
            if(pos >= len)
            {
               /* Allocate more memory */
               if(GETLINE_BLOCKSIZE > POSIX_SIZE_MAX - len)
               {
                  mems = POSIX_SIZE_MAX - len;
                  if(!mems)
                  {
                     posix_errno = POSIX_ENOMEM;
                     break;
                  }
               }
               memp = posix_realloc(*lineptr, *n + mems);
               if(NULL == memp)
               {
                  posix_errno = POSIX_ENOMEM;
                  break;
               }
               else
               {
                  *lineptr = memp;
                  *n += mems;
                  len = *n;
               }
            }

            /* Check for termination request */
            if(termreq)
            {
               /* Terminate result string */
               (*lineptr)[pos] = 0;
               res = (posix_ssize_t) pos;
               break;
            }

            /* Get next character */
            rv = posix_fgetc(stream);
            if(EOF == rv)  break;
            (*lineptr)[pos++] = (char) rv;
            if('\n' == (char) rv)
            {
               /* EOL detected */
               termreq = 1;
            }
         }

         /* Check for error */
         if((size_t) POSIX_SSIZE_MAX <= pos)
         {
            posix_errno = POSIX_EOVERFLOW;
         }
      }
   }

   return(res);
}


/* ========================================================================== */
/* Address information */

#if !CFG_USE_IP6
/* This implementation of 'freeaddrinfo()' should be POSIX.1-2008 compliant. */
void  posix_freeaddrinfo(struct_posix_addrinfo*  info)
{
   struct_posix_addrinfo*  next;

   while(NULL != info)
   {
      /* Store pointer to next object */
      next = info->ai_next;
      /* Free memory for current object */
      posix_free((void*) info);
      /* Continue with next object */
      info = next;
   }
}


/*
 * This implementation of 'getaddrinfo()' should be POSIX.1-2008 compliant with the
 * following exceptions:
 * - A nodename must be specified, resolving a service name alone is not supported
 * - Only the flag AI_ADDRCONFIG is supported
 *
 * \attention
 * This emulation is based on deprecated functions that are removed from
 * POSIX.1-2008, this means it can only be used for older API versions.
 */
static posix_pthread_mutex_t  getaddrinfo_mutex
                              = POSIX_PTHREAD_MUTEX_INITIALIZER;
int  posix_getaddrinfo(const char*  nodename, const char*  servname,
                       const struct_posix_addrinfo*  hints,
                       struct_posix_addrinfo**  result)
{
   int  res = 0;
   struct_posix_addrinfo*  info = NULL;
   struct_posix_addrinfo*  tmp = NULL;
   struct_posix_servent*  se;
   struct_posix_hostent*  he;
   size_t  num = 0;
   posix_in_port_t  port = 0;
   unsigned int  p;
   int  family = hints->ai_family;

   *result = NULL;

   /* Only IPv4 is supported */
   if(POSIX_AF_UNSPEC == family)  { family = POSIX_AF_INET; }

   /* This implementation can't resolve service names alone */
   if(NULL == nodename)
   {
      print_error("POSIX: getaddrinfo(): Call without nodename not supported");
      res = POSIX_EAI_FAIL;
   }
   else if(POSIX_AF_INET != family)
   {
      /* Address family not supported */
      res = POSIX_EAI_FAMILY;
   }
   else if (POSIX_SOCK_STREAM != hints->ai_socktype)
   {
      /* Socket type not supported */
      res = POSIX_EAI_SOCKTYPE;
   }
   else
   {
      if(NULL != servname)
      {
         /* Resolve service name using BSD API */
         if(posix_pthread_mutex_lock(&getaddrinfo_mutex))
         {
            print_error("POSIX: getaddrinfo(): Locking mutex failed");
            res = POSIX_EAI_AGAIN;
         }
         else
         {
            /* Lock UI mutex */
            if(ts_lock_ui())
            {
               print_error("POSIX: Locking UI mutex failed");
               res = POSIX_EAI_AGAIN;
            }
            else
            {
               /* Check whether a number is specified */
               if(1 == sscanf(servname, "%u", &p))
               {
                  port = (posix_in_port_t) posix_htons((posix_in_port_t) p);
               }
               else
               {
                  se = posix_getservbyname(servname, NULL);
                  if(NULL != se)  { port = (posix_in_port_t) se->s_port; }
               }
               /* Unlock UI mutex */
               if(ts_unlock_ui())
               {
                  print_error("POSIX: Unlocking UI mutex failed");
               }
            }
            /*
             * In a multithreading environment the systems hostent buffer may be
             * shared between all threads. Therefore the mutex must stay locked
             * until we have copied the data to our thread local buffer.
             */
            posix_pthread_mutex_unlock(&getaddrinfo_mutex);
         }
      }

      /* Resolve hostname to IPv4 address using BSD API */
      if(NULL != servname && !port)
      {
         print_error("POSIX: getaddrinfo(): Service resolution failed");
         res = POSIX_EAI_FAIL;
      }
      else if(posix_pthread_mutex_lock(&getaddrinfo_mutex))
      {
         print_error("POSIX: getaddrinfo(): Locking mutex failed");
         res = POSIX_EAI_AGAIN;
      }
      else
      {
         /* Lock UI mutex */
         if(ts_lock_ui())
         {
            print_error("POSIX: Locking UI mutex failed");
            res = POSIX_EAI_AGAIN;
         }
         else
         {
            he = posix_gethostbyname(nodename);
            if(NULL == he)  { res = POSIX_EAI_NONAME; }
            else if(POSIX_AF_INET != he->h_addrtype || 4 != he->h_length)
            {
               print_error("POSIX: getaddrinfo(): Address type not supported");
               res = POSIX_EAI_FAIL;
            }
            else
            {
               do
               {
                  /* Allocate memory for result object */
                  tmp = (struct_posix_addrinfo*)
                        posix_malloc(sizeof(struct_posix_addrinfo)
                                     + sizeof(struct_posix_sockaddr_in));
                  if(NULL == tmp)  { res = POSIX_EAI_MEMORY;  break; }
                  else
                  {
                     if(NULL == info)  { *result = info = tmp; }
                     else  { info = info->ai_next = tmp; }
                     /* Populate information structure */
                     memset((void*) info, 0, sizeof(struct_posix_addrinfo));
                     info->ai_family = POSIX_AF_INET;
                     info->ai_socktype = hints->ai_socktype;
                     info->ai_protocol = hints->ai_protocol;
                     info->ai_addrlen
                       = (posix_socklen_t) sizeof(struct_posix_sockaddr_in);
                     info->ai_addr
                       = (struct_posix_sockaddr*) (void*)
                         ((char*) (void*) info + sizeof(struct_posix_addrinfo));
                     ((struct_posix_sockaddr_in*) info->ai_addr)->sin_family
                        = info->ai_family;
                     ((struct_posix_sockaddr_in*) info->ai_addr)->sin_port
                        = port;
                     ((struct_posix_sockaddr_in*) info->ai_addr)
                        ->sin_addr.s_addr
                        = *((posix_in_addr_t*) he->h_addr_list[num++]);
                     info->ai_next = NULL;
#if 0
                     /* For debugging */
                     printf("POSIX: getaddrinfo(): Resolve result: "
                            "%s / %s -> %s:%u\n", nodename, servname,
                            inet_ntoa(((struct_posix_sockaddr_in*)info->ai_addr)
                                      ->sin_addr),
                            (unsigned int) posix_ntohs(port));

#endif
                  }
               }
               while(NULL != he->h_addr_list[num]);
            }
            /* Unlock UI mutex */
            if(ts_unlock_ui())
            {
               print_error("POSIX: Unlocking UI mutex failed");
            }
         }
         /*
          * In a multithreading environment the systems hostent buffer may be
          * shared between all threads. Therefore the mutex must stay locked
          * until we have copied the data to our thread local buffer.
          */
         posix_pthread_mutex_unlock(&getaddrinfo_mutex);
         /* printf("POSIX: getaddrinfo(): %u result(s)\n", (unsigned int) num); */
      }
   }

   /* Free memory on error */
   /* printf("POSIX: getaddrinfo(): Result: %d\n", res); */
   if(res)  { posix_freeaddrinfo(*result); }

   return(res);
}


/* This implementation of 'gai_strerror()' should be POSIX.1-2008 compliant */
const char*  posix_gai_strerror(int  ecode)
{
   const char*  res;

   switch(ecode)
   {
      case POSIX_EAI_AGAIN:
      {
         res = "Name could not be resolved at this time";
         break;
      }
      case POSIX_EAI_BADFLAGS:
      {
         res = "Parameter had an invalid value";
         break;
      }
      case POSIX_EAI_FAIL:
      {
         res = "Non-recoverable error occurred while resolving the name";
         break;
      }
      case POSIX_EAI_FAMILY:
      {
         res = "Address family was not recognized";
         break;
      }
      case POSIX_EAI_MEMORY:
      {
         res = "Memory allocation failed";
         break;
      }
      case POSIX_EAI_NONAME:
      {
         res = "Name does not resolve for the supplied parameters";
         break;
      }
      case POSIX_EAI_OVERFLOW:
      {
         res = "Argument buffer overflowed (passed buffer too small)";
         break;
      }
      case POSIX_EAI_SERVICE:
      {
         res = "Service passed not recognized for the specified socket type";
         break;
      }
      case POSIX_EAI_SOCKTYPE:
      {
         res = "Intended socket type was not recognized";
         break;
      }
      case POSIX_EAI_SYSTEM:
      {
         res = "System error occurred";
         break;
      }
      default:
      {
         res = "Unknown error occurred";
         break;
      }
   }

   return(res);
}
#endif  /* !CFG_USE_IP6 */


/* EOF */
