at v6.5 356 lines 6.7 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#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 */ 29typedef struct FILE { 30 char dummy[1]; 31} FILE; 32 33static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO; 34static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO; 35static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO; 36 37/* provides a FILE* equivalent of fd. The mode is ignored. */ 38static __attribute__((unused)) 39FILE *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. */ 49static __attribute__((unused)) 50int 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. */ 62static __attribute__((unused)) 63int 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. */ 78static __attribute__((unused)) 79int 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 98static __attribute__((unused)) 99int 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 108static __attribute__((unused)) 109int getchar(void) 110{ 111 return fgetc(stdin); 112} 113 114 115/* putc(), fputc(), putchar() */ 116 117#define putc(c, stream) fputc(c, stream) 118 119static __attribute__((unused)) 120int 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 129static __attribute__((unused)) 130int 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 */ 141static __attribute__((unused)) 142int _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 157static __attribute__((unused)) 158size_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 170static __attribute__((unused)) 171int fputs(const char *s, FILE *stream) 172{ 173 return _fwrite(s, strlen(s), stream); 174} 175 176static __attribute__((unused)) 177int puts(const char *s) 178{ 179 if (fputs(s, stdout) == EOF) 180 return EOF; 181 return putchar('\n'); 182} 183 184 185/* fgets() */ 186static __attribute__((unused)) 187char *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 */ 211static __attribute__((unused)) 212int 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 317static __attribute__((unused)) 318int vprintf(const char *fmt, va_list args) 319{ 320 return vfprintf(stdout, fmt, args); 321} 322 323static __attribute__((unused, format(printf, 2, 3))) 324int 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 335static __attribute__((unused, format(printf, 1, 2))) 336int 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 347static __attribute__((unused)) 348void 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 */