summaryrefslogtreecommitdiff
path: root/paxlib/paxbuf.c
blob: d21505203d471865e80fcae8e5f4461b5bef1084 (plain)
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/* This file is part of GNU paxutils

   Copyright (C) 2005, 2007, 2023 Free Software Foundation, Inc.

   Written by Sergey Poznyakoff

   GNU paxutils is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 3, or (at your option) any later
   version.

   GNU paxutils is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
   Public License for more details.

   You should have received a copy of the GNU General Public License along
   with GNU paxutils; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <gettext.h>
#include <system.h>
#include <paxbuf.h>

/* PAX buffer structure */
struct pax_buffer
{
  size_t record_size;         /* Size of a record, bytes */
  size_t record_level;        /* Number of bytes stored in the record */
  size_t pos;                 /* Current position in buffer */
  char  *record;              /* Record buffer, record_size bytes long */

  int status;                 /* Return code from the latest I/O */
  
    /* I/O functions */
  paxbuf_io_fp writer;        /* Writes data */
  paxbuf_io_fp reader;        /* Reads data */
  paxbuf_seek_fp seek;        /* Seeks the underlying transport layer */
    /* Terminal functions */
  paxbuf_term_fp open;        /* Open a new volume */ 
  paxbuf_term_fp close;       /* Close the existing volume */
  paxbuf_destroy_fp destroy;  /* Destroy the closure data */ 
    /* Other callbacks */
  paxbuf_wrapper_fp wrapper;  /* Called when writer or reader returns EOF */

  void *closure;              /* Implementation-specific data */
  int mode;                   /* Working mode */
};


/* Default callbacks. Do nothing useful, except bailing out */

static void
noinit (const char *name)
{
  fprintf (stderr,
	   _("INTERNAL ERROR: %s is not initialized. Please, report.\n"),
	   name);
  exit (1);
}

static pax_io_status_t
default_reader (void *closure, void *data, size_t size, size_t *ret_size)
{
  noinit ("pax_buffer.reader");
  return pax_io_failure;
}

static pax_io_status_t
default_writer (void *closure, void *data, size_t size, size_t *ret_size)
{
  noinit ("pax_buffer.writer");
  return pax_io_failure;
}

static int
default_seek (void *closure, off_t offset)
{
  noinit ("pax_buffer.seek");
  return -1;
}

static int
default_open (void *closure, int mode)
{
  noinit ("pax_buffer.open");
  return -1;
}

static int
default_close (void *closure, int mode)
{
  noinit ("pax_buffer.close");
  return -1;
}

static int
default_destroy (void *closure)
{
  noinit ("pax_buffer.destroy");
  return -1;
}

static int
default_wrapper (void *closure)
{
  noinit ("pax_buffer.wrapper");
  return -1;
}

static const char *
default_error (void *closure)
{
  return strerror (errno);
}


/* Interface funtions */

/* 1. Initialize/destroy */

int
paxbuf_create (paxbuf_t *pbuf, int mode, void *closure, size_t record_size)
{
  paxbuf_t buf;

  buf = malloc (sizeof *buf);
  if (!buf)
    return ENOMEM;
  buf->record = malloc (record_size);
  if (!buf->record)
    {
      free (buf);
      return ENOMEM;
    }
  
  buf->record_size = record_size;
  buf->record_level = 0;
  buf->closure = closure;
  buf->mode = mode;
  
  paxbuf_set_io (buf, default_reader, default_writer, default_seek);
  paxbuf_set_term (buf, default_open, default_close, default_destroy);
  paxbuf_set_wrapper (buf, default_wrapper);
  
  *pbuf = buf;
  return 0;
}

void
paxbuf_destroy (paxbuf_t *pbuf)
{
  paxbuf_t buf = *pbuf;
  free (buf->record);
  if (buf->destroy)
    buf->destroy (buf->closure);
  free (buf);
  *pbuf = NULL;
}

