whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit b02efdec25d157be4ba92516b3bff08f944d0563
parent f173072e30e3e5cc8d0e88d3c27dea2677073d10
Author: Louis Solofrizzo <lsolofrizzo@online.net>
Date:   Wed, 24 Apr 2019 12:04:40 +0200

ukl: add printf and family

This patch introduces printf in the ukl library. The following function
have been added:
- puts
- vfprintf
- printf
- vfprintf
- vprintf

All snprintf functions and like are already present in the kernel.
There's also the beginning of stdlib, with basic support for wchar_t,
and limits.h.

It is worth noting that printf does _not_ handle float and double, since
there are no FPU in the kernel.

Signed-off-by: Louis Solofrizzo <lsolofrizzo@online.net>

Diffstat:
Minclude/linux/limits.h | 4++++
Minclude/linux/types.h | 5+++++
Ainclude/ukl/bits/float.h | 33+++++++++++++++++++++++++++++++++
Ainclude/ukl/limits.h | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/ukl/stdio.h | 8++++++++
Ainclude/ukl/stdlib.h | 12++++++++++++
Ainclude/ukl/wchar.h | 6++++++
Mukl/Makefile | 2+-
Mukl/stdio/Makefile | 2+-
Aukl/stdio/printf.c | 31+++++++++++++++++++++++++++++++
Aukl/stdio/vfprintf.c | 587+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aukl/stdlib/Makefile | 1+
Aukl/stdlib/wctomb.c | 43+++++++++++++++++++++++++++++++++++++++++++
13 files changed, 858 insertions(+), 2 deletions(-)

