/**
 * printf helper function that converts unsigned integer to string
 */
static char*
libc_printf_uint_to_string (uintmax_t value, /**< integer value */
                            char *buffer_p, /**< buffer for output string */
                            size_t buffer_size, /**< buffer size */
                            const char *alphabet, /**< alphabet used for digits */
                            uint32_t radix) /**< radix */
{
  char *str_buffer_end = buffer_p + buffer_size;
  char *str_p = str_buffer_end;
  *--str_p = '\0';

  LIBC_ASSERT (radix >= 2);

  if ((radix & (radix - 1)) != 0)
  {
    /*
     * Radix is not power of 2. Only 32-bit numbers are supported in this mode.
     */
    LIBC_ASSERT ((value >> 32) == 0);

    uint32_t value_lo = (uint32_t) value;

    while (value_lo != 0)
    {
      LIBC_ASSERT (str_p != buffer_p);

      *--str_p = alphabet[ value_lo % radix ];
      value_lo /= radix;
    }
  }
/**
 * Setup new memory limits
 */
void
jrt_set_mem_limits (size_t data_size, /**< limit for data + bss + brk heap */
                    size_t stack_size) /**< limit for stack */
{
  struct
  {
    unsigned long long rlim_cur;
    unsigned long long rlim_max;
  } data_limit = { data_size, data_size };

  struct
  {
    unsigned long long rlim_cur;
    unsigned long long rlim_max;
  } stack_limit = { stack_size, stack_size };

  long int ret;

#ifdef __TARGET_HOST_x64
  ret = syscall_2 (__NR_setrlimit, RLIMIT_DATA, (intptr_t) &data_limit);
  LIBC_ASSERT (ret == 0);

  ret = syscall_2 (__NR_setrlimit, RLIMIT_STACK, (intptr_t) &stack_limit);
  LIBC_ASSERT (ret == 0);
#elif defined (__TARGET_HOST_ARMv7)
  ret = syscall_3 (__NR_prlimit64, 0, RLIMIT_DATA, (intptr_t) &data_limit);
  LIBC_ASSERT (ret == 0);

  ret = syscall_3 (__NR_prlimit64, 0, RLIMIT_STACK, (intptr_t) &stack_limit);
  LIBC_ASSERT (ret == 0);
#elif defined (__TARGET_HOST_x86)
# error "__TARGET_HOST_x86 case is not implemented"
#else /* !__TARGET_HOST_x64 && !__TARGET_HOST_ARMv7 && !__TARGET_HOST_x86 */
# error "!__TARGET_HOST_x64 && !__TARGET_HOST_ARMv7 && !__TARGET_HOST_x86"
#endif /* !__TARGET_HOST_x64 && !__TARGET_HOST_ARMv7 && !__TARGET_HOST_x86 */
} /* jrt_set_mem_limits */
/**
 * fopen
 *
 * @return FILE pointer - upon successful completion,
 *         NULL - otherwise
 */
FILE *
fopen (const char *path, /**< file path */
       const char *mode) /**< file open mode */
{
  bool may_read = false;
  bool may_write = false;
  bool truncate = false;
  bool create_if_not_exist = false;
  bool position_at_end = false;

  LIBC_ASSERT (path != NULL && mode != NULL);
  LIBC_ASSERT (mode[1] == '+' || mode[1] == '\0');

  switch (mode[0])
  {
    case 'r':
    {
      may_read = true;
      may_write = (mode[1] == '+');
      break;
    }
    case 'w':
    {
      may_write = true;
      truncate = true;
      create_if_not_exist = true;
      may_read = (mode[1] == '+');
      break;
    }
    case 'a':
    {
      may_write = true;
      position_at_end = true;
      create_if_not_exist = true;
      if (mode[1] == '+')
      {
        /* Not supported */
        LIBC_UNREACHABLE ();
      }
      break;
    }
    default:
    {
      LIBC_UNREACHABLE ();
    }
  }

  int flags = 0;
  int access = S_IRUSR | S_IWUSR;
  if (may_read && !may_write)
  {
    flags = O_RDONLY;
  }
  else if (!may_read && may_write)
  {
    flags = O_WRONLY;
  }
  else
  {
    LIBC_ASSERT (may_read && may_write);

    flags = O_RDWR;
  }

  if (truncate)
  {
    flags |= O_TRUNC;
  }

  if (create_if_not_exist)
  {
    flags |= O_CREAT;
  }

  if (position_at_end)
  {
    flags |= O_APPEND;
  }

  long int ret = syscall_3 (__NR_open, (long int) path, flags, access);

  return (void *) (uintptr_t) (ret);
} /* fopen */