summaryrefslogtreecommitdiff
path: root/src/stdio/__printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stdio/__printf.c')
-rw-r--r--src/stdio/__printf.c372
1 files changed, 126 insertions, 246 deletions
diff --git a/src/stdio/__printf.c b/src/stdio/__printf.c
index 807e0948..34904d30 100644
--- a/src/stdio/__printf.c
+++ b/src/stdio/__printf.c
@@ -1,4 +1,5 @@
#include <ctype.h>
+#include <limits.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
@@ -17,40 +18,51 @@
#define NUMBUFLEN 64
-#define LEFT (1 << 0)
-#define SIGN (1 << 1)
-#define SPACE (1 << 2)
-#define ALT (1 << 3)
-#define ZERO (1 << 4)
-#define UPPER (1 << 5)
-#define UNSIGNED (1 << 6)
+static int __output(struct io_options *opt, struct io_conversion *conv, const char *str, size_t len)
+{
+ size_t olen = strlen(str);
+ if (len == 0) {
+ len = olen;
+ }
-#define FLAG (1 << 0)
-#define WIDTH (1 << 1)
-#define PRECISION (1 << 2)
-#define LENGTH (1 << 3)
+ if (conv && olen < conv->width && (conv->flags & F_WIDTH) && !(conv->flags & F_LEFT)) {
+ for (size_t i = 0; i < conv->width - olen; i++) {
+ __output(opt, NULL, " ", 1);
+ }
+ }
+
+ for (size_t i = 0; i < len && str[i] != '\0'; i++) {
+ if (opt->ret == INT_MAX) {
+ UNDEFINED("In call to %s(): Attempting to output more than INT_MAX characters", opt->fnname);
+ }
+ if (opt->stream) {
+ fputc(str[i], opt->stream);
+ } else if (opt->string) {
+ if ((size_t)opt->ret < opt->maxlen) {
+ opt->string[opt->ret] = str[i];
+ }
+ } else {
+ /* file descriptor */
+ }
+ opt->ret++;
+ }
-static int __append(char *s, char *argstring, int nout, size_t n)
-{
- s += nout;
- while (*argstring) {
- if (nout < (int)n) {
- *s++ = *argstring;
+ if (conv && olen < conv->width && (conv->flags & F_WIDTH) && (conv->flags & F_LEFT)) {
+ for (size_t i = 0; i < conv->width - olen; i++) {
+ __output(opt, NULL, " ", 1);
}
- nout++;
- argstring++;
}
- return nout;
+
+ return 0;
}
-static void __itos(char *s, intmax_t n, int flags, int precision, int base)
+static void __utos(char *s, uintmax_t n, enum conversion_flags flags, int precision, int base)
{
- extern int toupper(int);
char digits[] = "0123456789abcdef";
- char sign = n < 0 ? '-' : '+';
+ char sign = '+';
char buf[NUMBUFLEN + 1];
char *out = buf + NUMBUFLEN;
- if (flags & UPPER && base > 10) {
+ if (flags & F_UPPER && base > 10) {
size_t i;
for (i = 0; i < sizeof(digits); i++) {
digits[i] = (char)toupper(digits[i]);
@@ -68,7 +80,13 @@ static void __itos(char *s, intmax_t n, int flags, int precision, int base)
n /= base;
out--;
}
- if (flags & SIGN || sign == '-') {
+ /* TODO: this has a risk of overflowing */
+ while (precision > 0 && (flags & (F_ZERO | F_PRECISION))) {
+ *out = '0';
+ precision--;
+ out--;
+ }
+ if (flags & F_SIGN) {
*out = sign;
out--;
}
@@ -78,193 +96,100 @@ static void __itos(char *s, intmax_t n, int flags, int precision, int base)
}
}
-int (__printf)(struct io_options *opt, const char * format, va_list arg)
+static void __itos(char *s, intmax_t n, enum conversion_flags flags, int precision, int base)
+{
+ uintmax_t absn = (uintmax_t)imaxabs(n);
+ __utos(s, absn, flags | ((n < 0) ? F_SIGN : 0), precision, base);
+
+ if (n < 0) {
+ char *sign = strrchr(s, '+');
+ *sign = '-';
+ }
+}
+
+int __printf(struct io_options *opt, const char * format, va_list arg)
{
- char buf[BUFSIZ];
int nout = 0;
intmax_t argint = 0;
+ uintmax_t arguns = 0;
void *argptr = NULL;
char numbuf[NUMBUFLEN];
size_t i;
- size_t n = 0;
- char *s = NULL;
+ //char *s = NULL;
if (opt->stream) {
- /* file based */
- s = buf;
- n = BUFSIZ;
flockfile(opt->stream);
- } else if (opt->string) {
- /* memory buffer */
- s = opt->string;
- n = opt->maxlen;
- } else {
- /* file descriptor */
- s = buf;
- n = BUFSIZ;
}
for (i = 0; format[i] != 0; i++) {
- /*
- // zero of more flags "-+ #0"
- // optional width "*" or decimal integer
- // optional precision ".*" or ".[decimal]"
- // optional length modifier "hh", "h", "l", "ll", "j",
- // "z", "t", "L"
- // conversion specifier "diouxXfFeEgGaAcspn%"
- */
- unsigned int flags = 0;
- uintmax_t width = 0;
- int step = 0;
- int precision = 0;
+ struct io_conversion conv = {
+ .func = opt->fnname,
+ .dir = IO_OUT,
+ };
int base = 10;
- unsigned int specified = 0;
- enum { def, hh, h, l, ll, j, z, t, L } length = def;
if (format[i] != '%') {
- if (nout < (int)n) {
- s[nout] = format[i];
+ size_t prev = i;
+ while (format[i] != '\0' && format[i] != '%') {
+ i++;
}
- nout++;
+ __output(opt, NULL, format + prev, i - prev);
+ i--;
continue;
}
- while (step == 0) {
- i++;
- switch (format[i]) {
- case '-': flags |= LEFT; specified |= FLAG; break;
- case '+': flags |= SIGN; specified |= FLAG; break;
- case ' ': flags |= SPACE; specified |= FLAG; break;
- case '#': flags |= ALT; specified |= FLAG; break;
- case '0': flags |= ZERO; specified |= FLAG; break;
- default: step = 1; break;
- }
- }
-
- if (format[i] == '*') {
- specified |= WIDTH;
- width = va_arg(arg, int);
- i++;
- } else if (isdigit(format[i])) {
- specified |= WIDTH;
+ i += __conv(format + i, &conv, arg);
- char *end;
- width = strtoumax(format + i, &end, 10);
- i = end - format;
- }
- (void)width; /* TODO!!!! */
+ switch (conv.spec) {
+ case 'o': /* unsigned int */
+ case 'u':
+ case 'x':
+ case 'X':
+ switch (conv.length) {
+ case L_hh: arguns = (unsigned char)va_arg(arg, unsigned int); break;
+ case L_h: arguns = (unsigned short int)va_arg(arg, unsigned int); break;
+ case L_l: arguns = va_arg(arg, unsigned long int); break;
- if (format[i] == '.') {
- specified |= PRECISION;
+ #if defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__
+ case L_ll: arguns = va_arg(arg, unsigned long long int); break;
+ #endif
- i++;
- if (format[i] == '*') {
- /* TODO */
- i++;
- } else if (isdigit(format[i])) {
- char *end;
- precision = (int)strtoumax(format + i, &end, 10);
- i = end - format;
- } else {
- precision = 0;
- }
- }
+ case L_j: arguns = va_arg(arg, uintmax_t); break;
+ case L_z: arguns = va_arg(arg, size_t); break;
+ case L_t: arguns = va_arg(arg, ptrdiff_t); break;
- if (format[i] == 'h') {
- i++;
- if (format[i] == 'h') {
- i++;
- length = hh;
- } else {
- length = h;
+ default: arguns = va_arg(arg, unsigned int); break;
}
- } else if (format[i] == 'l') {
- i++;
- if (format[i] == 'l') {
- i++;
- length = ll;
- } else {
- length = l;
+ if (conv.spec == 'o') {
+ base = 8;
+ } else if (conv.spec == 'x' || conv.spec == 'X') {
+ base = 16;
}
- } else if (format[i] == 'j') {
- i++;
- length = j;
- } else if (format[i] == 'z') {
- i++;
- length = z;
- } else if (format[i] == 't') {
- i++;
- length = t;
- } else if (format[i] == 'L') {
- i++;
- length = L;
- }
-
- if (isupper(format[i])) {
- flags |= UPPER;
- }
-
- if ((flags & ALT) && (!strchr("xXaAeEfFgG", format[i]))) {
- UNDEFINED("In call to %s(): The '#' flag is undefined for %%%c", opt->fnname, format[i]);
- } else if ((flags & ZERO) && (!strchr("diouxXaAeEfFgG", format[i]))) {
- UNDEFINED("In call to %s(): The '0' flag is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == hh) && (!strchr("diouxXn", format[i]))) {
- UNDEFINED("In call to %s(): The length 'hh' is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == h) && (!strchr("diouxXn", format[i]))) {
- UNDEFINED("In call to %s(): The length 'h' is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == l) && (!strchr("diouxXncsaAeEfFgG", format[i]))) {
- UNDEFINED("In call to %s(): The length 'l' is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == ll) && (!strchr("diouxXn", format[i]))) {
- UNDEFINED("In call to %s(): The length 'll' is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == j) && (!strchr("diouxXn", format[i]))) {
- UNDEFINED("In call to %s(): The length 'j' is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == z) && (!strchr("diouxXn", format[i]))) {
- UNDEFINED("In call to %s(): The length 'z' is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == t) && (!strchr("diouxXn", format[i]))) {
- UNDEFINED("In call to %s(): The length 't' is undefined for %%%c", opt->fnname, format[i]);
- } else if ((length == L) && (!strchr("aAeEfFgG", format[i]))) {
- UNDEFINED("In call to %s(): The length 'L' is undefined for %%%c", opt->fnname, format[i]);
- }
-
- switch (format[i]) {
- case 'o': /* unsigned int */
- case 'u':
- case 'x':
- case 'X':
- flags |= UNSIGNED;
- /* FALLTHRU */
+ __utos(numbuf, arguns, conv.flags, conv.precision, base);
+ __output(opt, &conv, numbuf, 0);
+ break;
case 'd': /* int */
case 'i':
- switch (length) {
- case hh: argint = (signed char)va_arg(arg, int); break;
- case h: argint = (short int)va_arg(arg, int); break;
- case l: argint = va_arg(arg, long int); break;
+ switch (conv.length) {
+ case L_hh: argint = (signed char)va_arg(arg, int); break;
+ case L_h: argint = (short int)va_arg(arg, int); break;
+ case L_l: argint = va_arg(arg, long int); break;
#if defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__
- case ll: argint = va_arg(arg, long long int); break;
+ case L_ll: argint = va_arg(arg, long long int); break;
#endif
- case j: argint = va_arg(arg, intmax_t); break;
- case z: argint = va_arg(arg, size_t); break;
- case t: argint = va_arg(arg, ptrdiff_t); break;
- case L: nout = -nout; goto end;
+ case L_j: argint = va_arg(arg, intmax_t); break;
+ case L_z: argint = va_arg(arg, size_t); break;
+ case L_t: argint = va_arg(arg, ptrdiff_t); break;
+
default: argint = va_arg(arg, int); break;
}
- if (format[i] == 'o') {
- base = 8;
- } else if (format[i] == 'x') {
- base = 16;
- } else if (format[i] == 'X') {
- base = 16;
- flags |= UPPER;
- } else {
- base = 10;
- }
- __itos(numbuf, (long int)argint, flags, precision, base);
- nout = __append(s, numbuf, nout, n);
+ __itos(numbuf, argint, conv.flags, conv.precision, 10);
+ __output(opt, &conv, numbuf, 0);
break;
@@ -288,122 +213,83 @@ int (__printf)(struct io_options *opt, const char * format, va_list arg)
break;
case 'c': /* char */
- if (specified & PRECISION) {
- UNDEFINED("In call to %s(): Precision with %%c conversions", opt->fnname);
- }
-
- if (length == def) {
+ if (conv.length == L_default) {
char c = va_arg(arg, int);
- if (nout < (int)n) {
- s[nout] = c;
- }
+ __output(opt, &conv, &c, 1);
nout++;
- } else if (length == l) {
+ } else if (conv.length == L_l) {
#if defined __STDC_VERSION__
wint_t wc = va_arg(arg, wint_t);
char mb[MB_CUR_MAX + 1];
mb[0] = 'W'; mb[1] = 'C'; mb[2] = '\0';
wctomb(mb, wc);
- nout = __append(s, mb, nout, n);
- #else
- nout = __append(s, "NOSUP", nout, n);
+ __output(opt, &conv, mb, 0);
#endif
- } else {
- nout = -nout;
- goto end;
}
break;
case 's': /* string */
- if (length == def) {
+ if (conv.length == L_default) {
char *string = va_arg(arg, char *);
- nout = __append(s, string, nout, n);
- } else if (length == l) {
+ __output(opt, &conv, string, 0);
+ } else if (conv.length == L_l) {
#if defined __STDC_VERSION__
wchar_t *ws = va_arg(arg, wchar_t *);
char *mbs = malloc(wcslen(ws) * MB_CUR_MAX + 1);
wcstombs(mbs, ws, wcslen(ws) * MB_CUR_MAX + 1);
- nout = __append(s, mbs, nout, n);
+ __output(opt, &conv, mbs, 0);
free(mbs);
- #else
- nout = __append(s, "NOSUP", nout, n);
#endif
- } else {
- nout = -nout;
- goto end;
}
break;
case 'p': /* pointer */
- if (specified & PRECISION) {
- UNDEFINED("In call to %s(): Precision with %%p conversion", opt->fnname);
- }
argptr = va_arg(arg, void *);
- char *s_to_track = s + nout;
- nout = __append(s, "0x", nout, n);
- __itos(numbuf, (intptr_t)argptr, ZERO, sizeof(argptr) * 2, 16);
- nout = __append(s, numbuf, nout, n);
- ADD_PREV_STRING(s_to_track, __stdio_h.formatted_pointers, __stdio_h.nformatted_pointers);
+ __itos(numbuf, (intptr_t)argptr, F_ALT | F_ZERO, sizeof(argptr) * 2, 16);
+ __output(opt, &conv, numbuf, 0);
+ ADD_PREV_STRING(numbuf, __stdio_h.formatted_pointers, __stdio_h.nformatted_pointers);
break;
case 'n': /* write-back */
- if (specified & FLAG) {
- UNDEFINED("In call to %s(): Flags with %%n conversion", opt->fnname);
- } else if (specified & 0) {
- /* TODO: output suppression (might only be for input) */
- } else if (specified & WIDTH) {
- UNDEFINED("In call to %s(): Width with %%n conversion", opt->fnname);
- } else if (specified & PRECISION) {
- UNDEFINED("In call to %s(): Precision with %%n conversion", opt->fnname);
- }
-
- switch (length) {
- case def:
- int *ip = va_arg(arg, int *);
- *ip = nout;
- break;
- case hh:
+ switch (conv.length) {
+ case L_hh:
signed char *sc = va_arg(arg, signed char *);
*sc = nout;
break;
- case h:
+ case L_h:
short int *si = va_arg(arg, short int *);
*si = nout;
break;
- case l:
+ case L_l:
long int *li = va_arg(arg, long int *);
*li = nout;
break;
- case ll:
+ case L_ll:
long long int *lli = va_arg(arg, long long int *);
*lli = nout;
break;
- case j:
+ case L_j:
intmax_t *im = va_arg(arg, intmax_t *);
*im = nout;
break;
- case z:
+ case L_z:
size_t *sz = va_arg(arg, size_t *);
*sz = nout;
break;
- case t:
+ case L_t:
ptrdiff_t *pd = va_arg(arg, ptrdiff_t *);
*pd = nout;
break;
- case L:
- UNDEFINED("In call to %s(): Invalid length 'L' for %%n conversion", opt->fnname);
+ default:
+ int *ip = va_arg(arg, int *);
+ *ip = nout;
+ break;
}
break;
case '%': /* literal '%' */
- if (specified != 0) {
- UNDEFINED("In call to %s(): \"%%%%\" conversion is not literally \"%%%%\"", opt->fnname);
- }
- if (nout < (int)n) {
- s[nout] = '%';
- }
- nout++;
+ __output(opt, &conv, "%", 1);
break;
default: /* undefined */
@@ -411,17 +297,11 @@ int (__printf)(struct io_options *opt, const char * format, va_list arg)
}
}
- end:
if (opt->stream) {
- fwrite(buf, 1, nout % BUFSIZ, opt->stream);
funlockfile(opt->stream);
- #ifdef _POSIX_SOURCE
- } else if (opt->fd != -1) {
- write(opt->fd, buf, nout % BUFSIZ);
- #endif
}
- return nout;
+ return opt->ret;
}
/*