mirror of
https://github.com/UzixLS/KernelEx.git
synced 2025-07-19 07:21:20 +03:00
import KernelEx-4.0-RC1
This commit is contained in:
487
kexcrt/vsnprintf.c
Normal file
487
kexcrt/vsnprintf.c
Normal file
@ -0,0 +1,487 @@
|
||||
/*
|
||||
* vsnprintf.c
|
||||
*
|
||||
* vsnprintf(), from which the rest of the printf()
|
||||
* family is built
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
enum flags {
|
||||
FL_ZERO = 0x01, /* Zero modifier */
|
||||
FL_MINUS = 0x02, /* Minus modifier */
|
||||
FL_PLUS = 0x04, /* Plus modifier */
|
||||
FL_TICK = 0x08, /* ' modifier */
|
||||
FL_SPACE = 0x10, /* Space modifier */
|
||||
FL_HASH = 0x20, /* # modifier */
|
||||
FL_SIGNED = 0x40, /* Number is signed */
|
||||
FL_UPPER = 0x80 /* Upper case digits */
|
||||
};
|
||||
|
||||
/* These may have to be adjusted on certain implementations */
|
||||
enum ranks {
|
||||
rank_char = -2,
|
||||
rank_short = -1,
|
||||
rank_int = 0,
|
||||
rank_long = 1,
|
||||
rank_longlong = 2
|
||||
};
|
||||
|
||||
#define MIN_RANK rank_char
|
||||
#define MAX_RANK rank_longlong
|
||||
|
||||
#define INTMAX_RANK rank_longlong
|
||||
#define SIZE_T_RANK rank_long
|
||||
#define PTRDIFF_T_RANK rank_long
|
||||
|
||||
#define EMIT(x) do { if (o<n){*q++ = (x);} o++; } while (0)
|
||||
|
||||
static size_t
|
||||
format_int(char *q, size_t n, uintmax_t val, enum flags flags,
|
||||
int base, int width, int prec)
|
||||
{
|
||||
char *qq;
|
||||
size_t o = 0, oo;
|
||||
static const char lcdigits[] = "0123456789abcdef";
|
||||
static const char ucdigits[] = "0123456789ABCDEF";
|
||||
const char *digits;
|
||||
uintmax_t tmpval;
|
||||
int minus = 0;
|
||||
int ndigits = 0, nchars;
|
||||
int tickskip, b4tick;
|
||||
|
||||
/* Select type of digits */
|
||||
digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
|
||||
|
||||
/* If signed, separate out the minus */
|
||||
if (flags & FL_SIGNED && (intmax_t) val < 0) {
|
||||
minus = 1;
|
||||
val = (uintmax_t) (-(intmax_t) val);
|
||||
}
|
||||
|
||||
/* Count the number of digits needed. This returns zero for 0. */
|
||||
tmpval = val;
|
||||
while (tmpval) {
|
||||
tmpval /= base;
|
||||
ndigits++;
|
||||
}
|
||||
|
||||
/* Adjust ndigits for size of output */
|
||||
|
||||
if (flags & FL_HASH && base == 8) {
|
||||
if (prec < ndigits + 1)
|
||||
prec = ndigits + 1;
|
||||
}
|
||||
|
||||
if (ndigits < prec) {
|
||||
ndigits = prec; /* Mandatory number padding */
|
||||
} else if (val == 0) {
|
||||
ndigits = 1; /* Zero still requires space */
|
||||
}
|
||||
|
||||
/* For ', figure out what the skip should be */
|
||||
if (flags & FL_TICK) {
|
||||
tickskip = (base == 16) ? 4 : 3;
|
||||
} else {
|
||||
tickskip = ndigits; /* No tick marks */
|
||||
}
|
||||
|
||||
/* Tick marks aren't digits, but generated by the number converter */
|
||||
ndigits += (ndigits - 1) / tickskip;
|
||||
|
||||
/* Now compute the number of nondigits */
|
||||
nchars = ndigits;
|
||||
|
||||
if (minus || (flags & (FL_PLUS | FL_SPACE)))
|
||||
nchars++; /* Need space for sign */
|
||||
if ((flags & FL_HASH) && base == 16) {
|
||||
nchars += 2; /* Add 0x for hex */
|
||||
}
|
||||
|
||||
/* Emit early space padding */
|
||||
if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars) {
|
||||
while (width > nchars) {
|
||||
EMIT(' ');
|
||||
width--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit nondigits */
|
||||
if (minus)
|
||||
EMIT('-');
|
||||
else if (flags & FL_PLUS)
|
||||
EMIT('+');
|
||||
else if (flags & FL_SPACE)
|
||||
EMIT(' ');
|
||||
|
||||
if ((flags & FL_HASH) && base == 16) {
|
||||
EMIT('0');
|
||||
EMIT((flags & FL_UPPER) ? 'X' : 'x');
|
||||
}
|
||||
|
||||
/* Emit zero padding */
|
||||
if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits) {
|
||||
while (width > nchars) {
|
||||
EMIT('0');
|
||||
width--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate the number. This is done from right to left. */
|
||||
q += ndigits; /* Advance the pointer to end of number */
|
||||
o += ndigits;
|
||||
qq = q;
|
||||
oo = o; /* Temporary values */
|
||||
|
||||
b4tick = tickskip;
|
||||
while (ndigits > 0) {
|
||||
if (!b4tick--) {
|
||||
qq--;
|
||||
oo--;
|
||||
ndigits--;
|
||||
if (oo < n)
|
||||
*qq = '_';
|
||||
b4tick = tickskip - 1;
|
||||
}
|
||||
qq--;
|
||||
oo--;
|
||||
ndigits--;
|
||||
if (oo < n)
|
||||
*qq = digits[val % base];
|
||||
val /= base;
|
||||
}
|
||||
|
||||
/* Emit late space padding */
|
||||
while ((flags & FL_MINUS) && width > nchars) {
|
||||
EMIT(' ');
|
||||
width--;
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
|
||||
{
|
||||
const char *p = format;
|
||||
char ch;
|
||||
char *q = buffer;
|
||||
size_t o = 0; /* Number of characters output */
|
||||
uintmax_t val = 0;
|
||||
int rank = rank_int; /* Default rank */
|
||||
int width = 0;
|
||||
int prec = -1;
|
||||
int base;
|
||||
size_t sz;
|
||||
enum flags flags = 0;
|
||||
enum {
|
||||
st_normal, /* Ground state */
|
||||
st_flags, /* Special flags */
|
||||
st_width, /* Field width */
|
||||
st_prec, /* Field precision */
|
||||
st_modifiers /* Length or conversion modifiers */
|
||||
} state = st_normal;
|
||||
const char *sarg; /* %s string argument */
|
||||
char carg; /* %c char argument */
|
||||
int slen; /* String length */
|
||||
|
||||
while ((ch = *p++)) {
|
||||
switch (state) {
|
||||
case st_normal:
|
||||
if (ch == '%') {
|
||||
state = st_flags;
|
||||
flags = 0;
|
||||
rank = rank_int;
|
||||
width = 0;
|
||||
prec = -1;
|
||||
} else {
|
||||
EMIT(ch);
|
||||
}
|
||||
break;
|
||||
|
||||
case st_flags:
|
||||
switch (ch) {
|
||||
case '-':
|
||||
flags |= FL_MINUS;
|
||||
break;
|
||||
case '+':
|
||||
flags |= FL_PLUS;
|
||||
break;
|
||||
case '\'':
|
||||
flags |= FL_TICK;
|
||||
break;
|
||||
case ' ':
|
||||
flags |= FL_SPACE;
|
||||
break;
|
||||
case '#':
|
||||
flags |= FL_HASH;
|
||||
break;
|
||||
case '0':
|
||||
flags |= FL_ZERO;
|
||||
break;
|
||||
default:
|
||||
state = st_width;
|
||||
p--; /* Process this character again */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case st_width:
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
width = width * 10 + (ch - '0');
|
||||
} else if (ch == '*') {
|
||||
width = va_arg(ap, int);
|
||||
if (width < 0) {
|
||||
width = -width;
|
||||
flags |= FL_MINUS;
|
||||
}
|
||||
} else if (ch == '.') {
|
||||
prec = 0; /* Precision given */
|
||||
state = st_prec;
|
||||
} else {
|
||||
state = st_modifiers;
|
||||
p--; /* Process this character again */
|
||||
}
|
||||
break;
|
||||
|
||||
case st_prec:
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
prec = prec * 10 + (ch - '0');
|
||||
} else if (ch == '*') {
|
||||
prec = va_arg(ap, int);
|
||||
if (prec < 0)
|
||||
prec = -1;
|
||||
} else {
|
||||
state = st_modifiers;
|
||||
p--; /* Process this character again */
|
||||
}
|
||||
break;
|
||||
|
||||
case st_modifiers:
|
||||
switch (ch) {
|
||||
/* Length modifiers - nonterminal sequences */
|
||||
case 'h':
|
||||
rank--; /* Shorter rank */
|
||||
break;
|
||||
case 'l':
|
||||
rank++; /* Longer rank */
|
||||
break;
|
||||
case 'j':
|
||||
rank = INTMAX_RANK;
|
||||
break;
|
||||
case 'z':
|
||||
rank = SIZE_T_RANK;
|
||||
break;
|
||||
case 't':
|
||||
rank = PTRDIFF_T_RANK;
|
||||
break;
|
||||
case 'L':
|
||||
case 'q':
|
||||
rank += 2;
|
||||
break;
|
||||
default:
|
||||
/* Output modifiers - terminal sequences */
|
||||
|
||||
/* Next state will be normal */
|
||||
state = st_normal;
|
||||
|
||||
/* Canonicalize rank */
|
||||
if (rank < MIN_RANK)
|
||||
rank = MIN_RANK;
|
||||
else if (rank > MAX_RANK)
|
||||
rank = MAX_RANK;
|
||||
|
||||
switch (ch) {
|
||||
case 'P': /* Upper case pointer */
|
||||
flags |= FL_UPPER;
|
||||
/* fall through */
|
||||
case 'p': /* Pointer */
|
||||
base = 16;
|
||||
prec = (CHAR_BIT*sizeof(void *)+3)/4;
|
||||
flags |= FL_HASH;
|
||||
val = (uintmax_t)(uintptr_t)
|
||||
va_arg(ap, void *);
|
||||
goto is_integer;
|
||||
|
||||
case 'd': /* Signed decimal output */
|
||||
case 'i':
|
||||
base = 10;
|
||||
flags |= FL_SIGNED;
|
||||
switch (rank) {
|
||||
case rank_char:
|
||||
/* Yes, all these casts are
|
||||
needed... */
|
||||
val = (uintmax_t)(intmax_t)
|
||||
(signed char)
|
||||
va_arg(ap, signed int);
|
||||
break;
|
||||
case rank_short:
|
||||
val = (uintmax_t)(intmax_t)
|
||||
(signed short)
|
||||
va_arg(ap, signed int);
|
||||
break;
|
||||
case rank_int:
|
||||
val = (uintmax_t)(intmax_t)
|
||||
va_arg(ap, signed int);
|
||||
break;
|
||||
case rank_long:
|
||||
val = (uintmax_t)(intmax_t)
|
||||
va_arg(ap, signed long);
|
||||
break;
|
||||
case rank_longlong:
|
||||
val = (uintmax_t)(intmax_t)
|
||||
va_arg(ap,
|
||||
int64_t);
|
||||
break;
|
||||
}
|
||||
goto is_integer;
|
||||
case 'o': /* Octal */
|
||||
base = 8;
|
||||
goto is_unsigned;
|
||||
case 'u': /* Unsigned decimal */
|
||||
base = 10;
|
||||
goto is_unsigned;
|
||||
case 'X': /* Upper case hexadecimal */
|
||||
flags |= FL_UPPER;
|
||||
/* fall through */
|
||||
case 'x': /* Hexadecimal */
|
||||
base = 16;
|
||||
goto is_unsigned;
|
||||
|
||||
is_unsigned:
|
||||
switch (rank) {
|
||||
case rank_char:
|
||||
val = (uintmax_t)
|
||||
(unsigned char)
|
||||
va_arg(ap, unsigned
|
||||
int);
|
||||
break;
|
||||
case rank_short:
|
||||
val = (uintmax_t)
|
||||
(unsigned short)
|
||||
va_arg(ap, unsigned
|
||||
int);
|
||||
break;
|
||||
case rank_int:
|
||||
val = (uintmax_t)
|
||||
va_arg(ap, unsigned
|
||||
int);
|
||||
break;
|
||||
case rank_long:
|
||||
val = (uintmax_t)
|
||||
va_arg(ap, unsigned
|
||||
long);
|
||||
break;
|
||||
case rank_longlong:
|
||||
val = (uintmax_t)
|
||||
va_arg(ap, uint64_t);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
is_integer:
|
||||
sz = format_int(q, (o < n) ? n - o : 0,
|
||||
val, flags, base,
|
||||
width, prec);
|
||||
q += sz;
|
||||
o += sz;
|
||||
break;
|
||||
|
||||
case 'c': /* Character */
|
||||
carg = (char)va_arg(ap, int);
|
||||
sarg = &carg;
|
||||
slen = 1;
|
||||
goto is_string;
|
||||
case 's': /* String */
|
||||
sarg = va_arg(ap, const char *);
|
||||
sarg = sarg ? sarg : "(null)";
|
||||
slen = strlen(sarg);
|
||||
goto is_string;
|
||||
|
||||
is_string:
|
||||
{
|
||||
char sch;
|
||||
int i;
|
||||
|
||||
if (prec != -1 && slen > prec)
|
||||
slen = prec;
|
||||
|
||||
if (width > slen
|
||||
&& !(flags & FL_MINUS)) {
|
||||
char pad =
|
||||
(flags & FL_ZERO) ?
|
||||
'0' : ' ';
|
||||
while (width > slen) {
|
||||
EMIT(pad);
|
||||
width--;
|
||||
}
|
||||
}
|
||||
for (i = slen; i; i--) {
|
||||
sch = *sarg++;
|
||||
EMIT(sch);
|
||||
}
|
||||
if (width > slen
|
||||
&& (flags & FL_MINUS)) {
|
||||
while (width > slen) {
|
||||
EMIT(' ');
|
||||
width--;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
{
|
||||
/* Output the number of
|
||||
characters written */
|
||||
|
||||
switch (rank) {
|
||||
case rank_char:
|
||||
*va_arg(ap,
|
||||
signed char *)
|
||||
= o;
|
||||
break;
|
||||
case rank_short:
|
||||
*va_arg(ap,
|
||||
signed short *)
|
||||
= o;
|
||||
break;
|
||||
case rank_int:
|
||||
*va_arg(ap,
|
||||
signed int *)
|
||||
= o;
|
||||
break;
|
||||
case rank_long:
|
||||
*va_arg(ap,
|
||||
signed long *)
|
||||
= o;
|
||||
break;
|
||||
case rank_longlong:
|
||||
*va_arg(ap,
|
||||
int64_t *)
|
||||
= o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* Anything else, including % */
|
||||
EMIT(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Null-terminate the string */
|
||||
if (o < n)
|
||||
*q = '\0'; /* No overflow */
|
||||
else if (n > 0)
|
||||
buffer[n - 1] = '\0'; /* Overflow - terminate at end of buffer */
|
||||
|
||||
return o;
|
||||
}
|
Reference in New Issue
Block a user