#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
#include "strfrom.h"
struct sf_buf {
char *s;
size_t n;
int pos;
};
static void
sf_putc(struct sf_buf *b, char c)
{
if (b->n > 0 && (size_t)b->pos < b->n - 1)
b->s[b->pos] = c;
b->pos++;
}
static void
sf_write(struct sf_buf *b, const char *src, int len)
{
int i;
for (i = 0; i < len; i++)
sf_putc(b, src[i]);
}
static void
sf_puts(struct sf_buf *b, const char *src)
{
while (*src != '\0')
sf_putc(b, *src++);
}
static void
sf_padc(struct sf_buf *b, char c, int count)
{
int i;
for (i = 0; i < count; i++)
sf_putc(b, c);
}
static void
sf_seal(struct sf_buf *b)
{
if (b->n > 0)
b->s[(size_t)b->pos < b->n ? b->pos : b->n - 1] = '\0';
}
static void
sf_putdp(struct sf_buf *b)
{
sf_puts(b, localeconv()->decimal_point);
}
static void
sf_emit_exp(struct sf_buf *b, char ec, int exp, int mindig)
{
char buf[16];
int len;
sf_putc(b, ec);
if (exp < 0) {
sf_putc(b, '-');
exp = -exp;
} else {
sf_putc(b, '+');
}
len = 0;
do {
buf[len++] = '0' + exp % 10;
exp /= 10;
} while (exp != 0);
while (len < mindig)
buf[len++] = '0';
while (len > 0)
sf_putc(b, buf[--len]);
}
char
__sf_parse_fmt(const char *fmt, int *prec)
{
const char *p;
*prec = -1;
if (*fmt != '%')
return ('\0');
p = fmt + 1;
if (*p == '.') {
*prec = 0;
while (*++p >= '0' && *p <= '9')
*prec = *prec * 10 + (*p - '0');
}
switch (*p) {
case 'a': case 'A':
case 'e': case 'E':
case 'f': case 'F':
case 'g': case 'G':
break;
default:
return ('\0');
}
return (p[1] == '\0' ? *p : '\0');
}
int
__sf_edoofus(char *s, size_t n)
{
struct sf_buf b;
errno = EDOOFUS;
b.s = s;
b.n = n;
b.pos = 0;
sf_puts(&b, "EDOOFUS");
sf_seal(&b);
return (b.pos);
}
static int
sf_special(struct sf_buf *b, char conv, int signflag, int is_nan)
{
int upper;
b->pos = 0;
upper = isupper((unsigned char)conv);
if (!is_nan && signflag)
sf_putc(b, '-');
sf_puts(b, is_nan ? (upper ? "NAN" : "nan") : (upper ? "INF" : "inf"));
sf_seal(b);
return (b->pos);
}
static int
sf_efmt(struct sf_buf *b, int prec, char conv,
const char *digits, int ndig, int decpt, int signflag)
{
int avail, copy;
b->pos = 0;
if (signflag)
sf_putc(b, '-');
sf_putc(b, ndig > 0 ? digits[0] : '0');
if (prec > 0) {
sf_putdp(b);
avail = ndig > 1 ? ndig - 1 : 0;
copy = avail < prec ? avail : prec;
sf_write(b, digits + 1, copy);
sf_padc(b, '0', prec - copy);
}
sf_emit_exp(b, isupper((unsigned char)conv) ? 'E' : 'e', decpt - 1, 2);
sf_seal(b);
return (b->pos);
}
static int
sf_ffmt(struct sf_buf *b, int prec, int signflag,
const char *digits, int ndig, int decpt)
{
int avail, copy, rem, zc;
b->pos = 0;
if (signflag)
sf_putc(b, '-');
if (decpt <= 0) {
sf_putc(b, '0');
if (prec > 0) {
sf_putdp(b);
zc = -decpt < prec ? -decpt : prec;
rem = prec - zc;
copy = ndig < rem ? ndig : rem;
sf_padc(b, '0', zc);
sf_write(b, digits, copy);
sf_padc(b, '0', rem - copy);
}
} else {
copy = ndig < decpt ? ndig : decpt;
sf_write(b, digits, copy);
sf_padc(b, '0', decpt - copy);
if (prec > 0) {
sf_putdp(b);
avail = ndig - decpt;
if (avail < 0)
avail = 0;
copy = avail < prec ? avail : prec;
sf_write(b, digits + decpt, copy);
sf_padc(b, '0', prec - copy);
}
}
sf_seal(b);
return (b->pos);
}
static int
sf_gfmt(struct sf_buf *b, int prec, char conv,
const char *digits, int ndig, int decpt, int signflag)
{
int ep, copy, frac;
b->pos = 0;
ep = prec == 0 ? 1 : prec;
while (ndig > 1 && digits[ndig - 1] == '0')
ndig--;
if (signflag)
sf_putc(b, '-');
if (decpt > -4 && decpt <= ep) {
if (decpt <= 0) {
sf_putc(b, '0');
if (ndig > 0) {
sf_putdp(b);
sf_padc(b, '0', -decpt);
sf_write(b, digits, ndig);
}
} else {
copy = ndig < decpt ? ndig : decpt;
sf_write(b, digits, copy);
sf_padc(b, '0', decpt - copy);
frac = ndig - decpt;
if (frac > 0) {
sf_putdp(b);
sf_write(b, digits + decpt, frac);
}
}
} else {
sf_putc(b, ndig > 0 ? digits[0] : '0');
if (ndig > 1) {
sf_putdp(b);
sf_write(b, digits + 1, ndig - 1);
}
sf_emit_exp(b, isupper((unsigned char)conv) ? 'E' : 'e',
decpt - 1, 2);
}
sf_seal(b);
return (b->pos);
}
static int
sf_afmt(struct sf_buf *b, int user_prec, char conv,
const char *digits, int ndig, int decpt, int signflag)
{
int after, copy;
b->pos = 0;
if (signflag)
sf_putc(b, '-');
sf_putc(b, '0');
sf_putc(b, isupper((unsigned char)conv) ? 'X' : 'x');
sf_putc(b, ndig > 0 ? digits[0] : '0');
after = ndig - 1;
if (user_prec < 0) {
if (after > 0) {
sf_putdp(b);
sf_write(b, digits + 1, after);
}
} else if (user_prec > 0) {
sf_putdp(b);
copy = after < user_prec ? after : user_prec;
sf_write(b, digits + 1, copy);
sf_padc(b, '0', user_prec - copy);
}
sf_emit_exp(b, isupper((unsigned char)conv) ? 'P' : 'p', decpt - 1, 1);
sf_seal(b);
return (b->pos);
}
const char *
__sf_xdigits(char conv)
{
return (isupper((unsigned char)conv) ? "0123456789ABCDEF" :
"0123456789abcdef");
}
void
__sf_decimal_mode(char lc, int prec, int *mode, int *ndig_req)
{
switch (lc) {
case 'e':
*mode = 2;
*ndig_req = prec + 1;
break;
case 'f':
*mode = 3;
*ndig_req = prec;
break;
case 'g':
*mode = 2;
*ndig_req = prec == 0 ? 1 : prec;
break;
default:
__unreachable();
}
}
int
__sf_render_hex(char *s, size_t n, char conv, int prec,
char *digits, char *dend, int decpt, int signflag)
{
struct sf_buf b;
int ndig, usprec;
b.s = s;
b.n = n;
if (decpt == INT_MAX)
return (sf_special(&b, conv, signflag, digits[0] == 'N'));
ndig = (int)(dend - digits);
usprec = prec >= 0 ? prec : ndig - 1;
return (sf_afmt(&b, usprec, conv, digits, ndig, decpt, signflag));
}
int
__sf_render_decimal(char *s, size_t n, char conv, char lc, int prec,
char *digits, char *dend, int decpt, int signflag, int is_special)
{
struct sf_buf b;
int ndig;
b.s = s;
b.n = n;
if (is_special)
return (sf_special(&b, conv, signflag, digits[0] == 'N'));
ndig = (int)(dend - digits);
switch (lc) {
case 'e':
return (sf_efmt(&b, prec, conv, digits, ndig, decpt, signflag));
case 'f':
return (sf_ffmt(&b, prec, signflag, digits, ndig, decpt));
case 'g':
return (sf_gfmt(&b, prec, conv, digits, ndig, decpt, signflag));
default:
__unreachable();
}
}