mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
191 lines
3.9 KiB
C
191 lines
3.9 KiB
C
/* fmtulong.c -- Convert unsigned long int to string. */
|
|
|
|
/* Copyright (C) 1998-2011 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Bash, the Bourne Again SHell.
|
|
|
|
Bash 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Bash 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 Bash. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#if defined (HAVE_UNISTD_H)
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#if defined (HAVE_LIMITS_H)
|
|
# include <limits.h>
|
|
#endif
|
|
|
|
#include "bashansi.h"
|
|
#ifdef HAVE_STDDEF_H
|
|
# include <stddef.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_STDINT_H
|
|
# include <stdint.h>
|
|
#endif
|
|
#ifdef HAVE_INTTYPES_H
|
|
# include <inttypes.h>
|
|
#endif
|
|
#include "chartypes.h"
|
|
#include <errno.h>
|
|
|
|
#include "bashintl.h"
|
|
|
|
#include "stdc.h"
|
|
|
|
#include "typemax.h"
|
|
|
|
#ifndef errno
|
|
extern int errno;
|
|
#endif
|
|
|
|
#define x_digs "0123456789abcdef"
|
|
#define X_digs "0123456789ABCDEF"
|
|
|
|
/* XXX -- assumes uppercase letters, lowercase letters, and digits are
|
|
contiguous */
|
|
#define FMTCHAR(x) \
|
|
((x) < 10) ? (x) + '0' \
|
|
: (((x) < 36) ? (x) - 10 + 'a' \
|
|
: (((x) < 62) ? (x) - 36 + 'A' \
|
|
: (((x) == 62) ? '@' : '_')))
|
|
|
|
#ifndef FL_PREFIX
|
|
# define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */
|
|
# define FL_ADDBASE 0x02 /* add base# prefix to converted value */
|
|
# define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */
|
|
# define FL_UNSIGNED 0x08 /* don't add any sign */
|
|
#endif
|
|
|
|
#ifndef LONG
|
|
# define LONG long
|
|
# define UNSIGNED_LONG unsigned long
|
|
#endif
|
|
|
|
/* `unsigned long' (or unsigned long long) to string conversion for a given
|
|
base. The caller passes the output buffer and the size. This should
|
|
check for buffer underflow, but currently does not. */
|
|
char *
|
|
fmtulong (ui, base, buf, len, flags)
|
|
UNSIGNED_LONG ui;
|
|
int base;
|
|
char *buf;
|
|
size_t len;
|
|
int flags;
|
|
{
|
|
char *p;
|
|
int sign;
|
|
LONG si;
|
|
|
|
if (base == 0)
|
|
base = 10;
|
|
|
|
if (base < 2 || base > 64)
|
|
{
|
|
#if 1
|
|
/* XXX - truncation possible with long translation */
|
|
strncpy (buf, _("invalid base"), len - 1);
|
|
buf[len-1] = '\0';
|
|
errno = EINVAL;
|
|
return (p = buf);
|
|
#else
|
|
base = 10;
|
|
#endif
|
|
}
|
|
|
|
sign = 0;
|
|
if ((flags & FL_UNSIGNED) == 0 && (LONG)ui < 0)
|
|
{
|
|
ui = -ui;
|
|
sign = '-';
|
|
}
|
|
|
|
p = buf + len - 2;
|
|
p[1] = '\0';
|
|
|
|
/* handle common cases explicitly */
|
|
switch (base)
|
|
{
|
|
case 10:
|
|
if (ui < 10)
|
|
{
|
|
*p-- = TOCHAR (ui);
|
|
break;
|
|
}
|
|
/* Favor signed arithmetic over unsigned arithmetic; it is faster on
|
|
many machines. */
|
|
if ((LONG)ui < 0)
|
|
{
|
|
*p-- = TOCHAR (ui % 10);
|
|
si = ui / 10;
|
|
}
|
|
else
|
|
si = ui;
|
|
do
|
|
*p-- = TOCHAR (si % 10);
|
|
while (si /= 10);
|
|
break;
|
|
|
|
case 8:
|
|
do
|
|
*p-- = TOCHAR (ui & 7);
|
|
while (ui >>= 3);
|
|
break;
|
|
|
|
case 16:
|
|
do
|
|
*p-- = (flags & FL_HEXUPPER) ? X_digs[ui & 15] : x_digs[ui & 15];
|
|
while (ui >>= 4);
|
|
break;
|
|
|
|
case 2:
|
|
do
|
|
*p-- = TOCHAR (ui & 1);
|
|
while (ui >>= 1);
|
|
break;
|
|
|
|
default:
|
|
do
|
|
*p-- = FMTCHAR (ui % base);
|
|
while (ui /= base);
|
|
break;
|
|
}
|
|
|
|
if ((flags & FL_PREFIX) && (base == 8 || base == 16))
|
|
{
|
|
if (base == 16)
|
|
{
|
|
*p-- = (flags & FL_HEXUPPER) ? 'X' : 'x';
|
|
*p-- = '0';
|
|
}
|
|
else if (p[1] != '0')
|
|
*p-- = '0';
|
|
}
|
|
else if ((flags & FL_ADDBASE) && base != 10)
|
|
{
|
|
*p-- = '#';
|
|
*p-- = TOCHAR (base % 10);
|
|
if (base > 10)
|
|
*p-- = TOCHAR (base / 10);
|
|
}
|
|
|
|
if (sign)
|
|
*p-- = '-';
|
|
|
|
return (p + 1);
|
|
}
|