github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/dmz/nolibc/stdio.h (about)

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