/* SPDX-License-Identifier: LGPL-2.1 OR MIT *//* * minimal stdio function definitions for NOLIBC * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu> */ /* make sure to include all global symbols */#include "nolibc.h" #ifndef _NOLIBC_STDIO_H#define _NOLIBC_STDIO_H #include "std.h"#include "arch.h"#include "errno.h"#include "fcntl.h"#include "types.h"#include "sys.h"#include "stdarg.h"#include "stdlib.h"#include "string.h"#include "compiler.h" static const char *strerror(int errnum); #ifndef EOF#define EOF (-1)#endif /* Buffering mode used by setvbuf. */#define _IOFBF 0 /* Fully buffered. */#define _IOLBF 1 /* Line buffered. */#define _IONBF 2 /* No buffering. */ /* just define FILE as a non-empty type. The value of the pointer gives * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE * are immediately identified as abnormal entries (i.e. possible copies * of valid pointers to something else). */typedef struct FILE { char dummy[1];} FILE; static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO; /* provides a FILE* equivalent of fd. The mode is ignored. */static __attribute__((unused))FILE *fdopen(int fd, const char *mode __attribute__((unused))){ if (fd < 0) { SET_ERRNO(EBADF); return NULL; } return (FILE*)(intptr_t)~fd;} static __attribute__((unused))FILE *fopen(const char *pathname, const char *mode){ int flags, fd; switch (*mode) { case 'r': flags = O_RDONLY; break; case 'w': flags = O_WRONLY | O_CREAT | O_TRUNC; break; case 'a': flags = O_WRONLY | O_CREAT | O_APPEND; break; default: SET_ERRNO(EINVAL); return NULL; } if (mode[1] == '+') flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; fd = open(pathname, flags, 0666); return fdopen(fd, mode);} /* provides the fd of stream. */static __attribute__((unused))int fileno(FILE *stream){ intptr_t i = (intptr_t)stream; if (i >= 0) { SET_ERRNO(EBADF); return -1; } return ~i;} /* flush a stream. */static __attribute__((unused))int fflush(FILE *stream){ intptr_t i = (intptr_t)stream; /* NULL is valid here. */ if (i > 0) { SET_ERRNO(EBADF); return -1; } /* Don't do anything, nolibc does not support buffering. */ return 0;} /* flush a stream. */static __attribute__((unused))int fclose(FILE *stream){ intptr_t i = (intptr_t)stream; if (i >= 0) { SET_ERRNO(EBADF); return -1; } if (close(~i)) return EOF; return 0;} /* getc(), fgetc(), getchar() */ #define getc(stream) fgetc(stream) static __attribute__((unused))int fgetc(FILE* stream){ unsigned char ch; if (read(fileno(stream), &ch, 1) <= 0) return EOF; return ch;} static __attribute__((unused))int getchar(void){ return fgetc(stdin);} /* putc(), fputc(), putchar() */ #define putc(c, stream) fputc(c, stream) static __attribute__((unused))int fputc(int c, FILE* stream){ unsigned char ch = c; if (write(fileno(stream), &ch, 1) <= 0) return EOF; return ch;} static __attribute__((unused))int putchar(int c){ return fputc(c, stdout);} /* fwrite(), fread(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */ /* internal fwrite()-like function which only takes a size and returns 0 on * success or EOF on error. It automatically retries on short writes. */static __attribute__((unused))int _fwrite(const void *buf, size_t size, FILE *stream){ ssize_t ret; int fd = fileno(stream); while (size) { ret = write(fd, buf, size); if (ret <= 0) return EOF; size -= ret; buf += ret; } return 0;} static __attribute__((unused))size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream){ size_t written; for (written = 0; written < nmemb; written++) { if (_fwrite(s, size, stream) != 0) break; s += size; } return written;} /* internal fread()-like function which only takes a size and returns 0 on * success or EOF on error. It automatically retries on short reads. */static __attribute__((unused))int _fread(void *buf, size_t size, FILE *stream){ int fd = fileno(stream); ssize_t ret; while (size) { ret = read(fd, buf, size); if (ret <= 0) return EOF; size -= ret; buf += ret; } return 0;} static __attribute__((unused))size_t fread(void *s, size_t size, size_t nmemb, FILE *stream){ size_t nread; for (nread = 0; nread < nmemb; nread++) { if (_fread(s, size, stream) != 0) break; s += size; } return nread;} static __attribute__((unused))int fputs(const char *s, FILE *stream){ return _fwrite(s, strlen(s), stream);} static __attribute__((unused))int puts(const char *s){ if (fputs(s, stdout) == EOF) return EOF; return putchar('\n');} /* fgets() */static __attribute__((unused))char *fgets(char *s, int size, FILE *stream){ int ofs; int c; for (ofs = 0; ofs + 1 < size;) { c = fgetc(stream); if (c == EOF) break; s[ofs++] = c; if (c == '\n') break; } if (ofs < size) s[ofs] = 0; return ofs ? s : NULL;} /* fseek */static __attribute__((unused))int fseek(FILE *stream, long offset, int whence){ int fd = fileno(stream); off_t ret; ret = lseek(fd, offset, whence); /* lseek() and fseek() differ in that lseek returns the new * position or -1, fseek() returns either 0 or -1. */ if (ret >= 0) return 0; return -1;} /* printf(). Supports most of the normal integer and string formats. * - %[#0-+ ][width|*[.precision|*}][{l,t,z,ll,L,j,q}]{c,d,i,u,o,x,X,p,s,m,%} * - %% generates a single % * - %m outputs strerror(errno). * - %X outputs a..f the same as %x. * - No support for floating point or wide characters. * - Invalid formats are copied to the output buffer. * * Called by vfprintf() and snprintf() to do the actual formatting. * The callers provide a callback function to save the formatted data. * The callback function is called multiple times: * - for each group of literal characters in the format string. * - for field padding. * - for each conversion specifier. * - with (NULL, 0) at the end of the __nolibc_printf. * If the callback returns non-zero __nolibc_printf() immediately returns -1. */ typedef int (*__nolibc_printf_cb)(void *state, const char *buf, size_t size); /* This code uses 'flag' variables that are indexed by the low 6 bits * of characters to optimise checks for multiple characters. * * _NOLIBC_PF_FLAGS_CONTAIN(flags, 'a', 'b'. ...) * returns non-zero if the bit for any of the specified characters is set. * * _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'a', 'b'. ...) * returns the flag bit for ch if it is one of the specified characters. * All the characters must be in the same 32 character block (non-alphabetic, * upper case, or lower case) of the ASCII character set. */#define _NOLIBC_PF_FLAG(ch) (1u << ((ch) & 0x1f))#define _NOLIBC_PF_FLAG_NZ(ch) ((ch) ? _NOLIBC_PF_FLAG(ch) : 0)#define _NOLIBC_PF_FLAG8(cmp_1, cmp_2, cmp_3, cmp_4, cmp_5, cmp_6, cmp_7, cmp_8, ...) \ (_NOLIBC_PF_FLAG_NZ(cmp_1) | _NOLIBC_PF_FLAG_NZ(cmp_2) | \ _NOLIBC_PF_FLAG_NZ(cmp_3) | _NOLIBC_PF_FLAG_NZ(cmp_4) | \ _NOLIBC_PF_FLAG_NZ(cmp_5) | _NOLIBC_PF_FLAG_NZ(cmp_6) | \ _NOLIBC_PF_FLAG_NZ(cmp_7) | _NOLIBC_PF_FLAG_NZ(cmp_8))#define _NOLIBC_PF_FLAGS_CONTAIN(flags, ...) \ ((flags) & _NOLIBC_PF_FLAG8(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0))#define _NOLIBC_PF_CHAR_IS_ONE_OF(ch, cmp_1, ...) \ ((unsigned int)(ch) - (cmp_1 & 0xe0) > 0x1f ? 0 : \ _NOLIBC_PF_FLAGS_CONTAIN(_NOLIBC_PF_FLAG(ch), cmp_1, __VA_ARGS__)) static __attribute__((unused, format(printf, 3, 0)))int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list args){ char ch; unsigned long long v; long long signed_v; int written, width, precision, len; unsigned int flags, ch_flag; char outbuf[2 + 31 + 22 + 1]; char *out; const char *outstr; unsigned int sign_prefix; int got_width; written = 0; while (1) { outstr = fmt; ch = *fmt++; if (!ch) break; width = 0; flags = 0; if (ch != '%') { while (*fmt && *fmt != '%') fmt++; /* Output characters from the format string. */ len = fmt - outstr; goto do_output; } /* we're in a format sequence */ /* Conversion flag characters */ while (1) { ch = *fmt++; ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, ' ', '#', '+', '-', '0'); if (!ch_flag) break; flags |= ch_flag; } /* Width and precision */ for (got_width = 0;; ch = *fmt++) { if (ch == '*') { precision = va_arg(args, int); ch = *fmt++; } else { for (precision = 0; ch >= '0' && ch <= '9'; ch = *fmt++) precision = precision * 10 + (ch - '0'); } if (got_width) break; width = precision; if (ch != '.') { /* Default precision for strings */ precision = -1; break; } got_width = 1; } /* A negative width (e.g. from "%*s") requests left justify. */ if (width < 0) { width = -width; flags |= _NOLIBC_PF_FLAG('-'); } /* Length modifier. * They miss the conversion flags characters " #+-0" so can go into flags. * Change both L and ll to j (all always 64bit). */ if (ch == 'L') ch = 'j'; ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'l', 't', 'z', 'j', 'q'); if (ch_flag != 0) { if (ch == 'l' && fmt[0] == 'l') { fmt++; ch_flag = _NOLIBC_PF_FLAG('j'); } flags |= ch_flag; ch = *fmt++; } /* Conversion specifiers. */ /* Numeric and pointer conversion specifiers. * * Use an explicit bound check (rather than _NOLIBC_PF_CHAR_IS_ONE_OF()) * so that 'X' can be allowed through. * 'X' gets treated and 'x' because _NOLIBC_PF_FLAG() returns the same * value for both. * * We need to check for "%p" or "%#x" later, merging here gives better code. * But '#' collides with 'c' so shift right. */ ch_flag = _NOLIBC_PF_FLAG(ch) | (flags & _NOLIBC_PF_FLAG('#')) >> 1; if (((ch >= 'a' && ch <= 'z') || ch == 'X') && _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'o', 'x', 'p', 's')) { /* 'long' is needed for pointer/string conversions and ltz lengths. * A single test can be used provided 'p' (the same bit as '0') * is masked from flags. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag | (flags & ~_NOLIBC_PF_FLAG('p')), 'p', 's', 'l', 't', 'z')) { v = va_arg(args, unsigned long); signed_v = (long)v; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, 'j', 'q')) { v = va_arg(args, unsigned long long); signed_v = v; } else { v = va_arg(args, unsigned int); signed_v = (int)v; } if (ch == 'c') { /* "%c" - single character. */ outbuf[0] = v; len = 1; outstr = outbuf; goto do_output; } if (ch == 's') { /* "%s" - character string. */ outstr = (const char *)(uintptr_t)v; if (!outstr) { outstr = "(null)"; /* Match glibc, nothing output if precision too small */ len = precision < 0 || precision >= 6 ? 6 : 0; goto do_output; } goto do_strlen_output; } /* The 'sign_prefix' can be zero, one or two ("0x") characters. * Prepended least significant byte first stopping on a zero byte. */ sign_prefix = 0; if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) { /* "%d" and "%i" - signed decimal numbers. */ if (signed_v < 0) { sign_prefix = '-'; v = -(signed_v + 1); v++; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '+')) { sign_prefix = '+'; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, ' ')) { sign_prefix = ' '; } } else { /* "#o" requires that the output always starts with a '0'. * This needs another check after any zero padding to avoid * adding an extra leading '0'. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o') && _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, '#' - 1)) sign_prefix = '0'; } /* The value is converted offset into the buffer so that * 31 zero pad characters and the sign/prefix can be added in front. * The longest digit string is 22 + 1 for octal conversions.Lines 1–500 of 1004. Load more
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ /* * minimal stdio function definitions for NOLIBC * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu> */ /* make sure to include all global symbols */ #include "nolibc.h" #ifndef _NOLIBC_STDIO_H #define _NOLIBC_STDIO_H #include "std.h" #include "arch.h" #include "errno.h" #include "fcntl.h" #include "types.h" #include "sys.h" #include "stdarg.h" #include "stdlib.h" #include "string.h" #include "compiler.h" static const char *strerror(int errnum); #ifndef EOF #define EOF (-1) #endif /* Buffering mode used by setvbuf. */ #define _IOFBF 0 /* Fully buffered. */ #define _IOLBF 1 /* Line buffered. */ #define _IONBF 2 /* No buffering. */ /* just define FILE as a non-empty type. The value of the pointer gives * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE * are immediately identified as abnormal entries (i.e. possible copies * of valid pointers to something else). */ typedef struct FILE { char dummy[1]; } FILE; static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO; static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO; static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO; /* provides a FILE* equivalent of fd. The mode is ignored. */ static __attribute__((unused)) FILE *fdopen(int fd, const char *mode __attribute__((unused))) { if (fd < 0) { SET_ERRNO(EBADF); return NULL; } return (FILE*)(intptr_t)~fd; } static __attribute__((unused)) FILE *fopen(const char *pathname, const char *mode) { int flags, fd; switch (*mode) { case 'r': flags = O_RDONLY; break; case 'w': flags = O_WRONLY | O_CREAT | O_TRUNC; break; case 'a': flags = O_WRONLY | O_CREAT | O_APPEND; break; default: SET_ERRNO(EINVAL); return NULL; } if (mode[1] == '+') flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; fd = open(pathname, flags, 0666); return fdopen(fd, mode); } /* provides the fd of stream. */ static __attribute__((unused)) int fileno(FILE *stream) { intptr_t i = (intptr_t)stream; if (i >= 0) { SET_ERRNO(EBADF); return -1; } return ~i; } /* flush a stream. */ static __attribute__((unused)) int fflush(FILE *stream) { intptr_t i = (intptr_t)stream; /* NULL is valid here. */ if (i > 0) { SET_ERRNO(EBADF); return -1; } /* Don't do anything, nolibc does not support buffering. */ return 0; } /* flush a stream. */ static __attribute__((unused)) int fclose(FILE *stream) { intptr_t i = (intptr_t)stream; if (i >= 0) { SET_ERRNO(EBADF); return -1; } if (close(~i)) return EOF; return 0; } /* getc(), fgetc(), getchar() */ #define getc(stream) fgetc(stream) static __attribute__((unused)) int fgetc(FILE* stream) { unsigned char ch; if (read(fileno(stream), &ch, 1) <= 0) return EOF; return ch; } static __attribute__((unused)) int getchar(void) { return fgetc(stdin); } /* putc(), fputc(), putchar() */ #define putc(c, stream) fputc(c, stream) static __attribute__((unused)) int fputc(int c, FILE* stream) { unsigned char ch = c; if (write(fileno(stream), &ch, 1) <= 0) return EOF; return ch; } static __attribute__((unused)) int putchar(int c) { return fputc(c, stdout); } /* fwrite(), fread(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */ /* internal fwrite()-like function which only takes a size and returns 0 on * success or EOF on error. It automatically retries on short writes. */ static __attribute__((unused)) int _fwrite(const void *buf, size_t size, FILE *stream) { ssize_t ret; int fd = fileno(stream); while (size) { ret = write(fd, buf, size); if (ret <= 0) return EOF; size -= ret; buf += ret; } return 0; } static __attribute__((unused)) size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream) { size_t written; for (written = 0; written < nmemb; written++) { if (_fwrite(s, size, stream) != 0) break; s += size; } return written; } /* internal fread()-like function which only takes a size and returns 0 on * success or EOF on error. It automatically retries on short reads. */ static __attribute__((unused)) int _fread(void *buf, size_t size, FILE *stream) { int fd = fileno(stream); ssize_t ret; while (size) { ret = read(fd, buf, size); if (ret <= 0) return EOF; size -= ret; buf += ret; } return 0; } static __attribute__((unused)) size_t fread(void *s, size_t size, size_t nmemb, FILE *stream) { size_t nread; for (nread = 0; nread < nmemb; nread++) { if (_fread(s, size, stream) != 0) break; s += size; } return nread; } static __attribute__((unused)) int fputs(const char *s, FILE *stream) { return _fwrite(s, strlen(s), stream); } static __attribute__((unused)) int puts(const char *s) { if (fputs(s, stdout) == EOF) return EOF; return putchar('\n'); } /* fgets() */ static __attribute__((unused)) char *fgets(char *s, int size, FILE *stream) { int ofs; int c; for (ofs = 0; ofs + 1 < size;) { c = fgetc(stream); if (c == EOF) break; s[ofs++] = c; if (c == '\n') break; } if (ofs < size) s[ofs] = 0; return ofs ? s : NULL; } /* fseek */ static __attribute__((unused)) int fseek(FILE *stream, long offset, int whence) { int fd = fileno(stream); off_t ret; ret = lseek(fd, offset, whence); /* lseek() and fseek() differ in that lseek returns the new * position or -1, fseek() returns either 0 or -1. */ if (ret >= 0) return 0; return -1; } /* printf(). Supports most of the normal integer and string formats. * - %[#0-+ ][width|*[.precision|*}][{l,t,z,ll,L,j,q}]{c,d,i,u,o,x,X,p,s,m,%} * - %% generates a single % * - %m outputs strerror(errno). * - %X outputs a..f the same as %x. * - No support for floating point or wide characters. * - Invalid formats are copied to the output buffer. * * Called by vfprintf() and snprintf() to do the actual formatting. * The callers provide a callback function to save the formatted data. * The callback function is called multiple times: * - for each group of literal characters in the format string. * - for field padding. * - for each conversion specifier. * - with (NULL, 0) at the end of the __nolibc_printf. * If the callback returns non-zero __nolibc_printf() immediately returns -1. */ typedef int (*__nolibc_printf_cb)(void *state, const char *buf, size_t size); /* This code uses 'flag' variables that are indexed by the low 6 bits * of characters to optimise checks for multiple characters. * * _NOLIBC_PF_FLAGS_CONTAIN(flags, 'a', 'b'. ...) * returns non-zero if the bit for any of the specified characters is set. * * _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'a', 'b'. ...) * returns the flag bit for ch if it is one of the specified characters. * All the characters must be in the same 32 character block (non-alphabetic, * upper case, or lower case) of the ASCII character set. */ #define _NOLIBC_PF_FLAG(ch) (1u << ((ch) & 0x1f)) #define _NOLIBC_PF_FLAG_NZ(ch) ((ch) ? _NOLIBC_PF_FLAG(ch) : 0) #define _NOLIBC_PF_FLAG8(cmp_1, cmp_2, cmp_3, cmp_4, cmp_5, cmp_6, cmp_7, cmp_8, ...) \ (_NOLIBC_PF_FLAG_NZ(cmp_1) | _NOLIBC_PF_FLAG_NZ(cmp_2) | \ _NOLIBC_PF_FLAG_NZ(cmp_3) | _NOLIBC_PF_FLAG_NZ(cmp_4) | \ _NOLIBC_PF_FLAG_NZ(cmp_5) | _NOLIBC_PF_FLAG_NZ(cmp_6) | \ _NOLIBC_PF_FLAG_NZ(cmp_7) | _NOLIBC_PF_FLAG_NZ(cmp_8)) #define _NOLIBC_PF_FLAGS_CONTAIN(flags, ...) \ ((flags) & _NOLIBC_PF_FLAG8(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0)) #define _NOLIBC_PF_CHAR_IS_ONE_OF(ch, cmp_1, ...) \ ((unsigned int)(ch) - (cmp_1 & 0xe0) > 0x1f ? 0 : \ _NOLIBC_PF_FLAGS_CONTAIN(_NOLIBC_PF_FLAG(ch), cmp_1, __VA_ARGS__)) static __attribute__((unused, format(printf, 3, 0))) int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list args) { char ch; unsigned long long v; long long signed_v; int written, width, precision, len; unsigned int flags, ch_flag; char outbuf[2 + 31 + 22 + 1]; char *out; const char *outstr; unsigned int sign_prefix; int got_width; written = 0; while (1) { outstr = fmt; ch = *fmt++; if (!ch) break; width = 0; flags = 0; if (ch != '%') { while (*fmt && *fmt != '%') fmt++; /* Output characters from the format string. */ len = fmt - outstr; goto do_output; } /* we're in a format sequence */ /* Conversion flag characters */ while (1) { ch = *fmt++; ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, ' ', '#', '+', '-', '0'); if (!ch_flag) break; flags |= ch_flag; } /* Width and precision */ for (got_width = 0;; ch = *fmt++) { if (ch == '*') { precision = va_arg(args, int); ch = *fmt++; } else { for (precision = 0; ch >= '0' && ch <= '9'; ch = *fmt++) precision = precision * 10 + (ch - '0'); } if (got_width) break; width = precision; if (ch != '.') { /* Default precision for strings */ precision = -1; break; } got_width = 1; } /* A negative width (e.g. from "%*s") requests left justify. */ if (width < 0) { width = -width; flags |= _NOLIBC_PF_FLAG('-'); } /* Length modifier. * They miss the conversion flags characters " #+-0" so can go into flags. * Change both L and ll to j (all always 64bit). */ if (ch == 'L') ch = 'j'; ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'l', 't', 'z', 'j', 'q'); if (ch_flag != 0) { if (ch == 'l' && fmt[0] == 'l') { fmt++; ch_flag = _NOLIBC_PF_FLAG('j'); } flags |= ch_flag; ch = *fmt++; } /* Conversion specifiers. */ /* Numeric and pointer conversion specifiers. * * Use an explicit bound check (rather than _NOLIBC_PF_CHAR_IS_ONE_OF()) * so that 'X' can be allowed through. * 'X' gets treated and 'x' because _NOLIBC_PF_FLAG() returns the same * value for both. * * We need to check for "%p" or "%#x" later, merging here gives better code. * But '#' collides with 'c' so shift right. */ ch_flag = _NOLIBC_PF_FLAG(ch) | (flags & _NOLIBC_PF_FLAG('#')) >> 1; if (((ch >= 'a' && ch <= 'z') || ch == 'X') && _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'o', 'x', 'p', 's')) { /* 'long' is needed for pointer/string conversions and ltz lengths. * A single test can be used provided 'p' (the same bit as '0') * is masked from flags. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag | (flags & ~_NOLIBC_PF_FLAG('p')), 'p', 's', 'l', 't', 'z')) { v = va_arg(args, unsigned long); signed_v = (long)v; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, 'j', 'q')) { v = va_arg(args, unsigned long long); signed_v = v; } else { v = va_arg(args, unsigned int); signed_v = (int)v; } if (ch == 'c') { /* "%c" - single character. */ outbuf[0] = v; len = 1; outstr = outbuf; goto do_output; } if (ch == 's') { /* "%s" - character string. */ outstr = (const char *)(uintptr_t)v; if (!outstr) { outstr = "(null)"; /* Match glibc, nothing output if precision too small */ len = precision < 0 || precision >= 6 ? 6 : 0; goto do_output; } goto do_strlen_output; } /* The 'sign_prefix' can be zero, one or two ("0x") characters. * Prepended least significant byte first stopping on a zero byte. */ sign_prefix = 0; if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) { /* "%d" and "%i" - signed decimal numbers. */ if (signed_v < 0) { sign_prefix = '-'; v = -(signed_v + 1); v++; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '+')) { sign_prefix = '+'; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, ' ')) { sign_prefix = ' '; } } else { /* "#o" requires that the output always starts with a '0'. * This needs another check after any zero padding to avoid * adding an extra leading '0'. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o') && _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, '#' - 1)) sign_prefix = '0'; } /* The value is converted offset into the buffer so that * 31 zero pad characters and the sign/prefix can be added in front. * The longest digit string is 22 + 1 for octal conversions.