#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//
// Converte un intero senza segno di rango massimo in una stringa.
//
static size_t
uimaxtoa (uintmax_t integer, char *buffer, int base, int uppercase, size_t n)
{
uintmax_t integer_copy = integer;
size_t digits;
int b;
unsigned char remainder;
for (digits = 0; integer_copy > 0; digits++)
{
integer_copy = integer_copy / base;
}
if (buffer == NULL && integer == 0) return 1;
if (buffer == NULL && integer > 0) return digits;
if (integer == 0)
{
buffer[0] = '0';
buffer[1] = '\0';
return 1;
}
if (n > 0 && digits > n) digits = n; // Sistema il numero massimo
// di cifre.
*(buffer + digits) = '\0'; // Fine della stringa.
for (b = digits - 1; integer != 0 && b >= 0; b--)
{
remainder = integer % base;
integer = integer / base;
if (remainder <= 9)
{
*(buffer + b) = remainder + '0';
}
else
{
if (uppercase)
{
*(buffer + b) = remainder - 10 + 'A';
}
else
{
*(buffer + b) = remainder - 10 + 'a';
}
}
}
return digits;
}
//
// Converte un intero con segno di rango massimo in una stringa.
//
static size_t
imaxtoa (intmax_t integer, char *buffer, int base, int uppercase, size_t n)
{
if (integer >= 0)
{
return uimaxtoa (integer, buffer, base, uppercase, n);
}
//
// A questo punto c'è un valore negativo, inferiore a zero.
//
if (buffer == NULL)
{
return uimaxtoa (-integer, NULL, base, uppercase, n) + 1;
}
*buffer = '-'; // Serve il segno meno all'inizio.
if (n == 1)
{
*(buffer + 1) = '\0';
return 1;
}
else
{
return uimaxtoa (-integer, buffer+1, base, uppercase, n-1) + 1;
}
}
//
// Converte un intero con segno di rango massimo in una stringa,
// mettendo il segno anche se è positivo.
//
static size_t
simaxtoa (intmax_t integer, char *buffer, int base, int uppercase, size_t n)
{
if (buffer == NULL && integer >= 0)
{
return uimaxtoa (integer, NULL, base, uppercase, n) + 1;
}
if (buffer == NULL && integer < 0)
{
return uimaxtoa (-integer, NULL, base, uppercase, n) + 1;
}
//
// A questo punto «buffer» è diverso da NULL.
//
if (integer >= 0)
{
*buffer = '+';
}
else
{
*buffer = '-';
}
if (n == 1)
{
*(buffer + 1) = '\0';
return 1;
}
if (integer >= 0)
{
return uimaxtoa (integer, buffer+1, base, uppercase, n-1) + 1;
}
else
{
return uimaxtoa (-integer, buffer+1, base, uppercase, n-1) + 1;
}
}
//
// Converte un intero senza segno di rango massimo in una stringa,
// provvedendo a sistemare anche l'allineamento.
//
static size_t
uimaxtoa_fill (uintmax_t integer, char *buffer, int base,
int uppercase, int width, int filler, int max)
{
if (max < 0) return 0; // «max» deve essere un valore positivo.
size_t size_i = uimaxtoa (integer, NULL, base, uppercase, 0);
size_t size_f;
if (width > 0 && max > 0 && width > max) width = max;
if (width < 0 && -max < 0 && width < -max) width = -max;
if (size_i > abs (width))
{
return uimaxtoa (integer, buffer, base, uppercase, abs (width));
}
if (width == 0 && max > 0)
{
return uimaxtoa (integer, buffer, base, uppercase, max);
}
if (width == 0)
{
return uimaxtoa (integer, buffer, base, uppercase, abs (width));
}
//
// size_i <= abs (width).
//
size_f = abs (width) - size_i;
if (width < 0)
{
// Allineamento a sinistra.
uimaxtoa (integer, buffer, base, uppercase, 0);
memset (buffer + size_i, filler, size_f);
}
else
{
// Allineamento a destra.
memset (buffer, filler, size_f);
uimaxtoa (integer, buffer + size_f, base, uppercase, 0);
}
*(buffer + abs (width)) = '\0';
return abs (width);
}
//
// Converte un intero con segno di rango massimo in una stringa,
// provvedendo a sistemare anche l'allineamento.
//
static size_t
imaxtoa_fill (intmax_t integer, char *buffer, int base,
int uppercase, int width, int filler, int max)
{
if (max < 0) return 0; // «max» deve essere un valore positivo.
size_t size_i = imaxtoa (integer, NULL, base, uppercase, 0);
size_t size_f;
if (width > 0 && max > 0 && width > max) width = max;
if (width < 0 && -max < 0 && width < -max) width = -max;
if (size_i > abs (width))
{
return imaxtoa (integer, buffer, base, uppercase, abs (width));
}
if (width == 0 && max > 0)
{
return imaxtoa (integer, buffer, base, uppercase, max);
}
if (width == 0)
{
return imaxtoa (integer, buffer, base, uppercase, abs (width));
}
// size_i <= abs (width).
size_f = abs (width) - size_i;
if (width < 0)
{
// Allineamento a sinistra.
imaxtoa (integer, buffer, base, uppercase, 0);
memset (buffer + size_i, filler, size_f);
}
else
{
// Allineamento a destra.
memset (buffer, filler, size_f);
imaxtoa (integer, buffer + size_f, base, uppercase, 0);
}
*(buffer + abs (width)) = '\0';
return abs (width);
}
//
// Converte un intero con segno di rango massimo in una stringa,
// mettendo il segno anche se è positivo, provvedendo a sistemare
// l'allineamento.
//
static size_t
simaxtoa_fill (intmax_t integer, char *buffer, int base,
int uppercase, int width, int filler, int max)
{
if (max < 0) return 0; // «max» deve essere un valore positivo.
size_t size_i = simaxtoa (integer, NULL, base, uppercase, 0);
size_t size_f;
if (width > 0 && max > 0 && width > max) width = max;
if (width < 0 && -max < 0 && width < -max) width = -max;
if (size_i > abs (width))
{
return simaxtoa (integer, buffer, base, uppercase, abs (width));
}
if (width == 0 && max > 0)
{
return simaxtoa (integer, buffer, base, uppercase, max);
}
if (width == 0)
{
return simaxtoa (integer, buffer, base, uppercase, abs (width));
}
//
// size_i <= abs (width).
//
size_f = abs (width) - size_i;
if (width < 0)
{
// Allineamento a sinistra.
simaxtoa (integer, buffer, base, uppercase, 0);
memset (buffer + size_i, filler, size_f);
}
else
{
// Allineamento a destra.
memset (buffer, filler, size_f);
simaxtoa (integer, buffer + size_f, base, uppercase, 0);
}
*(buffer + abs (width)) = '\0';
return abs (width);
}
//
// Trasferisce una stringa provvedendo all'allineamento.
//
static size_t
strtostr_fill (char *string, char *buffer, int width, int filler, int max)
{
if (max < 0) return 0; // «max» deve essere un valore positivo.
size_t size_s = strlen (string);
size_t size_f;
if (width > 0 && max > 0 && width > max) width = max;
if (width < 0 && -max < 0 && width < -max) width = -max;
if (width != 0 && size_s > abs (width))
{
memcpy (buffer, string, abs (width));
buffer[width] = '\0';
return width;
}
if (width == 0 && max > 0 && size_s > max)
{
memcpy (buffer, string, max);
buffer[max] = '\0';
return max;
}
if (width == 0 && max > 0 && size_s < max)
{
memcpy (buffer, string, size_s);
buffer[size_s] = '\0';
return size_s;
}
//
// width =! 0
// size_s <= abs (width)
//
size_f = abs (width) - size_s;
if (width < 0)
{
// Allineamento a destra.
memset (buffer, filler, size_f);
strncpy (buffer+size_f, string, size_s);
}
else
{
// Allineamento a sinistra.
strncpy (buffer, string, size_s);
memset (buffer+size_s, filler, size_f);
}
*(buffer + abs (width)) = '\0';
return abs (width);
}
//
// La funzione «vsnprintf()»
//
int
vsnprintf (char *restrict string, size_t n,
const char *restrict format, va_list ap)
{
if (n > INT_MAX) n = INT_MAX; // «n» non può essere superiore
// a INT_MAX.
//
// Al massimo si producono "n-1" caratteri, + '\0'.
// "n" viene usato anche come dimensione massima per le
// stringhe interne, se non è troppo grande.
//
int f = 0;
int s = 0;
int remain = n - 1;
bool specifier = 0;
bool specifier_flags = 0;
bool specifier_width = 0;
bool specifier_precision = 0;
bool specifier_type = 0;
bool flag_plus = 0;
bool flag_minus = 0;
bool flag_space = 0;
bool flag_alternate = 0;
bool flag_zero = 0;
int alignment;
int filler;
intmax_t value_i;
uintmax_t value_ui;
char *value_cp;
size_t width;
size_t precision;
size_t str_size = n > 1024 ? 1024 : n;
char width_string[str_size];
char precision_string[str_size];
int w;
int p;
width_string[0] = '\0';
precision_string[0] = '\0';
while (format[f] != 0 && s < (n - 1))
{
if (!specifier)
{
if (format[f] != '%')
{
string[s] = format[f];
s++;
remain--;
f++;
continue;
}
if (format[f] == '%' && format[f+1] == '%')
{
string[s] = '%';
f++;
f++;
s++;
remain--;
continue;
}
if (format[f] == '%')
{
f++;
specifier = 1;
specifier_flags = 1;
continue;
}
}
if (specifier && specifier_flags)
{
if (format[f] == '+')
{
flag_plus = 1;
f++;
continue;
}
else if (format[f] == '-')
{
flag_minus = 1;
f++;
continue;
}
else if (format[f] == ' ')
{
flag_space = 1;
f++;
continue;
}
else if (format[f] == '#')
{
flag_alternate = 1;
f++;
continue;
}
else if (format[f] == '0')
{
flag_zero = 1;
f++;
continue;
}
else
{
specifier_flags = 0;
specifier_width = 1;
}
}
if (specifier && specifier_width)
{
for (w = 0; format[f] >= '0' && format[f] <= '9'
&& w < str_size; w++)
{
width_string[w] = format[f];
f++;
}
width_string[w] = '\0';
specifier_width = 0;
if (format[f] == '.')
{
specifier_precision = 1;
f++;
}
else
{
specifier_precision = 0;
specifier_type = 1;
}
}
if (specifier && specifier_precision)
{
for (p = 0; format[f] >= '0' && format[f] <= '9'
&& p < str_size; p++)
{
precision_string[p] = format[f];
p++;
}
precision_string[p] = '\0';
specifier_precision = 0;
specifier_type = 1;
}
if (specifier && specifier_type)
{
width = atoi (width_string);
precision = atoi (precision_string);
filler = ' ';
if (flag_zero) filler = '0';
if (flag_space) filler = ' ';
alignment = width;
if (flag_minus)
{
alignment = -alignment;
filler = ' '; // Il carattere di riempimento
// non può essere zero.
}
if (format[f] == 'h' && format[f+1] == 'h')
{
if (format[f+2] == 'd' || format[f+2] == 'i')
{
// signed char, base 10.
value_i = va_arg (ap, int);
if (flag_plus)
{
s += simaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
else
{
s += imaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
f += 3;
}
else if (format[f+2] == 'u')
{
// unsigned char, base 10.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
alignment, filler, remain);
f += 3;
}
else if (format[f+2] == 'o')
{
// unsigned char, base 8.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 8, 0,
alignment, filler, remain);
f += 3;
}
else if (format[f+2] == 'x')
{
// unsigned char, base 16.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 0,
alignment, filler, remain);
f += 3;
}
else if (format[f+2] == 'X')
{
// unsigned char, base 16.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 1,
alignment, filler, remain);
f += 3;
}
else if (format[f+2] == 'b')
{
// unsigned char, base 2 (estensione).
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 2, 0,
alignment, filler, remain);
f += 3;
}
else // Specificatore errato;
{
f += 2;
}
}
else if (format[f] == 'h')
{
if (format[f+1] == 'd' || format[f+1] == 'i')
{
// short int, base 10.
value_i = va_arg (ap, int);
if (flag_plus)
{
s += simaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
else
{
s += imaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
f += 2;
}
else if (format[f+1] == 'u')
{
// unsigned short int, base 10.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'o')
{
// unsigned short int, base 8.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 8, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'x')
{
// unsigned short int, base 16.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'X')
{
// unsigned short int, base 16.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 1,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'b')
{
// unsigned short int, base 2 (estensione).
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 2, 0,
alignment, filler, remain);
f += 2;
}
else // Specificatore errato;
{
f += 1;
}
}
//
// Il tipo «long long int» non c'è, perché il compilatore
// GNU C, per poter eseguire le divisioni e il calcolo del
// resto, ha bisogno delle funzioni di libreria
// «__udivdi3()» e «__umoddi3()».
//
else if (format[f] == 'l')
{
if (format[f+1] == 'd' || format[f+1] == 'i')
{
// long int base 10.
value_i = va_arg (ap, long int);
if (flag_plus)
{
s += simaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
else
{
s += imaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
f += 2;
}
else if (format[f+1] == 'u')
{
// Unsigned long int base 10.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'o')
{
// Unsigned long int base 8.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 8, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'x')
{
// Unsigned long int base 16.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'X')
{
// Unsigned long int base 16.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 1,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'b')
{
// Unsigned long int base 2 (estensione).
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 2, 0,
alignment, filler, remain);
f += 2;
}
else // Specificatore errato;
{
f += 1;
}
}
else if (format[f] == 'j')
{
if (format[f+1] == 'd' || format[f+1] == 'i')
{
// intmax_t base 10.
value_i = va_arg (ap, intmax_t);
if (flag_plus)
{
s += simaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
else
{
s += imaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
f += 2;
}
else if (format[f+1] == 'u')
{
// uintmax_t base 10.
value_ui = va_arg (ap, uintmax_t);
s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'o')
{
// uintmax_t base 8.
value_ui = va_arg (ap, uintmax_t);
s += uimaxtoa_fill (value_ui, &string[s], 8, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'x')
{
// uintmax_t base 16.
value_ui = va_arg (ap, uintmax_t);
s += uimaxtoa_fill (value_ui, &string[s], 16, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'X')
{
// uintmax_t base 16.
value_ui = va_arg (ap, uintmax_t);
s += uimaxtoa_fill (value_ui, &string[s], 16, 1,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'b')
{
// uintmax_t base 2 (estensione).
value_ui = va_arg (ap, uintmax_t);
s += uimaxtoa_fill (value_ui, &string[s], 2, 0,
alignment, filler, remain);
f += 2;
}
else // Specificatore errato;
{
f += 1;
}
}
else if (format[f] == 'z')
{
if (format[f+1] == 'd'
|| format[f+1] == 'i'
|| format[f+1] == 'i')
{
// size_t base 10.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'o')
{
// size_t base 8.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 8, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'x')
{
// size_t base 16.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'X')
{
// size_t base 16.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 1,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'b')
{
// size_t base 2 (estensione).
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 2, 0,
alignment, filler, remain);
f += 2;
}
else // Specificatore errato;
{
f += 1;
}
}
else if (format[f] == 't')
{
if (format[f+1] == 'd' || format[f+1] == 'i')
{
// ptrdiff_t base 10.
value_i = va_arg (ap, long int);
if (flag_plus)
{
s += simaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
else
{
s += imaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
f += 2;
}
else if (format[f+1] == 'u')
{
// ptrdiff_t base 10, senza segno.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'o')
{
// ptrdiff_t base 8, senza segno.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 8, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'x')
{
// ptrdiff_t base 16, senza segno.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 0,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'X')
{
// ptrdiff_t base 16, senza segno.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 1,
alignment, filler, remain);
f += 2;
}
else if (format[f+1] == 'b')
{
// ptrdiff_t base 2, senza segno.
value_ui = va_arg (ap, unsigned long int);
s += uimaxtoa_fill (value_ui, &string[s], 2, 0,
alignment, filler, remain);
f += 2;
}
else // Specificatore errato;
{
f += 1;
}
}
if (format[f] == 'd' || format[f] == 'i')
{
// int base 10.
value_i = va_arg (ap, int);
if (flag_plus)
{
s += simaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
else
{
s += imaxtoa_fill (value_i, &string[s], 10, 0,
alignment, filler, remain);
}
f += 1;
}
else if (format[f] == 'u')
{
// unsigned int base 10.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
alignment, filler, remain);
f += 1;
}
else if (format[f] == 'o')
{
// Unsigned int base 8.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 8, 0,
alignment, filler, remain);
f += 1;
}
else if (format[f] == 'x')
{
// unsigned int base 16.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 0,
alignment, filler, remain);
f += 1;
}
else if (format[f] == 'X')
{
// unsigned int base 16.
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 16, 1,
alignment, filler, remain);
f += 1;
}
else if (format[f] == 'b')
{
// unsigned int base 2 (estensione).
value_ui = va_arg (ap, unsigned int);
s += uimaxtoa_fill (value_ui, &string[s], 2, 0,
alignment, filler, remain);
f += 1;
}
//else if (format[f] == 'c')
// {
// // unsigned char.
// value_ui = va_arg (ap, unsigned int);
// s += uimaxtoa_fill (value_ui, &string[s], 10, 0,
// alignment, filler, remain);
// f += 1;
// }
else if (format[f] == 'c')
{
// unsigned char.
value_ui = va_arg (ap, unsigned int);
string[s] = (char) value_ui;
s += 1;
f += 1;
}
else if (format[f] == 's')
{
// string.
value_cp = va_arg (ap, char *);
filler = ' ';
s += strtostr_fill (value_cp, &string[s], alignment,
filler, remain);
f += 1;
}
else // Specificatore errato;
{
;
}
//
// Fine dello specificatore.
//
width_string[0] = '\0';
precision_string[0] = '\0';
specifier = 0;
specifier_flags = 0;
specifier_width = 0;
specifier_precision = 0;
specifier_type = 0;
flag_plus = 0;
flag_minus = 0;
flag_space = 0;
flag_alternate = 0;
flag_zero = 0;
}
}
string[s] = '\0';
return s;
}
|