at master 644 lines 13 kB view raw
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/* make sure to include all global symbols */ 8#include "nolibc.h" 9 10#ifndef _NOLIBC_STDIO_H 11#define _NOLIBC_STDIO_H 12 13#include "std.h" 14#include "arch.h" 15#include "errno.h" 16#include "fcntl.h" 17#include "types.h" 18#include "sys.h" 19#include "stdarg.h" 20#include "stdlib.h" 21#include "string.h" 22#include "compiler.h" 23 24static const char *strerror(int errnum); 25 26#ifndef EOF 27#define EOF (-1) 28#endif 29 30/* Buffering mode used by setvbuf. */ 31#define _IOFBF 0 /* Fully buffered. */ 32#define _IOLBF 1 /* Line buffered. */ 33#define _IONBF 2 /* No buffering. */ 34 35/* just define FILE as a non-empty type. The value of the pointer gives 36 * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE 37 * are immediately identified as abnormal entries (i.e. possible copies 38 * of valid pointers to something else). 39 */ 40typedef struct FILE { 41 char dummy[1]; 42} FILE; 43 44static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO; 45static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO; 46static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO; 47 48/* provides a FILE* equivalent of fd. The mode is ignored. */ 49static __attribute__((unused)) 50FILE *fdopen(int fd, const char *mode __attribute__((unused))) 51{ 52 if (fd < 0) { 53 SET_ERRNO(EBADF); 54 return NULL; 55 } 56 return (FILE*)(intptr_t)~fd; 57} 58 59static __attribute__((unused)) 60FILE *fopen(const char *pathname, const char *mode) 61{ 62 int flags, fd; 63 64 switch (*mode) { 65 case 'r': 66 flags = O_RDONLY; 67 break; 68 case 'w': 69 flags = O_WRONLY | O_CREAT | O_TRUNC; 70 break; 71 case 'a': 72 flags = O_WRONLY | O_CREAT | O_APPEND; 73 break; 74 default: 75 SET_ERRNO(EINVAL); return NULL; 76 } 77 78 if (mode[1] == '+') 79 flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; 80 81 fd = open(pathname, flags, 0666); 82 return fdopen(fd, mode); 83} 84 85/* provides the fd of stream. */ 86static __attribute__((unused)) 87int fileno(FILE *stream) 88{ 89 intptr_t i = (intptr_t)stream; 90 91 if (i >= 0) { 92 SET_ERRNO(EBADF); 93 return -1; 94 } 95 return ~i; 96} 97 98/* flush a stream. */ 99static __attribute__((unused)) 100int fflush(FILE *stream) 101{ 102 intptr_t i = (intptr_t)stream; 103 104 /* NULL is valid here. */ 105 if (i > 0) { 106 SET_ERRNO(EBADF); 107 return -1; 108 } 109 110 /* Don't do anything, nolibc does not support buffering. */ 111 return 0; 112} 113 114/* flush a stream. */ 115static __attribute__((unused)) 116int fclose(FILE *stream) 117{ 118 intptr_t i = (intptr_t)stream; 119 120 if (i >= 0) { 121 SET_ERRNO(EBADF); 122 return -1; 123 } 124 125 if (close(~i)) 126 return EOF; 127 128 return 0; 129} 130 131/* getc(), fgetc(), getchar() */ 132 133#define getc(stream) fgetc(stream) 134 135static __attribute__((unused)) 136int fgetc(FILE* stream) 137{ 138 unsigned char ch; 139 140 if (read(fileno(stream), &ch, 1) <= 0) 141 return EOF; 142 return ch; 143} 144 145static __attribute__((unused)) 146int getchar(void) 147{ 148 return fgetc(stdin); 149} 150 151 152/* putc(), fputc(), putchar() */ 153 154#define putc(c, stream) fputc(c, stream) 155 156static __attribute__((unused)) 157int fputc(int c, FILE* stream) 158{ 159 unsigned char ch = c; 160 161 if (write(fileno(stream), &ch, 1) <= 0) 162 return EOF; 163 return ch; 164} 165 166static __attribute__((unused)) 167int putchar(int c) 168{ 169 return fputc(c, stdout); 170} 171 172 173/* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */ 174 175/* internal fwrite()-like function which only takes a size and returns 0 on 176 * success or EOF on error. It automatically retries on short writes. 177 */ 178static __attribute__((unused)) 179int _fwrite(const void *buf, size_t size, FILE *stream) 180{ 181 ssize_t ret; 182 int fd = fileno(stream); 183 184 while (size) { 185 ret = write(fd, buf, size); 186 if (ret <= 0) 187 return EOF; 188 size -= ret; 189 buf += ret; 190 } 191 return 0; 192} 193 194static __attribute__((unused)) 195size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream) 196{ 197 size_t written; 198 199 for (written = 0; written < nmemb; written++) { 200 if (_fwrite(s, size, stream) != 0) 201 break; 202 s += size; 203 } 204 return written; 205} 206 207static __attribute__((unused)) 208int fputs(const char *s, FILE *stream) 209{ 210 return _fwrite(s, strlen(s), stream); 211} 212 213static __attribute__((unused)) 214int puts(const char *s) 215{ 216 if (fputs(s, stdout) == EOF) 217 return EOF; 218 return putchar('\n'); 219} 220 221 222/* fgets() */ 223static __attribute__((unused)) 224char *fgets(char *s, int size, FILE *stream) 225{ 226 int ofs; 227 int c; 228 229 for (ofs = 0; ofs + 1 < size;) { 230 c = fgetc(stream); 231 if (c == EOF) 232 break; 233 s[ofs++] = c; 234 if (c == '\n') 235 break; 236 } 237 if (ofs < size) 238 s[ofs] = 0; 239 return ofs ? s : NULL; 240} 241 242 243/* minimal printf(). It supports the following formats: 244 * - %[l*]{d,u,c,x,p} 245 * - %s 246 * - unknown modifiers are ignored. 247 */ 248typedef int (*__nolibc_printf_cb)(intptr_t state, const char *buf, size_t size); 249 250static __attribute__((unused, format(printf, 4, 0))) 251int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char *fmt, va_list args) 252{ 253 char escape, lpref, c; 254 unsigned long long v; 255 unsigned int written, width; 256 size_t len, ofs, w; 257 char tmpbuf[21]; 258 const char *outstr; 259 260 written = ofs = escape = lpref = 0; 261 while (1) { 262 c = fmt[ofs++]; 263 width = 0; 264 265 if (escape) { 266 /* we're in an escape sequence, ofs == 1 */ 267 escape = 0; 268 269 /* width */ 270 while (c >= '0' && c <= '9') { 271 width *= 10; 272 width += c - '0'; 273 274 c = fmt[ofs++]; 275 } 276 277 if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') { 278 char *out = tmpbuf; 279 280 if (c == 'p') 281 v = va_arg(args, unsigned long); 282 else if (lpref) { 283 if (lpref > 1) 284 v = va_arg(args, unsigned long long); 285 else 286 v = va_arg(args, unsigned long); 287 } else 288 v = va_arg(args, unsigned int); 289 290 if (c == 'd') { 291 /* sign-extend the value */ 292 if (lpref == 0) 293 v = (long long)(int)v; 294 else if (lpref == 1) 295 v = (long long)(long)v; 296 } 297 298 switch (c) { 299 case 'c': 300 out[0] = v; 301 out[1] = 0; 302 break; 303 case 'd': 304 i64toa_r(v, out); 305 break; 306 case 'u': 307 u64toa_r(v, out); 308 break; 309 case 'p': 310 *(out++) = '0'; 311 *(out++) = 'x'; 312 __nolibc_fallthrough; 313 default: /* 'x' and 'p' above */ 314 u64toh_r(v, out); 315 break; 316 } 317 outstr = tmpbuf; 318 } 319 else if (c == 's') { 320 outstr = va_arg(args, char *); 321 if (!outstr) 322 outstr="(null)"; 323 } 324 else if (c == 'm') { 325#ifdef NOLIBC_IGNORE_ERRNO 326 outstr = "unknown error"; 327#else 328 outstr = strerror(errno); 329#endif /* NOLIBC_IGNORE_ERRNO */ 330 } 331 else if (c == '%') { 332 /* queue it verbatim */ 333 continue; 334 } 335 else { 336 /* modifiers or final 0 */ 337 if (c == 'l') { 338 /* long format prefix, maintain the escape */ 339 lpref++; 340 } else if (c == 'j') { 341 lpref = 2; 342 } 343 escape = 1; 344 goto do_escape; 345 } 346 len = strlen(outstr); 347 goto flush_str; 348 } 349 350 /* not an escape sequence */ 351 if (c == 0 || c == '%') { 352 /* flush pending data on escape or end */ 353 escape = 1; 354 lpref = 0; 355 outstr = fmt; 356 len = ofs - 1; 357 flush_str: 358 if (n) { 359 w = len < n ? len : n; 360 n -= w; 361 while (width-- > w) { 362 if (cb(state, " ", 1) != 0) 363 return -1; 364 written += 1; 365 } 366 if (cb(state, outstr, w) != 0) 367 return -1; 368 } 369 370 written += len; 371 do_escape: 372 if (c == 0) 373 break; 374 fmt += ofs; 375 ofs = 0; 376 continue; 377 } 378 379 /* literal char, just queue it */ 380 } 381 return written; 382} 383 384static int __nolibc_fprintf_cb(intptr_t state, const char *buf, size_t size) 385{ 386 return _fwrite(buf, size, (FILE *)state); 387} 388 389static __attribute__((unused, format(printf, 2, 0))) 390int vfprintf(FILE *stream, const char *fmt, va_list args) 391{ 392 return __nolibc_printf(__nolibc_fprintf_cb, (intptr_t)stream, SIZE_MAX, fmt, args); 393} 394 395static __attribute__((unused, format(printf, 1, 0))) 396int vprintf(const char *fmt, va_list args) 397{ 398 return vfprintf(stdout, fmt, args); 399} 400 401static __attribute__((unused, format(printf, 2, 3))) 402int fprintf(FILE *stream, const char *fmt, ...) 403{ 404 va_list args; 405 int ret; 406 407 va_start(args, fmt); 408 ret = vfprintf(stream, fmt, args); 409 va_end(args); 410 return ret; 411} 412 413static __attribute__((unused, format(printf, 1, 2))) 414int printf(const char *fmt, ...) 415{ 416 va_list args; 417 int ret; 418 419 va_start(args, fmt); 420 ret = vfprintf(stdout, fmt, args); 421 va_end(args); 422 return ret; 423} 424 425static __attribute__((unused, format(printf, 2, 0))) 426int vdprintf(int fd, const char *fmt, va_list args) 427{ 428 FILE *stream; 429 430 stream = fdopen(fd, NULL); 431 if (!stream) 432 return -1; 433 /* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */ 434 return vfprintf(stream, fmt, args); 435} 436 437static __attribute__((unused, format(printf, 2, 3))) 438int dprintf(int fd, const char *fmt, ...) 439{ 440 va_list args; 441 int ret; 442 443 va_start(args, fmt); 444 ret = vdprintf(fd, fmt, args); 445 va_end(args); 446 447 return ret; 448} 449 450static int __nolibc_sprintf_cb(intptr_t _state, const char *buf, size_t size) 451{ 452 char **state = (char **)_state; 453 454 memcpy(*state, buf, size); 455 *state += size; 456 return 0; 457} 458 459static __attribute__((unused, format(printf, 3, 0))) 460int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) 461{ 462 char *state = buf; 463 int ret; 464 465 ret = __nolibc_printf(__nolibc_sprintf_cb, (intptr_t)&state, size, fmt, args); 466 if (ret < 0) 467 return ret; 468 buf[(size_t)ret < size ? (size_t)ret : size - 1] = '\0'; 469 return ret; 470} 471 472static __attribute__((unused, format(printf, 3, 4))) 473int snprintf(char *buf, size_t size, const char *fmt, ...) 474{ 475 va_list args; 476 int ret; 477 478 va_start(args, fmt); 479 ret = vsnprintf(buf, size, fmt, args); 480 va_end(args); 481 482 return ret; 483} 484 485static __attribute__((unused, format(printf, 2, 0))) 486int vsprintf(char *buf, const char *fmt, va_list args) 487{ 488 return vsnprintf(buf, SIZE_MAX, fmt, args); 489} 490 491static __attribute__((unused, format(printf, 2, 3))) 492int sprintf(char *buf, const char *fmt, ...) 493{ 494 va_list args; 495 int ret; 496 497 va_start(args, fmt); 498 ret = vsprintf(buf, fmt, args); 499 va_end(args); 500 501 return ret; 502} 503 504static __attribute__((unused)) 505int vsscanf(const char *str, const char *format, va_list args) 506{ 507 uintmax_t uval; 508 intmax_t ival; 509 int base; 510 char *endptr; 511 int matches; 512 int lpref; 513 514 matches = 0; 515 516 while (1) { 517 if (*format == '%') { 518 /* start of pattern */ 519 lpref = 0; 520 format++; 521 522 if (*format == 'l') { 523 /* same as in printf() */ 524 lpref = 1; 525 format++; 526 if (*format == 'l') { 527 lpref = 2; 528 format++; 529 } 530 } 531 532 if (*format == '%') { 533 /* literal % */ 534 if ('%' != *str) 535 goto done; 536 str++; 537 format++; 538 continue; 539 } else if (*format == 'd') { 540 ival = strtoll(str, &endptr, 10); 541 if (lpref == 0) 542 *va_arg(args, int *) = ival; 543 else if (lpref == 1) 544 *va_arg(args, long *) = ival; 545 else if (lpref == 2) 546 *va_arg(args, long long *) = ival; 547 } else if (*format == 'u' || *format == 'x' || *format == 'X') { 548 base = *format == 'u' ? 10 : 16; 549 uval = strtoull(str, &endptr, base); 550 if (lpref == 0) 551 *va_arg(args, unsigned int *) = uval; 552 else if (lpref == 1) 553 *va_arg(args, unsigned long *) = uval; 554 else if (lpref == 2) 555 *va_arg(args, unsigned long long *) = uval; 556 } else if (*format == 'p') { 557 *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16); 558 } else { 559 SET_ERRNO(EILSEQ); 560 goto done; 561 } 562 563 format++; 564 str = endptr; 565 matches++; 566 567 } else if (*format == '\0') { 568 goto done; 569 } else if (isspace(*format)) { 570 /* skip spaces in format and str */ 571 while (isspace(*format)) 572 format++; 573 while (isspace(*str)) 574 str++; 575 } else if (*format == *str) { 576 /* literal match */ 577 format++; 578 str++; 579 } else { 580 if (!matches) 581 matches = EOF; 582 goto done; 583 } 584 } 585 586done: 587 return matches; 588} 589 590static __attribute__((unused, format(scanf, 2, 3))) 591int sscanf(const char *str, const char *format, ...) 592{ 593 va_list args; 594 int ret; 595 596 va_start(args, format); 597 ret = vsscanf(str, format, args); 598 va_end(args); 599 return ret; 600} 601 602static __attribute__((unused)) 603void perror(const char *msg) 604{ 605#ifdef NOLIBC_IGNORE_ERRNO 606 fprintf(stderr, "%s%sunknown error\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : ""); 607#else 608 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno); 609#endif 610} 611 612static __attribute__((unused)) 613int setvbuf(FILE *stream __attribute__((unused)), 614 char *buf __attribute__((unused)), 615 int mode, 616 size_t size __attribute__((unused))) 617{ 618 /* 619 * nolibc does not support buffering so this is a nop. Just check mode 620 * is valid as required by the spec. 621 */ 622 switch (mode) { 623 case _IOFBF: 624 case _IOLBF: 625 case _IONBF: 626 break; 627 default: 628 return EOF; 629 } 630 631 return 0; 632} 633 634static __attribute__((unused)) 635const char *strerror(int errno) 636{ 637 static char buf[18] = "errno="; 638 639 i64toa_r(errno, &buf[6]); 640 641 return buf; 642} 643 644#endif /* _NOLIBC_STDIO_H */