393 lines · c
1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */2/*3 * minimal stdio function definitions for NOLIBC4 * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>5 */6 7#ifndef _NOLIBC_STDIO_H8#define _NOLIBC_STDIO_H9 10#include "std.h"11#include "arch.h"12#include "errno.h"13#include "types.h"14#include "sys.h"15#include "stdarg.h"16#include "stdlib.h"17#include "string.h"18 19#ifndef EOF20#define EOF (-1)21#endif22 23/* Buffering mode used by setvbuf. */24#define _IOFBF 0 /* Fully buffered. */25#define _IOLBF 1 /* Line buffered. */26#define _IONBF 2 /* No buffering. */27 28/* just define FILE as a non-empty type. The value of the pointer gives29 * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE30 * are immediately identified as abnormal entries (i.e. possible copies31 * of valid pointers to something else).32 */33typedef struct FILE {34 char dummy[1];35} FILE;36 37static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;38static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;39static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;40 41/* provides a FILE* equivalent of fd. The mode is ignored. */42static __attribute__((unused))43FILE *fdopen(int fd, const char *mode __attribute__((unused)))44{45 if (fd < 0) {46 SET_ERRNO(EBADF);47 return NULL;48 }49 return (FILE*)(intptr_t)~fd;50}51 52/* provides the fd of stream. */53static __attribute__((unused))54int fileno(FILE *stream)55{56 intptr_t i = (intptr_t)stream;57 58 if (i >= 0) {59 SET_ERRNO(EBADF);60 return -1;61 }62 return ~i;63}64 65/* flush a stream. */66static __attribute__((unused))67int fflush(FILE *stream)68{69 intptr_t i = (intptr_t)stream;70 71 /* NULL is valid here. */72 if (i > 0) {73 SET_ERRNO(EBADF);74 return -1;75 }76 77 /* Don't do anything, nolibc does not support buffering. */78 return 0;79}80 81/* flush a stream. */82static __attribute__((unused))83int fclose(FILE *stream)84{85 intptr_t i = (intptr_t)stream;86 87 if (i >= 0) {88 SET_ERRNO(EBADF);89 return -1;90 }91 92 if (close(~i))93 return EOF;94 95 return 0;96}97 98/* getc(), fgetc(), getchar() */99 100#define getc(stream) fgetc(stream)101 102static __attribute__((unused))103int fgetc(FILE* stream)104{105 unsigned char ch;106 107 if (read(fileno(stream), &ch, 1) <= 0)108 return EOF;109 return ch;110}111 112static __attribute__((unused))113int getchar(void)114{115 return fgetc(stdin);116}117 118 119/* putc(), fputc(), putchar() */120 121#define putc(c, stream) fputc(c, stream)122 123static __attribute__((unused))124int fputc(int c, FILE* stream)125{126 unsigned char ch = c;127 128 if (write(fileno(stream), &ch, 1) <= 0)129 return EOF;130 return ch;131}132 133static __attribute__((unused))134int putchar(int c)135{136 return fputc(c, stdout);137}138 139 140/* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */141 142/* internal fwrite()-like function which only takes a size and returns 0 on143 * success or EOF on error. It automatically retries on short writes.144 */145static __attribute__((unused))146int _fwrite(const void *buf, size_t size, FILE *stream)147{148 ssize_t ret;149 int fd = fileno(stream);150 151 while (size) {152 ret = write(fd, buf, size);153 if (ret <= 0)154 return EOF;155 size -= ret;156 buf += ret;157 }158 return 0;159}160 161static __attribute__((unused))162size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)163{164 size_t written;165 166 for (written = 0; written < nmemb; written++) {167 if (_fwrite(s, size, stream) != 0)168 break;169 s += size;170 }171 return written;172}173 174static __attribute__((unused))175int fputs(const char *s, FILE *stream)176{177 return _fwrite(s, strlen(s), stream);178}179 180static __attribute__((unused))181int puts(const char *s)182{183 if (fputs(s, stdout) == EOF)184 return EOF;185 return putchar('\n');186}187 188 189/* fgets() */190static __attribute__((unused))191char *fgets(char *s, int size, FILE *stream)192{193 int ofs;194 int c;195 196 for (ofs = 0; ofs + 1 < size;) {197 c = fgetc(stream);198 if (c == EOF)199 break;200 s[ofs++] = c;201 if (c == '\n')202 break;203 }204 if (ofs < size)205 s[ofs] = 0;206 return ofs ? s : NULL;207}208 209 210/* minimal vfprintf(). It supports the following formats:211 * - %[l*]{d,u,c,x,p}212 * - %s213 * - unknown modifiers are ignored.214 */215static __attribute__((unused, format(printf, 2, 0)))216int vfprintf(FILE *stream, const char *fmt, va_list args)217{218 char escape, lpref, c;219 unsigned long long v;220 unsigned int written;221 size_t len, ofs;222 char tmpbuf[21];223 const char *outstr;224 225 written = ofs = escape = lpref = 0;226 while (1) {227 c = fmt[ofs++];228 229 if (escape) {230 /* we're in an escape sequence, ofs == 1 */231 escape = 0;232 if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {233 char *out = tmpbuf;234 235 if (c == 'p')236 v = va_arg(args, unsigned long);237 else if (lpref) {238 if (lpref > 1)239 v = va_arg(args, unsigned long long);240 else241 v = va_arg(args, unsigned long);242 } else243 v = va_arg(args, unsigned int);244 245 if (c == 'd') {246 /* sign-extend the value */247 if (lpref == 0)248 v = (long long)(int)v;249 else if (lpref == 1)250 v = (long long)(long)v;251 }252 253 switch (c) {254 case 'c':255 out[0] = v;256 out[1] = 0;257 break;258 case 'd':259 i64toa_r(v, out);260 break;261 case 'u':262 u64toa_r(v, out);263 break;264 case 'p':265 *(out++) = '0';266 *(out++) = 'x';267 /* fall through */268 default: /* 'x' and 'p' above */269 u64toh_r(v, out);270 break;271 }272 outstr = tmpbuf;273 }274 else if (c == 's') {275 outstr = va_arg(args, char *);276 if (!outstr)277 outstr="(null)";278 }279 else if (c == '%') {280 /* queue it verbatim */281 continue;282 }283 else {284 /* modifiers or final 0 */285 if (c == 'l') {286 /* long format prefix, maintain the escape */287 lpref++;288 }289 escape = 1;290 goto do_escape;291 }292 len = strlen(outstr);293 goto flush_str;294 }295 296 /* not an escape sequence */297 if (c == 0 || c == '%') {298 /* flush pending data on escape or end */299 escape = 1;300 lpref = 0;301 outstr = fmt;302 len = ofs - 1;303 flush_str:304 if (_fwrite(outstr, len, stream) != 0)305 break;306 307 written += len;308 do_escape:309 if (c == 0)310 break;311 fmt += ofs;312 ofs = 0;313 continue;314 }315 316 /* literal char, just queue it */317 }318 return written;319}320 321static __attribute__((unused, format(printf, 1, 0)))322int vprintf(const char *fmt, va_list args)323{324 return vfprintf(stdout, fmt, args);325}326 327static __attribute__((unused, format(printf, 2, 3)))328int fprintf(FILE *stream, const char *fmt, ...)329{330 va_list args;331 int ret;332 333 va_start(args, fmt);334 ret = vfprintf(stream, fmt, args);335 va_end(args);336 return ret;337}338 339static __attribute__((unused, format(printf, 1, 2)))340int printf(const char *fmt, ...)341{342 va_list args;343 int ret;344 345 va_start(args, fmt);346 ret = vfprintf(stdout, fmt, args);347 va_end(args);348 return ret;349}350 351static __attribute__((unused))352void perror(const char *msg)353{354 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);355}356 357static __attribute__((unused))358int setvbuf(FILE *stream __attribute__((unused)),359 char *buf __attribute__((unused)),360 int mode,361 size_t size __attribute__((unused)))362{363 /*364 * nolibc does not support buffering so this is a nop. Just check mode365 * is valid as required by the spec.366 */367 switch (mode) {368 case _IOFBF:369 case _IOLBF:370 case _IONBF:371 break;372 default:373 return EOF;374 }375 376 return 0;377}378 379static __attribute__((unused))380const char *strerror(int errno)381{382 static char buf[18] = "errno=";383 384 i64toa_r(errno, &buf[6]);385 386 return buf;387}388 389/* make sure to include all global symbols */390#include "nolibc.h"391 392#endif /* _NOLIBC_STDIO_H */393