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