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