brintos

brintos / linux-shallow public Read only

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