stdio.h (5590B)
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 */ 25typedef 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 */ 32static __attribute__((unused)) FILE* const stdin = (FILE*)-3; 33static __attribute__((unused)) FILE* const stdout = (FILE*)-2; 34static __attribute__((unused)) FILE* const stderr = (FILE*)-1; 35 36/* getc(), fgetc(), getchar() */ 37 38#define getc(stream) fgetc(stream) 39 40static __attribute__((unused)) 41int 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 56static __attribute__((unused)) 57int getchar(void) 58{ 59 return fgetc(stdin); 60} 61 62 63/* putc(), fputc(), putchar() */ 64 65#define putc(c, stream) fputc(c, stream) 66 67static __attribute__((unused)) 68int 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 83static __attribute__((unused)) 84int 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 */ 95static __attribute__((unused)) 96int _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 116static __attribute__((unused)) 117size_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 129static __attribute__((unused)) 130int fputs(const char *s, FILE *stream) 131{ 132 return _fwrite(s, strlen(s), stream); 133} 134 135static __attribute__((unused)) 136int puts(const char *s) 137{ 138 if (fputs(s, stdout) == EOF) 139 return EOF; 140 return putchar('\n'); 141} 142 143 144/* fgets() */ 145static __attribute__((unused)) 146char *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 */ 170static __attribute__((unused)) 171int 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 276static __attribute__((unused)) 277int 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 288static __attribute__((unused)) 289int 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 300static __attribute__((unused)) 301void 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 */