File: fopencookie.c

package info (click to toggle)
html-xml-utils 7.7-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid
  • size: 2,488 kB
  • sloc: ansic: 11,213; sh: 7,996; lex: 243; makefile: 193; yacc: 125
file content (113 lines) | stat: -rw-r--r-- 3,370 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/* fopencookie.c -- implement GNU-style fopencookie() with BSD-style funopen()
 *
 * Todo: handle "a+" mode (probably by keeping read and write file pointers and
 * calling seek() before calling read or write)
 *
 * Todo: Is this solution, calling readfn() which in turn calls
 * sc->read() with some typecasts, better than ignoring the compiler
 * warnings and calling sc->read directly?
 *
 * Created: 22 August 2011
 * Author: Bert Bos <bert@w3.org>
 *
 * Copyright © 2011 World Wide Web Consortium
 * See http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
 */

#include "config.h"
#if HAVE_LIBCURL		/* We only need this if we're using libcurl */
#if !HAVE_FOPENCOOKIE		/* We don't need this on GNU Linux */

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "errexit.e"
#include "fopencookie.h"

typedef struct {
  cookie_read_function_t *read;
  cookie_write_function_t *write;
  cookie_seek_function_t *seek;
  cookie_close_function_t *close;
  void *cookie;
} cookiewrapper;


/* readfn -- callback that in turn calls sc->read with proper typecasts */
static int readfn(void *sc, char *buf, int n)
{
  cookiewrapper *c = (cookiewrapper*)sc;
  return (int)(c->read(c->cookie, buf, (size_t)n));
}

/* writefn -- callback that in turn calls sc->write with proper typecasts */
static int writefn(void *sc, const char *buf, int n)
{
  cookiewrapper *c = (cookiewrapper*)sc;
  return (int)(c->write(c->cookie, buf, (size_t)n));
}

/* seekfn -- callback that in turn calls sc->seek with proper typecasts */
static fpos_t seekfn(void *sc, fpos_t offset, int whence)
{
  cookiewrapper *c = (cookiewrapper*)sc;
  return (fpos_t)(c->seek(c->cookie, (off64_t)offset, whence));
}

/* closefn -- callback that in turn calls sc->close and then frees memory */
static int closefn(void *sc)
{
  cookiewrapper *c = (cookiewrapper*)sc;
  int r = c->close ? c->close(c->cookie) : 0;
  free(sc);
  return r;
}


/* fopencookie -- open a stream defined by four callback functions */
FILE *fopencookie(void *cookie, const char *mode,
			 cookie_io_functions_t funcs)
{
  cookiewrapper *s;
  int mask;			/* 1 = read, 2 = write, 4 = append */
  FILE *f;

  /* Check that the parameters make sense */
  if (!mode) {errno = EINVAL; return NULL;}

  if (mode[0] == 'r') mask = 1;
  else if (mode[0] == 'w') mask = 2;
  else if (mode[0] == 'a') mask = 4;
  else {errno = EINVAL; return NULL;}
  if (mode[1] == '+' || (mode[1] =='b' && mode[2] == '+')) mask |= 3;

  if ((mask & 1) && !funcs.read) {errno = EINVAL; return NULL;}
  if ((mask & 2) && !funcs.write) {errno = EINVAL; return NULL;}
  if ((mask & 4) && !funcs.seek) {errno = EINVAL; return NULL;}

  if (mask == 7) errexit("Bug: fopencookie() can't yet handle mode \"a+\"\n");

  /* Open the "file" */
  if (!(s = malloc(sizeof(*s)))) {errno = ENOMEM; return NULL;}
  s->read = funcs.read;
  s->write = funcs.write;
  s->seek = funcs.seek;
  s->close = funcs.close;
  s->cookie = cookie;

  f = funopen(s,
	      s->read ? readfn : NULL,
	      s->write ? writefn : NULL,
	      s->seek ? seekfn : NULL,
	      closefn);
  if (!f) {free(s); return NULL;}

  /* If the mode is "a" (append), position the file pointer at the end */
  if ((mask & 4) && fseek(f, 0L, SEEK_END) < 0) {fclose(f); return NULL;}

  return f;
}

#endif /* !HAVE_FOPENCOOKIE */
#endif /* HAVE_LIBCURL */