1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2/* 3 * minimal stdio function definitions for NOLIBC 4 * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu> 5 */ 6 7#ifndef _NOLIBC_STDIO_H 8#define _NOLIBC_STDIO_H 9 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 EOF 20#define EOF (-1) 21#endif 22 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 gives 29 * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE 30 * are immediately identified as abnormal entries (i.e. possible copies 31 * 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 on 143 * 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 * - %s 213 * - 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 else 241 v = va_arg(args, unsigned long); 242 } else 243 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 mode 365 * 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 379/* make sure to include all global symbols */ 380#include "nolibc.h" 381 382#endif /* _NOLIBC_STDIO_H */ 383