diff --git a/include/linux/limits.h b/include/linux/limits.h @@ -32,5 +32,9 @@ #define U64_MAX ((u64)~0ULL) #define S64_MAX ((s64)(U64_MAX >> 1)) #define S64_MIN ((s64)(-S64_MAX - 1)) +#ifdef CONFIG_UKL_LINUX +#define UINTMAX_MAX U64_MAX +#define INTMAX_MAX S64_MAX +#endif /* CONFIG_UKL_LINUX */ #endif /* _LINUX_LIMITS_H */ diff --git a/include/linux/types.h b/include/linux/types.h @@ -112,6 +112,11 @@ typedef u32 uint32_t; typedef u64 uint64_t; typedef u64 u_int64_t; typedef s64 int64_t; +#ifdef CONFIG_UKL_LINUX +typedef u64 uintmax_t; +typedef s64 intmax_t; +typedef struct __mbstate_t { unsigned __opaque1, __opaque2; } mbstate_t; +#endif /* CONFIG_UKL_LINUX */ #endif /* this is a special 64bit data type that is 8-byte aligned */ diff --git a/include/ukl/bits/float.h b/include/ukl/bits/float.h @@ -0,0 +1,33 @@ +#ifndef FLOAT_H +#define FLOAT_H + +#ifdef __FLT_EVAL_METHOD__ +#define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ +#else +#define FLT_EVAL_METHOD 0 +#endif + +#define LDBL_TRUE_MIN 3.6451995318824746025e-4951L +#define LDBL_MIN 3.3621031431120935063e-4932L +#define LDBL_MAX 1.1897314953572317650e+4932L +#define LDBL_EPSILON 1.0842021724855044340e-19L + +#define LDBL_MANT_DIG 64 +#define LDBL_MIN_EXP (-16381) +#define LDBL_MAX_EXP 16384 + +#define LDBL_DIG 18 +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_10_EXP 4932 + +#define DECIMAL_DIG 21 + +#ifdef __BIG_ENDIAN__ +#define __BYTE_ORDER __BIG_ENDIAN +#define __LITTLE_ENDIAN 0 +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#define __BIG_ENDIAN 0 +#endif + +#endif /* FLOAT_H */ diff --git a/include/ukl/limits.h b/include/ukl/limits.h @@ -0,0 +1,126 @@ +#ifndef LIMITS_H +#define LIMITS_H + +/* Most limits are system-specific */ + +#include <linux/limits.h> + +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ + || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) + +#define PIPE_BUF 4096 +#define FILESIZEBITS 64 +#define NAME_MAX 255 +#define PATH_MAX 4096 +#define ARG_MAX 131072 +#define IOV_MAX 1024 +#define SYMLOOP_MAX 40 +#define WORD_BIT 32 +#define SSIZE_MAX LONG_MAX +#define TZNAME_MAX 6 +#define TTY_NAME_MAX 32 +#define HOST_NAME_MAX 255 + +/* Implementation choices... */ + +#define PTHREAD_KEYS_MAX 128 +#define PTHREAD_STACK_MIN 2048 +#define PTHREAD_DESTRUCTOR_ITERATIONS 4 +#define SEM_VALUE_MAX 0x7fffffff +#define SEM_NSEMS_MAX 256 +#define DELAYTIMER_MAX 0x7fffffff +#define MQ_PRIO_MAX 32768 +#define LOGIN_NAME_MAX 256 + +/* Arbitrary numbers... */ + +#define BC_BASE_MAX 99 +#define BC_DIM_MAX 2048 +#define BC_SCALE_MAX 99 +#define BC_STRING_MAX 1000 +#define CHARCLASS_NAME_MAX 14 +#define COLL_WEIGHTS_MAX 2 +#define EXPR_NEST_MAX 32 +#define LINE_MAX 4096 +#define RE_DUP_MAX 255 + +#define NL_ARGMAX 9 +#define NL_MSGMAX 32767 +#define NL_SETMAX 255 +#define NL_TEXTMAX 2048 + +#endif + +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) || defined(_XOPEN_SOURCE) + +#ifdef PAGESIZE +#define PAGE_SIZE PAGESIZE +#endif +#define NZERO 20 +#define NL_LANGMAX 32 + +#endif + +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) \ + || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE+0 < 700) + +#define NL_NMAX 16 + +#endif + +/* POSIX/SUS requirements follow. These numbers come directly + * from SUS and have nothing to do with the host system. */ + +#define _POSIX_AIO_LISTIO_MAX 2 +#define _POSIX_AIO_MAX 1 +#define _POSIX_ARG_MAX 4096 +#define _POSIX_CHILD_MAX 25 +#define _POSIX_CLOCKRES_MIN 20000000 +#define _POSIX_DELAYTIMER_MAX 32 +#define _POSIX_HOST_NAME_MAX 255 +#define _POSIX_LINK_MAX 8 +#define _POSIX_LOGIN_NAME_MAX 9 +#define _POSIX_MAX_CANON 255 +#define _POSIX_MAX_INPUT 255 +#define _POSIX_MQ_OPEN_MAX 8 +#define _POSIX_MQ_PRIO_MAX 32 +#define _POSIX_NAME_MAX 14 +#define _POSIX_NGROUPS_MAX 8 +#define _POSIX_OPEN_MAX 20 +#define _POSIX_PATH_MAX 256 +#define _POSIX_PIPE_BUF 512 +#define _POSIX_RE_DUP_MAX 255 +#define _POSIX_RTSIG_MAX 8 +#define _POSIX_SEM_NSEMS_MAX 256 +#define _POSIX_SEM_VALUE_MAX 32767 +#define _POSIX_SIGQUEUE_MAX 32 +#define _POSIX_SSIZE_MAX 32767 +#define _POSIX_STREAM_MAX 8 +#define _POSIX_SS_REPL_MAX 4 +#define _POSIX_SYMLINK_MAX 255 +#define _POSIX_SYMLOOP_MAX 8 +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 +#define _POSIX_THREAD_KEYS_MAX 128 +#define _POSIX_THREAD_THREADS_MAX 64 +#define _POSIX_TIMER_MAX 32 +#define _POSIX_TRACE_EVENT_NAME_MAX 30 +#define _POSIX_TRACE_NAME_MAX 8 +#define _POSIX_TRACE_SYS_MAX 8 +#define _POSIX_TRACE_USER_EVENT_MAX 32 +#define _POSIX_TTY_NAME_MAX 9 +#define _POSIX_TZNAME_MAX 6 +#define _POSIX2_BC_BASE_MAX 99 +#define _POSIX2_BC_DIM_MAX 2048 +#define _POSIX2_BC_SCALE_MAX 99 +#define _POSIX2_BC_STRING_MAX 1000 +#define _POSIX2_CHARCLASS_NAME_MAX 14 +#define _POSIX2_COLL_WEIGHTS_MAX 2 +#define _POSIX2_EXPR_NEST_MAX 32 +#define _POSIX2_LINE_MAX 2048 +#define _POSIX2_RE_DUP_MAX 255 + +#define _XOPEN_IOV_MAX 16 +#define _XOPEN_NAME_MAX 255 +#define _XOPEN_PATH_MAX 1024 + +#endif /* LIMITS_H */ diff --git a/include/ukl/stdio.h b/include/ukl/stdio.h @@ -24,11 +24,19 @@ FILE *fopen(const char *, const char *); size_t fwrite(const void *, size_t, size_t, FILE *); size_t fread(void *, size_t, size_t, FILE *); int fputs(const char *, FILE *); +int puts(const char *); int fclose(FILE *); int fflush(FILE *); int fseek(FILE *, long, int); int putc(int c, FILE *f); int putc_unlocked(int c, FILE *f); int putchar(int c); +int vfprintf(FILE *f, const char *fmt, va_list ap); +int printf(const char *fmt, ...); +int fprintf(FILE *f, const char *fmt, ...); +int vprintf(const char *fmt, va_list ap); +int vsnprintf(char *s, size_t n, const char *fmt, va_list ap); +int sprintf(char *s, const char *fmt, ...); +int snprintf(char *s, size_t n, const char *fmt, ...); #endif /* STDIO_H */ diff --git a/include/ukl/stdlib.h b/include/ukl/stdlib.h @@ -0,0 +1,12 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#include <linux/types.h> +#include <linux/nls.h> + +#define MB_CUR_MAX 4 + +size_t wcrtomb(char *s, wchar_t wc, mbstate_t *st); +int wctomb(char *s, wchar_t wc); + +#endif /* STDLIB_H */ diff --git a/include/ukl/wchar.h b/include/ukl/wchar.h @@ -0,0 +1,6 @@ +#ifndef WCHAR_H +#define WCHAR_H + +#define IS_CODEUNIT(c) ((unsigned)(c)-0xdf80 < 0x80) + +#endif /* WCHAR_H */ diff --git a/ukl/Makefile b/ukl/Makefile @@ -1,2 +1,2 @@ ukl-real-obj-m := $(addprefix ../../../../../../../..$(UKL)/,$(ukl-obj-m)) -obj-y := unistd/ fcntl/ stdio/ sys/ $(ukl-real-obj-m) +obj-y := unistd/ fcntl/ stdio/ sys/ stdlib/ $(ukl-real-obj-m) diff --git a/ukl/stdio/Makefile b/ukl/stdio/Makefile @@ -1 +1 @@ -obj-y := fopen.o __internals.o stdin.o stdout.o stderr.o fwrite.o fputs.o fread.o fclose.o fflush.o fseek.o putc.o +obj-y := fopen.o __internals.o stdin.o stdout.o stderr.o fwrite.o fputs.o fread.o fclose.o fflush.o fseek.o putc.o vfprintf.o printf.o diff --git a/ukl/stdio/printf.c b/ukl/stdio/printf.c @@ -0,0 +1,31 @@ +#include <stdio.h> +#include "internals.h" + +#include <linux/string.h> + +int printf(const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = vfprintf(stdout, fmt, ap); + va_end(ap); + return ret; +} + +int fprintf(FILE *f, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = vfprintf(f, fmt, ap); + va_end(ap); + return ret; +} + +int vprintf(const char *fmt, va_list ap) +{ + return vfprintf(stdout, fmt, ap); +} diff --git a/ukl/stdio/vfprintf.c b/ukl/stdio/vfprintf.c @@ -0,0 +1,587 @@ +#define _POSIX_SOURCE +#include <stdio.h> +#include "internals.h" + +#include <bits/float.h> +#include <limits.h> +#include <stdlib.h> + +#include <linux/string.h> +#include <linux/ctype.h> + +/* Some useful macros */ + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +/* Convenient bit representation for modifier flags, which all fall + * within 31 codepoints of the space character. */ + +#define ALT_FORM (1U << ('#' - ' ')) +#define ZERO_PAD (1U << ('0' - ' ')) +#define LEFT_ADJ (1U << ('-' - ' ')) +#define PAD_POS (1U << (' ' - ' ')) +#define MARK_POS (1U << ('+' - ' ')) +#define GROUPED (1U << ('\'' - ' ')) + +#define FLAGMASK (ALT_FORM | ZERO_PAD | LEFT_ADJ | PAD_POS | MARK_POS | GROUPED) + +/* State machine to accept length modifiers + conversion specifiers. + * Result is 0 on failure, or an argument type to pop on success. */ + +enum { BARE, + LPRE, + LLPRE, + HPRE, + HHPRE, + BIGLPRE, + ZTPRE, + JPRE, + STOP, + PTR, + INT, + UINT, + ULLONG, + LONG, + ULONG, + SHORT, + USHORT, + CHAR, + UCHAR, + LLONG, + SIZET, + IMAX, + UMAX, + PDIFF, + UIPTR, + NOARG, + MAXSTATE }; + +#define S(x) [(x) - 'A'] + +static const unsigned char states[]['z' - 'A' + 1] = { + { + /* 0: bare types */ + S('d') = INT, S('i') = INT, S('o') = UINT, S('u') = UINT, + S('x') = UINT, S('X') = UINT, S('c') = CHAR, S('C') = INT, + S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR, + S('m') = NOARG, S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE, + S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE, + }, + { + /* 1: l-prefixed */ + S('d') = LONG, + S('i') = LONG, + S('o') = ULONG, + S('u') = ULONG, + S('x') = ULONG, + S('X') = ULONG, + S('c') = INT, + S('s') = PTR, + S('n') = PTR, + S('l') = LLPRE, + }, + { + /* 2: ll-prefixed */ + S('d') = LLONG, + S('i') = LLONG, + S('o') = ULLONG, + S('u') = ULLONG, + S('x') = ULLONG, + S('X') = ULLONG, + S('n') = PTR, + }, + { + /* 3: h-prefixed */ + S('d') = SHORT, + S('i') = SHORT, + S('o') = USHORT, + S('u') = USHORT, + S('x') = USHORT, + S('X') = USHORT, + S('n') = PTR, + S('h') = HHPRE, + }, + { + /* 4: hh-prefixed */ + S('d') = CHAR, + S('i') = CHAR, + S('o') = UCHAR, + S('u') = UCHAR, + S('x') = UCHAR, + S('X') = UCHAR, + S('n') = PTR, + }, + { + /* 5: L-prefixed */ + S('n') = PTR, + }, + { + /* 6: z- or t-prefixed (assumed to be same size) */ + S('d') = PDIFF, + S('i') = PDIFF, + S('o') = SIZET, + S('u') = SIZET, + S('x') = SIZET, + S('X') = SIZET, + S('n') = PTR, + }, + { + /* 7: j-prefixed */ + S('d') = IMAX, + S('i') = IMAX, + S('o') = UMAX, + S('u') = UMAX, + S('x') = UMAX, + S('X') = UMAX, + S('n') = PTR, + } +}; + +#define OOB(x) ((unsigned)(x) - 'A' > 'z' - 'A') + +union arg { + uintmax_t i; + void *p; +}; + +static void pop_arg(union arg *arg, int type, va_list *ap) +{ + switch (type) { + case PTR: + arg->p = va_arg(*ap, void *); + break; + case INT: + arg->i = va_arg(*ap, int); + break; + case UINT: + arg->i = va_arg(*ap, unsigned int); + break; + case LONG: + arg->i = va_arg(*ap, long); + break; + case ULONG: + arg->i = va_arg(*ap, unsigned long); + break; + case ULLONG: + arg->i = va_arg(*ap, unsigned long long); + break; + case SHORT: + arg->i = (short)va_arg(*ap, int); + break; + case USHORT: + arg->i = (unsigned short)va_arg(*ap, int); + break; + case CHAR: + arg->i = (signed char)va_arg(*ap, int); + break; + case UCHAR: + arg->i = (unsigned char)va_arg(*ap, int); + break; + case LLONG: + arg->i = va_arg(*ap, long long); + break; + case SIZET: + arg->i = va_arg(*ap, size_t); + break; + case IMAX: + arg->i = va_arg(*ap, intmax_t); + break; + case UMAX: + arg->i = va_arg(*ap, uintmax_t); + break; + case PDIFF: + arg->i = va_arg(*ap, ptrdiff_t); + break; + case UIPTR: + arg->i = (uintptr_t)va_arg(*ap, void *); + break; + } +} + +static void out(FILE *f, const char *s, size_t l) +{ + if (!(f->flags & F_ERR)) + __fwritex((void *)s, l, f); +} + +static void pad(FILE *f, char c, int w, int l, int fl) +{ + char pad[256]; + if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) + return; + l = w - l; + memset(pad, c, l > sizeof(pad) ? sizeof(pad) : l); + for (; l >= sizeof(pad); l -= sizeof(pad)) + out(f, pad, sizeof(pad)); + out(f, pad, l); +} + +static const char xdigits[16] = { "0123456789ABCDEF" }; + +static char *fmt_x(uintmax_t x, char *s, int lower) +{ + for (; x; x >>= 4) + *--s = xdigits[(x & 15)] | lower; + return s; +} + +static char *fmt_o(uintmax_t x, char *s) +{ + for (; x; x >>= 3) + *--s = '0' + (x & 7); + return s; +} + +static char *fmt_u(uintmax_t x, char *s) +{ + unsigned long y; + for (; x > ULONG_MAX; x /= 10) + *--s = '0' + x % 10; + for (y = x; y; y /= 10) + *--s = '0' + y % 10; + return s; +} + +static int getint(char **s) +{ + int i; + for (i = 0; isdigit(**s); (*s)++) { + if (i > INT_MAX / 10U || **s - '0' > INT_MAX - 10 * i) + i = -1; + else + i = 10 * i + (**s - '0'); + } + return i; +} + +static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, + int *nl_type) +{ + char *a, *z, *s = (char *)fmt; + unsigned l10n = 0, fl; + int w, p, xp; + union arg arg; + int argpos; + unsigned st, ps; + int cnt = 0, l = 0; + size_t i; + char buf[sizeof(uintmax_t) * 3 + 3 + LDBL_MANT_DIG / 4]; + const char *prefix; + int t, pl; + wchar_t wc[2], *ws; + char mb[4]; + + for (;;) { + /* This error is only specified for snprintf, but since it's + * unspecified for other forms, do the same. Stop immediately + * on overflow; otherwise %n could produce wrong results. */ + if (l > INT_MAX - cnt) + goto overflow; + + /* Update output count, end loop when fmt is exhausted */ + cnt += l; + if (!*s) + break; + + /* Handle literal text and %% format specifiers */ + for (a = s; *s && *s != '%'; s++) + ; + for (z = s; s[0] == '%' && s[1] == '%'; z++, s += 2) + ; + if (z - a > INT_MAX - cnt) + goto overflow; + l = z - a; + if (f) + out(f, a, l); + if (l) + continue; + + if (isdigit(s[1]) && s[2] == '$') { + l10n = 1; + argpos = s[1] - '0'; + s += 3; + } else { + argpos = -1; + s++; + } + + /* Read modifier flags */ + for (fl = 0; + (unsigned)*s - ' ' < 32 && (FLAGMASK & (1U << (*s - ' '))); + s++) + fl |= 1U << (*s - ' '); + + /* Read field width */ + if (*s == '*') { + if (isdigit(s[1]) && s[2] == '$') { + l10n = 1; + nl_type[s[1] - '0'] = INT; + w = nl_arg[s[1] - '0'].i; + s += 3; + } else if (!l10n) { + w = f ? va_arg(*ap, int) : 0; + s++; + } else + goto inval; + if (w < 0) + fl |= LEFT_ADJ, w = -w; + } else if ((w = getint(&s)) < 0) + goto overflow; + + /* Read precision */ + if (*s == '.' && s[1] == '*') { + if (isdigit(s[2]) && s[3] == '$') { + nl_type[s[2] - '0'] = INT; + p = nl_arg[s[2] - '0'].i; + s += 4; + } else if (!l10n) { + p = f ? va_arg(*ap, int) : 0; + s += 2; + } else + goto inval; + xp = (p >= 0); + } else if (*s == '.') { + s++; + p = getint(&s); + xp = 1; + } else { + p = -1; + xp = 0; + } + + /* Format specifier state machine */ + st = 0; + do { + if (OOB(*s)) + goto inval; + ps = st; + st = states[st] S(*s++); + } while (st - 1 < STOP); + if (!st) + goto inval; + + /* Check validity of argument type (nl/normal) */ + if (st == NOARG) { + if (argpos >= 0) + goto inval; + } else { + if (argpos >= 0) + nl_type[argpos] = st, arg = nl_arg[argpos]; + else if (f) + pop_arg(&arg, st, ap); + else + return 0; + } + + if (!f) + continue; + + z = buf + sizeof(buf); + prefix = "-+ 0X0x"; + pl = 0; + t = s[-1]; + + /* Transform ls,lc -> S,C */ + if (ps && (t & 15) == 3) + t &= ~32; + + /* - and 0 flags are mutually exclusive */ + if (fl & LEFT_ADJ) + fl &= ~ZERO_PAD; + + switch (t) { + case 'n': + switch (ps) { + case BARE: + *(int *)arg.p = cnt; + break; + case LPRE: + *(long *)arg.p = cnt; + break; + case LLPRE: + *(long long *)arg.p = cnt; + break; + case HPRE: + *(unsigned short *)arg.p = cnt; + break; + case HHPRE: + *(unsigned char *)arg.p = cnt; + break; + case ZTPRE: + *(size_t *)arg.p = cnt; + break; + case JPRE: + *(uintmax_t *)arg.p = cnt; + break; + } + continue; + case 'p': + p = MAX(p, 2 * sizeof(void *)); + t = 'x'; + fl |= ALT_FORM; + case 'x': + case 'X': + a = fmt_x(arg.i, z, t & 32); + if (arg.i && (fl & ALT_FORM)) + prefix += (t >> 4), pl = 2; + if (0) { + case 'o': + a = fmt_o(arg.i, z); + if ((fl & ALT_FORM) && p < z - a + 1) + p = z - a + 1; + } + if (0) { + case 'd': + case 'i': + pl = 1; + if (arg.i > INTMAX_MAX) { + arg.i = -arg.i; + } else if (fl & MARK_POS) { + prefix++; + } else if (fl & PAD_POS) { + prefix += 2; + } else + pl = 0; + case 'u': + a = fmt_u(arg.i, z); + } + if (xp && p < 0) + goto overflow; + if (xp) + fl &= ~ZERO_PAD; + if (!arg.i && !p) { + a = z; + break; + } + p = MAX(p, z - a + !arg.i); + break; + case 'c': + *(a = z - (p = 1)) = arg.i; + fl &= ~ZERO_PAD; + break; + case 'm': + if (1) + /*a = strerror(errno);*/ + ; + else + case 's': + a = arg.p ? arg.p : "(null)"; + z = a + strnlen(a, p < 0 ? INT_MAX : p); + if (p < 0 && *z) + goto overflow; + p = z - a; + fl &= ~ZERO_PAD; + break; + case 'C': + wc[0] = arg.i; + wc[1] = 0; + arg.p = wc; + p = -1; + case 'S': + ws = arg.p; + for (i = l = 0; + i < p && *ws && (l = wctomb(mb, *ws++)) >= 0 && + l <= p - i; + i += l) + ; + if (l < 0) + return -1; + if (i > INT_MAX) + goto overflow; + p = i; + pad(f, ' ', w, p, fl); + ws = arg.p; + for (i = 0; i < 0U + p && *ws && + i + (l = wctomb(mb, *ws++)) <= p; + i += l) + out(f, mb, l); + pad(f, ' ', w, p, fl ^ LEFT_ADJ); + l = w > p ? w : p; + continue; + } + + if (p < z - a) + p = z - a; + if (p > INT_MAX - pl) + goto overflow; + if (w < pl + p) + w = pl + p; + if (w > INT_MAX - cnt) + goto overflow; + + pad(f, ' ', w, pl + p, fl); + out(f, prefix, pl); + pad(f, '0', w, pl + p, fl ^ ZERO_PAD); + pad(f, '0', p, z - a, 0); + out(f, a, z - a); + pad(f, ' ', w, pl + p, fl ^ LEFT_ADJ); + + l = w; + } + + if (f) + return cnt; + if (!l10n) + return 0; + + for (i = 1; i <= NL_ARGMAX && nl_type[i]; i++) + pop_arg(nl_arg + i, nl_type[i], ap); + for (; i <= NL_ARGMAX && !nl_type[i]; i++) + ; + if (i <= NL_ARGMAX) + goto inval; + return 1; + +inval: + /*errno = EINVAL;*/ + return -1; +overflow: + /*rrno = EOVERFLOW;*/ + return -1; +} + +int vfprintf(FILE *f, const char *fmt, va_list ap) +{ + va_list ap2; + int nl_type[NL_ARGMAX + 1] = { 0 }; + union arg nl_arg[NL_ARGMAX + 1]; + unsigned char internal_buf[80], *saved_buf = 0; + int olderr; + int ret; + + /* the copy allows passing va_list* even if va_list is an array */ + va_copy(ap2, ap); + if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) { + va_end(ap2); + return -1; + } + + olderr = f->flags & F_ERR; + if (f->mode < 1) + f->flags &= ~F_ERR; + if (!f->buf_size) { + saved_buf = f->buf; + f->buf = internal_buf; + f->buf_size = sizeof(internal_buf); + f->wpos = f->wbase = f->wend = 0; + } + if (!f->wend && __towrite(f)) + ret = -1; + else + ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); + if (saved_buf) { + f->write(f, 0, 0); + if (!f->wpos) + ret = -1; + f->buf = saved_buf; + f->buf_size = 0; + f->wpos = f->wbase = f->wend = 0; + } + if (f->flags & F_ERR) + ret = -1; + f->flags |= olderr; + + va_end(ap2); + return ret; +} diff --git a/ukl/stdlib/Makefile b/ukl/stdlib/Makefile @@ -0,0 +1 @@ +obj-y := wctomb.o diff --git a/ukl/stdlib/wctomb.c b/ukl/stdlib/wctomb.c @@ -0,0 +1,43 @@ +#include <stdlib.h> +#include <wchar.h> + +int wctomb(char *s, wchar_t wc) +{ + if (!s) + return 0; + return wcrtomb(s, wc, 0); +} + +size_t wcrtomb(char *s, wchar_t wc, mbstate_t *st) +{ + if (!s) + return 1; + if ((unsigned)wc < 0x80) { + *s = wc; + return 1; + } else if (MB_CUR_MAX == 1) { + if (!IS_CODEUNIT(wc)) { + /*errno = EILSEQ;*/ + return -1; + } + *s = wc; + return 1; + } else if ((unsigned)wc < 0x800) { + *s++ = 0xc0 | (wc >> 6); + *s = 0x80 | (wc & 0x3f); + return 2; + } else if ((unsigned)wc < 0xd800 || (unsigned)wc - 0xe000 < 0x2000) { + *s++ = 0xe0 | (wc >> 12); + *s++ = 0x80 | ((wc >> 6) & 0x3f); + *s = 0x80 | (wc & 0x3f); + return 3; + } else if ((unsigned)wc - 0x10000 < 0x100000) { + *s++ = 0xf0 | (wc >> 18); + *s++ = 0x80 | ((wc >> 12) & 0x3f); + *s++ = 0x80 | ((wc >> 6) & 0x3f); + *s = 0x80 | (wc & 0x3f); + return 4; + } + /*errno = EILSEQ;*/ + return -1; +}