void
paxbuf_set_io (paxbuf_t buf,
	       paxbuf_io_fp rd, paxbuf_io_fp wr, paxbuf_seek_fp seek)
{
  buf->writer = wr;
  buf->reader = rd;
  buf->seek = seek;
}

void
paxbuf_set_term (paxbuf_t buf,
		 paxbuf_term_fp open, paxbuf_term_fp close,
		 paxbuf_destroy_fp destroy)
{
  buf->open = open;
  buf->close = close;
  buf->destroy = destroy;
}

void
paxbuf_set_wrapper (paxbuf_t buf, paxbuf_wrapper_fp wrap)
{
  buf->wrapper = wrap;
}


/* 2. I/O operations and seek */

static pax_io_status_t
fill_buffer (paxbuf_t buf)
{
  pax_io_status_t status = pax_io_success;

  buf->record_level = 0;
  do
    {
      size_t s = 0;
      
      status = buf->reader (buf->closure, buf->record + buf->record_level,
			    buf->record_size - buf->record_level, &s);
      buf->record_level += s;
    }
  while ((status == pax_io_success && buf->record_level < buf->record_size)
	 || (status == pax_io_eof
	     && buf->wrapper
	     && buf->wrapper (buf->closure) == 0));

  buf->pos = 0;
  return status;
}

static pax_io_status_t
flush_buffer (paxbuf_t buf)
{
  pax_io_status_t status = pax_io_success;

  buf->record_level = 0;
  do
    {
      size_t s = 0;
      status = buf->writer (buf->closure, buf->record + buf->record_level,
			    buf->record_size - buf->record_level, &s);
      buf->record_level += s;
    }
  while ((status == pax_io_success && buf->record_level < buf->record_size)
	 || (status == pax_io_eof
	     && buf->wrapper
	     && buf->wrapper (buf->closure) == 0));
  buf->pos = 0;
  return status;
}

pax_io_status_t
paxbuf_read (paxbuf_t buf, char *data, size_t size, size_t *rsize)
{
  pax_io_status_t status = pax_io_success;

  *rsize = 0;
  while (size && status == pax_io_success)
    {
      size_t s;

      if (buf->pos == buf->record_level)
	{
	  status = fill_buffer (buf);
	  if (status == pax_io_failure)
	    break;
	}
      s = buf->record_level - buf->pos;
      if (s > size)
	s = size;
      memcpy (data, buf->record + buf->pos, s);
      data += s;
      buf->pos += s;
      size -= s;
      *rsize += s;
    }
  return status;
}

pax_io_status_t
paxbuf_write (paxbuf_t buf, char *data, size_t size, size_t *wsize)
{
  pax_io_status_t status = pax_io_success;

  *wsize = 0;
  while (size && status == pax_io_success)
    {
      size_t s;

      if (buf->pos == buf->record_size)
	{
	  status = flush_buffer (buf);
	  if (status == pax_io_failure)
	    break;
	}
      s = buf->record_size - buf->pos;
      if (s > size)
	s = size;
      memcpy (buf->record + buf->pos, data, s);
      data += s;
      buf->pos += s;
      size -= s;
      *wsize += s;
    }
  return status;
}

int
paxbuf_seek (paxbuf_t buf, off_t offset)
{
  /* FIXME */
  return buf->seek (buf->closure, offset);
}


/* 3. Open/close */
int
paxbuf_open (paxbuf_t buf)
{
  return buf->open (buf->closure, buf->mode);
}

int
paxbuf_close (paxbuf_t buf)
{
  pax_io_status_t status;
  if ((buf->mode & PAXBUF_WRITE) && buf->pos != 0)
    status = flush_buffer (buf);
  return buf->close (buf->closure, buf->mode) || status != pax_io_success;
}


/* Accessors */

void *
paxbuf_get_data (paxbuf_t buf)
{
  return buf->closure;
}

int
paxbuf_get_mode (paxbuf_t buf)
{
  return buf->mode;
}