fopencookie.c raw

   1  #define _GNU_SOURCE
   2  #include "stdio_impl.h"
   3  #include <stdlib.h>
   4  #include <sys/ioctl.h>
   5  #include <fcntl.h>
   6  #include <errno.h>
   7  #include <string.h>
   8  
   9  struct fcookie {
  10  	void *cookie;
  11  	cookie_io_functions_t iofuncs;
  12  };
  13  
  14  struct cookie_FILE {
  15  	FILE f;
  16  	struct fcookie fc;
  17  	unsigned char buf[UNGET+BUFSIZ];
  18  };
  19  
  20  static size_t cookieread(FILE *f, unsigned char *buf, size_t len)
  21  {
  22  	struct fcookie *fc = f->cookie;
  23  	ssize_t ret = -1;
  24  	size_t remain = len, readlen = 0;
  25  	size_t len2 = len - !!f->buf_size;
  26  
  27  	if (!fc->iofuncs.read) goto bail;
  28  
  29  	if (len2) {
  30  		ret = fc->iofuncs.read(fc->cookie, (char *) buf, len2);
  31  		if (ret <= 0) goto bail;
  32  
  33  		readlen += ret;
  34  		remain -= ret;
  35  	}
  36  
  37  	if (!f->buf_size || remain > !!f->buf_size) return readlen;
  38  
  39  	f->rpos = f->buf;
  40  	ret = fc->iofuncs.read(fc->cookie, (char *) f->rpos, f->buf_size);
  41  	if (ret <= 0) goto bail;
  42  	f->rend = f->rpos + ret;
  43  
  44  	buf[readlen++] = *f->rpos++;
  45  
  46  	return readlen;
  47  
  48  bail:
  49  	f->flags |= ret == 0 ? F_EOF : F_ERR;
  50  	f->rpos = f->rend = f->buf;
  51  	return readlen;
  52  }
  53  
  54  static size_t cookiewrite(FILE *f, const unsigned char *buf, size_t len)
  55  {
  56  	struct fcookie *fc = f->cookie;
  57  	ssize_t ret;
  58  	size_t len2 = f->wpos - f->wbase;
  59  	if (!fc->iofuncs.write) return len;
  60  	if (len2) {
  61  		f->wpos = f->wbase;
  62  		if (cookiewrite(f, f->wpos, len2) < len2) return 0;
  63  	}
  64  	ret = fc->iofuncs.write(fc->cookie, (const char *) buf, len);
  65  	if (ret < 0) {
  66  		f->wpos = f->wbase = f->wend = 0;
  67  		f->flags |= F_ERR;
  68  		return 0;
  69  	}
  70  	return ret;
  71  }
  72  
  73  static off_t cookieseek(FILE *f, off_t off, int whence)
  74  {
  75  	struct fcookie *fc = f->cookie;
  76  	int res;
  77  	if (whence > 2U) {
  78  		errno = EINVAL;
  79  		return -1;
  80  	}
  81  	if (!fc->iofuncs.seek) {
  82  		errno = ENOTSUP;
  83  		return -1;
  84  	}
  85  	res = fc->iofuncs.seek(fc->cookie, &off, whence);
  86  	if (res < 0)
  87  		return res;
  88  	return off;
  89  }
  90  
  91  static int cookieclose(FILE *f)
  92  {
  93  	struct fcookie *fc = f->cookie;
  94  	if (fc->iofuncs.close) return fc->iofuncs.close(fc->cookie);
  95  	return 0;
  96  }
  97  
  98  FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t iofuncs)
  99  {
 100  	struct cookie_FILE *f;
 101  
 102  	/* Check for valid initial mode character */
 103  	if (!strchr("rwa", *mode)) {
 104  		errno = EINVAL;
 105  		return 0;
 106  	}
 107  
 108  	/* Allocate FILE+fcookie+buffer or fail */
 109  	if (!(f=malloc(sizeof *f))) return 0;
 110  
 111  	/* Zero-fill only the struct, not the buffer */
 112  	memset(&f->f, 0, sizeof f->f);
 113  
 114  	/* Impose mode restrictions */
 115  	if (!strchr(mode, '+')) f->f.flags = (*mode == 'r') ? F_NOWR : F_NORD;
 116  
 117  	/* Set up our fcookie */
 118  	f->fc.cookie = cookie;
 119  	f->fc.iofuncs = iofuncs;
 120  
 121  	f->f.fd = -1;
 122  	f->f.cookie = &f->fc;
 123  	f->f.buf = f->buf + UNGET;
 124  	f->f.buf_size = sizeof f->buf - UNGET;
 125  	f->f.lbf = EOF;
 126  
 127  	/* Initialize op ptrs. No problem if some are unneeded. */
 128  	f->f.read = cookieread;
 129  	f->f.write = cookiewrite;
 130  	f->f.seek = cookieseek;
 131  	f->f.close = cookieclose;
 132  
 133  	/* Add new FILE to open file list */
 134  	return __ofl_add(&f->f);
 135  }
 136