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