1509a47dbSJiří Techet /*
2509a47dbSJiří Techet * MIO, an I/O abstraction layer replicating C file I/O API.
3509a47dbSJiří Techet * Copyright (C) 2010 Colomban Wendling <ban@herbesfolles.org>
4509a47dbSJiří Techet *
5509a47dbSJiří Techet * This program is free software; you can redistribute it and/or modify
6509a47dbSJiří Techet * it under the terms of the GNU General Public License as published by
7509a47dbSJiří Techet * the Free Software Foundation; either version 2 of the License, or
8509a47dbSJiří Techet * (at your option) any later version.
9509a47dbSJiří Techet *
10509a47dbSJiří Techet * This program is distributed in the hope that it will be useful,
11509a47dbSJiří Techet * but WITHOUT ANY WARRANTY; without even the implied warranty of
12509a47dbSJiří Techet * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13509a47dbSJiří Techet * GNU General Public License for more details.
14509a47dbSJiří Techet *
15509a47dbSJiří Techet * You should have received a copy of the GNU General Public License along
16509a47dbSJiří Techet * with this program; if not, write to the Free Software Foundation, Inc.,
17509a47dbSJiří Techet * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18509a47dbSJiří Techet *
19509a47dbSJiří Techet */
20509a47dbSJiří Techet
21667f4cd4SMasatake YAMATO #ifndef READTAGS_DSL
222db30dfeSJiří Techet #include "general.h" /* must always come first */
232db30dfeSJiří Techet
242db30dfeSJiří Techet #include "routines.h"
25dded9801SMasatake YAMATO #include "debug.h"
2664ae08e5SMasatake YAMATO #else
27ce990805SThomas Braun
28ce990805SThomas Braun #if defined (HAVE_CONFIG_H)
29ce990805SThomas Braun #include <config.h>
30ce990805SThomas Braun #endif
31ce990805SThomas Braun
3280f291cfSMasatake YAMATO #ifdef HAVE_STDBOOL_H
33ce990805SThomas Braun #include <stdbool.h>
34ce990805SThomas Braun #endif
35667f4cd4SMasatake YAMATO #endif /* READTAGS_DSL */
3674c3b045SMasatake YAMATO
3774c3b045SMasatake YAMATO #include "mio.h"
38509a47dbSJiří Techet
39509a47dbSJiří Techet #include <stdarg.h>
40509a47dbSJiří Techet #include <stdio.h>
41509a47dbSJiří Techet #include <string.h>
42509a47dbSJiří Techet #include <errno.h>
43509a47dbSJiří Techet #include <stdlib.h>
44509a47dbSJiří Techet #include <limits.h>
45509a47dbSJiří Techet
46*4a3ba775SMasatake YAMATO #ifndef _MSC_VER
47*4a3ba775SMasatake YAMATO #define MAY_HAVE_FTRUNCATE
48*4a3ba775SMasatake YAMATO #include <unistd.h>
49*4a3ba775SMasatake YAMATO #endif
50*4a3ba775SMasatake YAMATO
51667f4cd4SMasatake YAMATO #ifdef READTAGS_DSL
5274c3b045SMasatake YAMATO #define xMalloc(n,Type) (Type *)eMalloc((size_t)(n) * sizeof (Type))
5374c3b045SMasatake YAMATO #define xRealloc(p,n,Type) (Type *)eRealloc((p), (n) * sizeof (Type))
5474c3b045SMasatake YAMATO
eMalloc(const size_t size)5500ab700eSColomban Wendling static void *eMalloc (const size_t size)
5674c3b045SMasatake YAMATO {
5774c3b045SMasatake YAMATO void *buffer = malloc (size);
5874c3b045SMasatake YAMATO
5974c3b045SMasatake YAMATO if (buffer == NULL)
6074c3b045SMasatake YAMATO {
6174c3b045SMasatake YAMATO fprintf(stderr, "out of memory");
6274c3b045SMasatake YAMATO abort ();
6374c3b045SMasatake YAMATO }
6474c3b045SMasatake YAMATO
6574c3b045SMasatake YAMATO return buffer;
6674c3b045SMasatake YAMATO }
6774c3b045SMasatake YAMATO
eRealloc(void * const ptr,const size_t size)6800ab700eSColomban Wendling static void *eRealloc (void *const ptr, const size_t size)
6974c3b045SMasatake YAMATO {
7074c3b045SMasatake YAMATO void *buffer;
7174c3b045SMasatake YAMATO if (ptr == NULL)
7274c3b045SMasatake YAMATO buffer = eMalloc (size);
7374c3b045SMasatake YAMATO else
7474c3b045SMasatake YAMATO {
7574c3b045SMasatake YAMATO buffer = realloc (ptr, size);
7674c3b045SMasatake YAMATO if (buffer == NULL)
7774c3b045SMasatake YAMATO {
7874c3b045SMasatake YAMATO fprintf(stderr, "out of memory");
7974c3b045SMasatake YAMATO abort ();
8074c3b045SMasatake YAMATO }
8174c3b045SMasatake YAMATO }
8274c3b045SMasatake YAMATO return buffer;
8374c3b045SMasatake YAMATO }
8474c3b045SMasatake YAMATO
eFree(void * const ptr)8500ab700eSColomban Wendling static void eFree (void *const ptr)
8674c3b045SMasatake YAMATO {
8774c3b045SMasatake YAMATO free (ptr);
8874c3b045SMasatake YAMATO }
89a055e2beSMasatake YAMATO #define eFreeNoNullCheck eFree
9074c3b045SMasatake YAMATO
91866f617fSColomban Wendling # define Assert(c) do {} while(0)
92866f617fSColomban Wendling # define AssertNotReached() do {} while(0)
93667f4cd4SMasatake YAMATO #endif /* READTAGS_DSL */
9474c3b045SMasatake YAMATO
95509a47dbSJiří Techet /* minimal reallocation chunk size */
96509a47dbSJiří Techet #define MIO_CHUNK_SIZE 4096
97509a47dbSJiří Techet
98509a47dbSJiří Techet #define MAX(a, b) (((a) > (b)) ? (a) : (b))
99509a47dbSJiří Techet
100509a47dbSJiří Techet
101509a47dbSJiří Techet /**
102509a47dbSJiří Techet * SECTION:mio
103509a47dbSJiří Techet * @short_description: The MIO object
104509a47dbSJiří Techet * @include: mio/mio.h
105509a47dbSJiří Techet *
106509a47dbSJiří Techet * The #MIO object replicates the C file I/O API with support of both standard
107509a47dbSJiří Techet * file based operations and in-memory operations. Its goal is to ease the port
108509a47dbSJiří Techet * of an application that uses C file I/O API to perform in-memory operations.
109509a47dbSJiří Techet *
110e1ef8f28SMasatake YAMATO * A #MIO object is created using mio_new_file(), mio_new_memory() or mio_new_mio(),
11139ef3738SMasatake YAMATO * depending on whether you want file or in-memory operations.
11239ef3738SMasatake YAMATO * Its life is managed by reference counting. Just after calling one of functions
113b978efd6SMasatake YAMATO * for creating, the count is 1. mio_ref() increments the counter. mio_unref()
11439ef3738SMasatake YAMATO * decrements it. When the counter becomes 0, the #MIO object will be destroyed
115b978efd6SMasatake YAMATO * in mio_unref(). There is also some other convenient API to create file-based
116509a47dbSJiří Techet * #MIO objects for more complex cases, such as mio_new_file_full() and
117509a47dbSJiří Techet * mio_new_fp().
118509a47dbSJiří Techet *
119509a47dbSJiří Techet * Once the #MIO object is created, you can perform standard I/O operations on
120509a47dbSJiří Techet * it transparently without the need to care about the effective underlying
121509a47dbSJiří Techet * operations.
122509a47dbSJiří Techet *
123509a47dbSJiří Techet * The I/O API is almost exactly a replication of the standard C file I/O API
124509a47dbSJiří Techet * with the significant difference that the first parameter is always the #MIO
125509a47dbSJiří Techet * object to work on.
126509a47dbSJiří Techet */
127509a47dbSJiří Techet
128509a47dbSJiří Techet
129bf6da614SMasatake YAMATO typedef struct _MIOUserData MIOUserData;
130bf6da614SMasatake YAMATO struct _MIOUserData {
131bf6da614SMasatake YAMATO void *d;
132bf6da614SMasatake YAMATO MIODestroyNotify f;
133bf6da614SMasatake YAMATO };
134bf6da614SMasatake YAMATO
135509a47dbSJiří Techet /**
1361d7aeadfSMasatake YAMATO * MIO:
1371d7aeadfSMasatake YAMATO *
1381d7aeadfSMasatake YAMATO * An object representing a #MIO stream. No assumptions should be made about
1391d7aeadfSMasatake YAMATO * what compose this object, and none of its fields should be accessed directly.
1401d7aeadfSMasatake YAMATO */
1411d7aeadfSMasatake YAMATO struct _MIO {
1421d7aeadfSMasatake YAMATO /*< private >*/
1431d7aeadfSMasatake YAMATO MIOType type;
1441d7aeadfSMasatake YAMATO unsigned int refcount;
1451d7aeadfSMasatake YAMATO union {
1461d7aeadfSMasatake YAMATO struct {
1471d7aeadfSMasatake YAMATO FILE *fp;
1481d7aeadfSMasatake YAMATO MIOFCloseFunc close_func;
1491d7aeadfSMasatake YAMATO } file;
1501d7aeadfSMasatake YAMATO struct {
1511d7aeadfSMasatake YAMATO unsigned char *buf;
1521d7aeadfSMasatake YAMATO int ungetch;
1531d7aeadfSMasatake YAMATO size_t pos;
1541d7aeadfSMasatake YAMATO size_t size;
1551d7aeadfSMasatake YAMATO size_t allocated_size;
1561d7aeadfSMasatake YAMATO MIOReallocFunc realloc_func;
1571d7aeadfSMasatake YAMATO MIODestroyNotify free_func;
158ce990805SThomas Braun bool error;
159ce990805SThomas Braun bool eof;
1601d7aeadfSMasatake YAMATO } mem;
1611d7aeadfSMasatake YAMATO } impl;
162bf6da614SMasatake YAMATO MIOUserData udata;
1631d7aeadfSMasatake YAMATO };
1641d7aeadfSMasatake YAMATO
1651d7aeadfSMasatake YAMATO
1661d7aeadfSMasatake YAMATO /**
167509a47dbSJiří Techet * mio_new_file_full:
168509a47dbSJiří Techet * @filename: Filename to open, passed as-is to @open_func as the first argument
169509a47dbSJiří Techet * @mode: Mode in which open the file, passed as-is to @open_func as the second
170509a47dbSJiří Techet * argument
171509a47dbSJiří Techet * @open_func: A function with the fopen() semantic to use to open the file
172509a47dbSJiří Techet * @close_func: A function with the fclose() semantic to close the file when
173509a47dbSJiří Techet * the #MIO object is destroyed, or %NULL not to close the #FILE
174509a47dbSJiří Techet * object
175509a47dbSJiří Techet *
176509a47dbSJiří Techet * Creates a new #MIO object working on a file, from a filename and an opening
177509a47dbSJiří Techet * function. See also mio_new_file().
178509a47dbSJiří Techet *
179509a47dbSJiří Techet * This function is generally overkill and mio_new_file() should often be used
180509a47dbSJiří Techet * instead, but it allows to specify a custom function to open a file, as well
181509a47dbSJiří Techet * as a close function. The former is useful e.g. if you need to wrap fopen()
182509a47dbSJiří Techet * for some reason (like filename encoding conversion for example), and the
183509a47dbSJiří Techet * latter allows you both to match your custom open function and to choose
184b978efd6SMasatake YAMATO * whether the underlying #FILE object should or not be closed when mio_unref()
185509a47dbSJiří Techet * is called on the returned object.
186509a47dbSJiří Techet *
187b978efd6SMasatake YAMATO * Free-function: mio_unref()
188509a47dbSJiří Techet *
189509a47dbSJiří Techet * Returns: A new #MIO on success, or %NULL on failure.
190509a47dbSJiří Techet */
mio_new_file_full(const char * filename,const char * mode,MIOFOpenFunc open_func,MIOFCloseFunc close_func)191509a47dbSJiří Techet MIO *mio_new_file_full (const char *filename,
192509a47dbSJiří Techet const char *mode,
193509a47dbSJiří Techet MIOFOpenFunc open_func,
194509a47dbSJiří Techet MIOFCloseFunc close_func)
195509a47dbSJiří Techet {
196509a47dbSJiří Techet MIO *mio;
197509a47dbSJiří Techet
198509a47dbSJiří Techet /* we need to create the MIO object first, because we may not be able to close
199509a47dbSJiří Techet * the opened file if the user passed NULL as the close function, which means
200509a47dbSJiří Techet * that everything must succeed if we've opened the file successfully */
2012db30dfeSJiří Techet mio = xMalloc (1, MIO);
202509a47dbSJiří Techet if (mio)
203509a47dbSJiří Techet {
204509a47dbSJiří Techet FILE *fp = open_func (filename, mode);
205509a47dbSJiří Techet
206509a47dbSJiří Techet if (! fp)
207509a47dbSJiří Techet {
2082db30dfeSJiří Techet eFree (mio);
209509a47dbSJiří Techet mio = NULL;
210509a47dbSJiří Techet }
211509a47dbSJiří Techet else
212509a47dbSJiří Techet {
213509a47dbSJiří Techet mio->type = MIO_TYPE_FILE;
214509a47dbSJiří Techet mio->impl.file.fp = fp;
215509a47dbSJiří Techet mio->impl.file.close_func = close_func;
21639ef3738SMasatake YAMATO mio->refcount = 1;
217bf6da614SMasatake YAMATO mio->udata.d = NULL;
218bf6da614SMasatake YAMATO mio->udata.f = NULL;
219509a47dbSJiří Techet }
220509a47dbSJiří Techet }
221509a47dbSJiří Techet
222509a47dbSJiří Techet return mio;
223509a47dbSJiří Techet }
224509a47dbSJiří Techet
225509a47dbSJiří Techet /**
226509a47dbSJiří Techet * mio_new_file:
227509a47dbSJiří Techet * @filename: Filename to open, same as the fopen()'s first argument
228509a47dbSJiří Techet * @mode: Mode in which open the file, fopen()'s second argument
229509a47dbSJiří Techet *
230509a47dbSJiří Techet * Creates a new #MIO object working on a file from a filename; wrapping
231509a47dbSJiří Techet * fopen().
232509a47dbSJiří Techet * This function simply calls mio_new_file_full() with the libc's fopen() and
233509a47dbSJiří Techet * fclose() functions.
234509a47dbSJiří Techet *
235b978efd6SMasatake YAMATO * Free-function: mio_unref()
236509a47dbSJiří Techet *
237509a47dbSJiří Techet * Returns: A new #MIO on success, or %NULL on failure.
238509a47dbSJiří Techet */
mio_new_file(const char * filename,const char * mode)239509a47dbSJiří Techet MIO *mio_new_file (const char *filename, const char *mode)
240509a47dbSJiří Techet {
241509a47dbSJiří Techet return mio_new_file_full (filename, mode, fopen, fclose);
242509a47dbSJiří Techet }
243509a47dbSJiří Techet
244509a47dbSJiří Techet /**
245509a47dbSJiří Techet * mio_new_fp:
246509a47dbSJiří Techet * @fp: An opened #FILE object
247509a47dbSJiří Techet * @close_func: (allow-none): Function used to close @fp when the #MIO object
248509a47dbSJiří Techet * gets destroyed, or %NULL not to close the #FILE object
249509a47dbSJiří Techet *
250509a47dbSJiří Techet * Creates a new #MIO object working on a file, from an already opened #FILE
251509a47dbSJiří Techet * object.
252509a47dbSJiří Techet *
253509a47dbSJiří Techet * <example>
254509a47dbSJiří Techet * <title>Typical use of this function</title>
255509a47dbSJiří Techet * <programlisting>
256509a47dbSJiří Techet * MIO *mio = mio_new_fp (fp, fclose);
257509a47dbSJiří Techet * </programlisting>
258509a47dbSJiří Techet * </example>
259509a47dbSJiří Techet *
260b978efd6SMasatake YAMATO * Free-function: mio_unref()
261509a47dbSJiří Techet *
262509a47dbSJiří Techet * Returns: A new #MIO on success or %NULL on failure.
263509a47dbSJiří Techet */
mio_new_fp(FILE * fp,MIOFCloseFunc close_func)264509a47dbSJiří Techet MIO *mio_new_fp (FILE *fp, MIOFCloseFunc close_func)
265509a47dbSJiří Techet {
266509a47dbSJiří Techet MIO *mio;
267509a47dbSJiří Techet
268509a47dbSJiří Techet if (!fp)
269509a47dbSJiří Techet return NULL;
270509a47dbSJiří Techet
2712db30dfeSJiří Techet mio = xMalloc (1, MIO);
272509a47dbSJiří Techet if (mio)
273509a47dbSJiří Techet {
274509a47dbSJiří Techet mio->type = MIO_TYPE_FILE;
275509a47dbSJiří Techet mio->impl.file.fp = fp;
276509a47dbSJiří Techet mio->impl.file.close_func = close_func;
27739ef3738SMasatake YAMATO mio->refcount = 1;
278bf6da614SMasatake YAMATO mio->udata.d = NULL;
279bf6da614SMasatake YAMATO mio->udata.f = NULL;
280509a47dbSJiří Techet }
281509a47dbSJiří Techet
282509a47dbSJiří Techet return mio;
283509a47dbSJiří Techet }
284509a47dbSJiří Techet
285509a47dbSJiří Techet /**
286509a47dbSJiří Techet * mio_new_memory:
287509a47dbSJiří Techet * @data: Initial data (may be %NULL)
288509a47dbSJiří Techet * @size: Length of @data in bytes
289509a47dbSJiří Techet * @realloc_func: A function with the realloc() semantic used to grow the
290509a47dbSJiří Techet * buffer, or %NULL to disable buffer growing
291509a47dbSJiří Techet * @free_func: A function with the free() semantic to destroy the data together
292509a47dbSJiří Techet * with the object, or %NULL not to destroy the data
293509a47dbSJiří Techet *
294509a47dbSJiří Techet * Creates a new #MIO object working on memory.
295509a47dbSJiří Techet *
296509a47dbSJiří Techet * To allow the buffer to grow, you must provide a @realloc_func, otherwise
297509a47dbSJiří Techet * trying to write after the end of the current data will fail.
298509a47dbSJiří Techet *
299509a47dbSJiří Techet * If you want the buffer to be freed together with the #MIO object, you must
300509a47dbSJiří Techet * give a @free_func; otherwise the data will still live after #MIO object
301509a47dbSJiří Techet * termination.
302509a47dbSJiří Techet *
303509a47dbSJiří Techet * <example>
304509a47dbSJiří Techet * <title>Basic creation of a non-growable, freeable #MIO object</title>
305509a47dbSJiří Techet * <programlisting>
306509a47dbSJiří Techet * MIO *mio = mio_new_memory (data, size, NULL, g_free);
307509a47dbSJiří Techet * </programlisting>
308509a47dbSJiří Techet * </example>
309509a47dbSJiří Techet *
310509a47dbSJiří Techet * <example>
311509a47dbSJiří Techet * <title>Basic creation of an empty growable and freeable #MIO object</title>
312509a47dbSJiří Techet * <programlisting>
313509a47dbSJiří Techet * MIO *mio = mio_new_memory (NULL, 0, g_try_realloc, g_free);
314509a47dbSJiří Techet * </programlisting>
315509a47dbSJiří Techet * </example>
316509a47dbSJiří Techet *
317b978efd6SMasatake YAMATO * Free-function: mio_unref()
318509a47dbSJiří Techet *
319509a47dbSJiří Techet * Returns: A new #MIO on success, or %NULL on failure.
320509a47dbSJiří Techet */
mio_new_memory(unsigned char * data,size_t size,MIOReallocFunc realloc_func,MIODestroyNotify free_func)321509a47dbSJiří Techet MIO *mio_new_memory (unsigned char *data,
322509a47dbSJiří Techet size_t size,
323509a47dbSJiří Techet MIOReallocFunc realloc_func,
324509a47dbSJiří Techet MIODestroyNotify free_func)
325509a47dbSJiří Techet {
326509a47dbSJiří Techet MIO *mio;
327509a47dbSJiří Techet
3282db30dfeSJiří Techet mio = xMalloc (1, MIO);
329509a47dbSJiří Techet if (mio)
330509a47dbSJiří Techet {
331509a47dbSJiří Techet mio->type = MIO_TYPE_MEMORY;
332509a47dbSJiří Techet mio->impl.mem.buf = data;
333509a47dbSJiří Techet mio->impl.mem.ungetch = EOF;
334509a47dbSJiří Techet mio->impl.mem.pos = 0;
335509a47dbSJiří Techet mio->impl.mem.size = size;
336509a47dbSJiří Techet mio->impl.mem.allocated_size = size;
337509a47dbSJiří Techet mio->impl.mem.realloc_func = realloc_func;
338509a47dbSJiří Techet mio->impl.mem.free_func = free_func;
339ce990805SThomas Braun mio->impl.mem.eof = false;
340ce990805SThomas Braun mio->impl.mem.error = false;
34139ef3738SMasatake YAMATO mio->refcount = 1;
342bf6da614SMasatake YAMATO mio->udata.d = NULL;
343bf6da614SMasatake YAMATO mio->udata.f = NULL;
344509a47dbSJiří Techet }
345509a47dbSJiří Techet
346509a47dbSJiří Techet return mio;
347509a47dbSJiří Techet }
348509a47dbSJiří Techet
349509a47dbSJiří Techet /**
350e1ef8f28SMasatake YAMATO * mio_new_mio:
351e1ef8f28SMasatake YAMATO * @base: The original mio
352e1ef8f28SMasatake YAMATO * @start: stream offset of the @base where new mio starts
353e1ef8f28SMasatake YAMATO * @size: the length of the data copied from @base to new mio
354e1ef8f28SMasatake YAMATO *
355e1ef8f28SMasatake YAMATO * Creates a new #MIO object by copying data from existing #MIO (@base).
356915d6979SJiří Techet * The range for copying is given with @start and @size.
357e1ef8f28SMasatake YAMATO * Copying data at the range from @start to the end of @base is
358915d6979SJiří Techet * done if -1 is given as @size.
359e1ef8f28SMasatake YAMATO *
360915d6979SJiří Techet * If @size is larger than the length from @start to the end of
361915d6979SJiří Techet * @base, %NULL is returned.
362e1ef8f28SMasatake YAMATO *
363e1ef8f28SMasatake YAMATO * The function doesn't move the file position of @base.
364e1ef8f28SMasatake YAMATO *
365b978efd6SMasatake YAMATO * Free-function: mio_unref()
366e1ef8f28SMasatake YAMATO *
367e1ef8f28SMasatake YAMATO */
368e1ef8f28SMasatake YAMATO
mio_new_mio(MIO * base,long start,long size)369915d6979SJiří Techet MIO *mio_new_mio (MIO *base, long start, long size)
370e1ef8f28SMasatake YAMATO {
371e1ef8f28SMasatake YAMATO unsigned char *data;
372e1ef8f28SMasatake YAMATO long original_pos;
373e1ef8f28SMasatake YAMATO MIO *submio;
374e1ef8f28SMasatake YAMATO size_t r;
375e1ef8f28SMasatake YAMATO
376e1ef8f28SMasatake YAMATO original_pos = mio_tell (base);
377e1ef8f28SMasatake YAMATO
378915d6979SJiří Techet if (size == -1)
379e1ef8f28SMasatake YAMATO {
380e1ef8f28SMasatake YAMATO long end;
381e1ef8f28SMasatake YAMATO
382e1ef8f28SMasatake YAMATO if (mio_seek (base, 0, SEEK_END) != 0)
383e1ef8f28SMasatake YAMATO return NULL;
384e1ef8f28SMasatake YAMATO end = mio_tell (base);
3855b666401SMasatake YAMATO Assert (end >= start);
386e1ef8f28SMasatake YAMATO size = end - start;
387e1ef8f28SMasatake YAMATO }
388e1ef8f28SMasatake YAMATO
389e1ef8f28SMasatake YAMATO if (mio_seek (base, start, SEEK_SET) != 0)
390e1ef8f28SMasatake YAMATO return NULL;
391e1ef8f28SMasatake YAMATO
392e1ef8f28SMasatake YAMATO data = xMalloc (size, unsigned char);
393e1ef8f28SMasatake YAMATO r= mio_read (base, data, 1, size);
394e1ef8f28SMasatake YAMATO mio_seek (base, original_pos, SEEK_SET);
395e1ef8f28SMasatake YAMATO
396e1ef8f28SMasatake YAMATO if (r != size)
397e1ef8f28SMasatake YAMATO goto cleanup;
398e1ef8f28SMasatake YAMATO
399a055e2beSMasatake YAMATO submio = mio_new_memory (data, size, eRealloc, eFreeNoNullCheck);
400e1ef8f28SMasatake YAMATO if (! submio)
401e1ef8f28SMasatake YAMATO goto cleanup;
402e1ef8f28SMasatake YAMATO
403e1ef8f28SMasatake YAMATO return submio;
404e1ef8f28SMasatake YAMATO
405e1ef8f28SMasatake YAMATO cleanup:
406e1ef8f28SMasatake YAMATO eFree (data);
407e1ef8f28SMasatake YAMATO return NULL;
408e1ef8f28SMasatake YAMATO }
409e1ef8f28SMasatake YAMATO
410e1ef8f28SMasatake YAMATO /**
41139ef3738SMasatake YAMATO * mio_ref:
41239ef3738SMasatake YAMATO * @mio: A #MIO object
41339ef3738SMasatake YAMATO *
41439ef3738SMasatake YAMATO * Increments the reference counter of a #MIO.
41539ef3738SMasatake YAMATO *
41639ef3738SMasatake YAMATO * Returns: passed @mio.
41739ef3738SMasatake YAMATO */
mio_ref(MIO * mio)41839ef3738SMasatake YAMATO MIO *mio_ref (MIO *mio)
41939ef3738SMasatake YAMATO {
42039ef3738SMasatake YAMATO mio->refcount++;
42139ef3738SMasatake YAMATO return mio;
42239ef3738SMasatake YAMATO }
42339ef3738SMasatake YAMATO
42439ef3738SMasatake YAMATO /**
425509a47dbSJiří Techet * mio_file_get_fp:
426509a47dbSJiří Techet * @mio: A #MIO object
427509a47dbSJiří Techet *
428509a47dbSJiří Techet * Gets the underlying #FILE object associated with a #MIO file stream.
429509a47dbSJiří Techet *
430509a47dbSJiří Techet * <warning><para>The returned object may become invalid after a call to
431b978efd6SMasatake YAMATO * mio_unref() if the stream was configured to close the file when
432509a47dbSJiří Techet * destroyed.</para></warning>
433509a47dbSJiří Techet *
434509a47dbSJiří Techet * Returns: The underlying #FILE object of the given stream, or %NULL if the
435509a47dbSJiří Techet * stream is not a file stream.
436509a47dbSJiří Techet */
mio_file_get_fp(MIO * mio)437509a47dbSJiří Techet FILE *mio_file_get_fp (MIO *mio)
438509a47dbSJiří Techet {
439509a47dbSJiří Techet FILE *fp = NULL;
440509a47dbSJiří Techet
441509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
442509a47dbSJiří Techet fp = mio->impl.file.fp;
443509a47dbSJiří Techet
444509a47dbSJiří Techet return fp;
445509a47dbSJiří Techet }
446509a47dbSJiří Techet
447509a47dbSJiří Techet /**
448509a47dbSJiří Techet * mio_memory_get_data:
449509a47dbSJiří Techet * @mio: A #MIO object
450509a47dbSJiří Techet * @size: (allow-none) (out): Return location for the length of the returned
451509a47dbSJiří Techet * memory, or %NULL
452509a47dbSJiří Techet *
453509a47dbSJiří Techet * Gets the underlying memory buffer associated with a #MIO memory stream.
454509a47dbSJiří Techet *
455509a47dbSJiří Techet * <warning><para>The returned pointer and size may become invalid after a
456b978efd6SMasatake YAMATO * successful write on the stream or after a call to mio_unref() if the stream
457509a47dbSJiří Techet * was configured to free the memory when destroyed.</para></warning>
458509a47dbSJiří Techet *
459509a47dbSJiří Techet * Returns: The memory buffer of the given #MIO stream, or %NULL if the stream
460509a47dbSJiří Techet * is not a memory stream.
461509a47dbSJiří Techet */
mio_memory_get_data(MIO * mio,size_t * size)462509a47dbSJiří Techet unsigned char *mio_memory_get_data (MIO *mio, size_t *size)
463509a47dbSJiří Techet {
464509a47dbSJiří Techet unsigned char *ptr = NULL;
465509a47dbSJiří Techet
466509a47dbSJiří Techet if (mio->type == MIO_TYPE_MEMORY)
467509a47dbSJiří Techet {
468509a47dbSJiří Techet ptr = mio->impl.mem.buf;
469509a47dbSJiří Techet if (size)
470509a47dbSJiří Techet *size = mio->impl.mem.size;
471509a47dbSJiří Techet }
472509a47dbSJiří Techet
473509a47dbSJiří Techet return ptr;
474509a47dbSJiří Techet }
475509a47dbSJiří Techet
476509a47dbSJiří Techet /**
477b978efd6SMasatake YAMATO * mio_unref:
478509a47dbSJiří Techet * @mio: A #MIO object
479509a47dbSJiří Techet *
48039ef3738SMasatake YAMATO * Decrements the reference counter of a #MIO and destroys the #MIO
48139ef3738SMasatake YAMATO * object if its counter becomes 0.
482509a47dbSJiří Techet *
483509a47dbSJiří Techet * Returns: Error code obtained from the registered MIOFCloseFunc or 0 on success.
484509a47dbSJiří Techet */
mio_unref(MIO * mio)485b978efd6SMasatake YAMATO int mio_unref (MIO *mio)
486509a47dbSJiří Techet {
487509a47dbSJiří Techet int rv = 0;
488509a47dbSJiří Techet
489509a47dbSJiří Techet if (mio)
490509a47dbSJiří Techet {
49139ef3738SMasatake YAMATO if (--mio->refcount)
49239ef3738SMasatake YAMATO return 0;
49339ef3738SMasatake YAMATO
494bf6da614SMasatake YAMATO if (mio->udata.d && mio->udata.f)
495bf6da614SMasatake YAMATO mio->udata.f (mio->udata.d);
496bf6da614SMasatake YAMATO
497509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
498509a47dbSJiří Techet {
499509a47dbSJiří Techet if (mio->impl.file.close_func)
500509a47dbSJiří Techet rv = mio->impl.file.close_func (mio->impl.file.fp);
501509a47dbSJiří Techet mio->impl.file.close_func = NULL;
502509a47dbSJiří Techet mio->impl.file.fp = NULL;
503509a47dbSJiří Techet }
504dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
505509a47dbSJiří Techet {
506509a47dbSJiří Techet if (mio->impl.mem.free_func)
507509a47dbSJiří Techet mio->impl.mem.free_func (mio->impl.mem.buf);
508509a47dbSJiří Techet mio->impl.mem.buf = NULL;
509509a47dbSJiří Techet mio->impl.mem.pos = 0;
510509a47dbSJiří Techet mio->impl.mem.size = 0;
511509a47dbSJiří Techet mio->impl.mem.allocated_size = 0;
512509a47dbSJiří Techet mio->impl.mem.realloc_func = NULL;
513509a47dbSJiří Techet mio->impl.mem.free_func = NULL;
514ce990805SThomas Braun mio->impl.mem.eof = false;
515ce990805SThomas Braun mio->impl.mem.error = false;
516509a47dbSJiří Techet }
517dded9801SMasatake YAMATO else
518dded9801SMasatake YAMATO AssertNotReached ();
519509a47dbSJiří Techet
5202db30dfeSJiří Techet eFree (mio);
521509a47dbSJiří Techet }
522509a47dbSJiří Techet
523509a47dbSJiří Techet return rv;
524509a47dbSJiří Techet }
525509a47dbSJiří Techet
526509a47dbSJiří Techet /**
527509a47dbSJiří Techet * mio_read:
528509a47dbSJiří Techet * @mio: A #MIO object
529509a47dbSJiří Techet * @ptr: Pointer to the memory to fill with the read data
530509a47dbSJiří Techet * @size: Size of each block to read
531509a47dbSJiří Techet * @nmemb: Number o blocks to read
532509a47dbSJiří Techet *
533509a47dbSJiří Techet * Reads raw data from a #MIO stream. This function behave the same as fread().
534509a47dbSJiří Techet *
535509a47dbSJiří Techet * Returns: The number of actually read blocks. If an error occurs or if the
536509a47dbSJiří Techet * end of the stream is reached, the return value may be smaller than
537509a47dbSJiří Techet * the requested block count, or even 0. This function doesn't
538509a47dbSJiří Techet * distinguish between end-of-stream and an error, you should then use
539509a47dbSJiří Techet * mio_eof() and mio_error() to determine which occurred.
540509a47dbSJiří Techet */
mio_read(MIO * mio,void * ptr_,size_t size,size_t nmemb)541509a47dbSJiří Techet size_t mio_read (MIO *mio,
542509a47dbSJiří Techet void *ptr_,
543509a47dbSJiří Techet size_t size,
544509a47dbSJiří Techet size_t nmemb)
545509a47dbSJiří Techet {
546509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
547509a47dbSJiří Techet return fread (ptr_, size, nmemb, mio->impl.file.fp);
548dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
549509a47dbSJiří Techet {
550509a47dbSJiří Techet size_t n_read = 0;
551509a47dbSJiří Techet
552509a47dbSJiří Techet if (size != 0 && nmemb != 0)
553509a47dbSJiří Techet {
554509a47dbSJiří Techet size_t size_avail = mio->impl.mem.size - mio->impl.mem.pos;
555509a47dbSJiří Techet size_t copy_bytes = size * nmemb;
556509a47dbSJiří Techet unsigned char *ptr = ptr_;
557509a47dbSJiří Techet
558509a47dbSJiří Techet if (size_avail < copy_bytes)
559509a47dbSJiří Techet copy_bytes = size_avail;
560509a47dbSJiří Techet
561509a47dbSJiří Techet if (copy_bytes > 0)
562509a47dbSJiří Techet {
563509a47dbSJiří Techet n_read = copy_bytes / size;
564509a47dbSJiří Techet
565509a47dbSJiří Techet if (mio->impl.mem.ungetch != EOF)
566509a47dbSJiří Techet {
567509a47dbSJiří Techet *ptr = (unsigned char) mio->impl.mem.ungetch;
568509a47dbSJiří Techet mio->impl.mem.ungetch = EOF;
569509a47dbSJiří Techet copy_bytes--;
570509a47dbSJiří Techet mio->impl.mem.pos++;
571509a47dbSJiří Techet ptr++;
572509a47dbSJiří Techet }
573509a47dbSJiří Techet
574509a47dbSJiří Techet memcpy (ptr, &mio->impl.mem.buf[mio->impl.mem.pos], copy_bytes);
575509a47dbSJiří Techet mio->impl.mem.pos += copy_bytes;
576509a47dbSJiří Techet }
577509a47dbSJiří Techet if (mio->impl.mem.pos >= mio->impl.mem.size)
578ce990805SThomas Braun mio->impl.mem.eof = true;
579509a47dbSJiří Techet }
580509a47dbSJiří Techet
581509a47dbSJiří Techet return n_read;
582509a47dbSJiří Techet }
583dded9801SMasatake YAMATO else
584dded9801SMasatake YAMATO {
585dded9801SMasatake YAMATO AssertNotReached ();
586dded9801SMasatake YAMATO return 0;
587dded9801SMasatake YAMATO }
588509a47dbSJiří Techet }
589509a47dbSJiří Techet
590509a47dbSJiří Techet /*
591509a47dbSJiří Techet * mem_try_resize:
592509a47dbSJiří Techet * @mio: A #MIO object of the type %MIO_TYPE_MEMORY
593509a47dbSJiří Techet * @new_size: Requested new size
594509a47dbSJiří Techet *
595509a47dbSJiří Techet * Tries to resize the underlying buffer of an in-memory #MIO object.
596509a47dbSJiří Techet * This supports both growing and shrinking.
597509a47dbSJiří Techet *
598ce990805SThomas Braun * Returns: %true on success, %false otherwise.
599509a47dbSJiří Techet */
mem_try_resize(MIO * mio,size_t new_size)600509a47dbSJiří Techet static int mem_try_resize (MIO *mio, size_t new_size)
601509a47dbSJiří Techet {
602ce990805SThomas Braun int success = false;
603509a47dbSJiří Techet
604509a47dbSJiří Techet if (mio->impl.mem.realloc_func)
605509a47dbSJiří Techet {
606509a47dbSJiří Techet if (new_size == ULONG_MAX)
607509a47dbSJiří Techet {
608509a47dbSJiří Techet #ifdef EOVERFLOW
609509a47dbSJiří Techet errno = EOVERFLOW;
610509a47dbSJiří Techet #endif
611509a47dbSJiří Techet }
612509a47dbSJiří Techet else
613509a47dbSJiří Techet {
614509a47dbSJiří Techet if (new_size > mio->impl.mem.size)
615509a47dbSJiří Techet {
616509a47dbSJiří Techet if (new_size <= mio->impl.mem.allocated_size)
617509a47dbSJiří Techet {
618509a47dbSJiří Techet mio->impl.mem.size = new_size;
619ce990805SThomas Braun success = true;
620509a47dbSJiří Techet }
621509a47dbSJiří Techet else
622509a47dbSJiří Techet {
623509a47dbSJiří Techet size_t newsize;
624509a47dbSJiří Techet unsigned char *newbuf;
625509a47dbSJiří Techet
626509a47dbSJiří Techet newsize = MAX (mio->impl.mem.allocated_size + MIO_CHUNK_SIZE,
627509a47dbSJiří Techet new_size);
628509a47dbSJiří Techet newbuf = mio->impl.mem.realloc_func (mio->impl.mem.buf, newsize);
629509a47dbSJiří Techet if (newbuf)
630509a47dbSJiří Techet {
631509a47dbSJiří Techet mio->impl.mem.buf = newbuf;
632509a47dbSJiří Techet mio->impl.mem.allocated_size = newsize;
633509a47dbSJiří Techet mio->impl.mem.size = new_size;
634ce990805SThomas Braun success = true;
635509a47dbSJiří Techet }
636509a47dbSJiří Techet }
637509a47dbSJiří Techet }
638509a47dbSJiří Techet else
639509a47dbSJiří Techet {
640509a47dbSJiří Techet unsigned char *newbuf;
641509a47dbSJiří Techet
642509a47dbSJiří Techet newbuf = mio->impl.mem.realloc_func (mio->impl.mem.buf, new_size);
643509a47dbSJiří Techet if (newbuf || new_size == 0)
644509a47dbSJiří Techet {
645509a47dbSJiří Techet mio->impl.mem.buf = newbuf;
646509a47dbSJiří Techet mio->impl.mem.allocated_size = new_size;
647509a47dbSJiří Techet mio->impl.mem.size = new_size;
648ce990805SThomas Braun success = true;
649509a47dbSJiří Techet }
650509a47dbSJiří Techet }
651509a47dbSJiří Techet }
652509a47dbSJiří Techet }
653509a47dbSJiří Techet
654509a47dbSJiří Techet return success;
655509a47dbSJiří Techet }
656509a47dbSJiří Techet
file_try_resize(MIO * mio,size_t new_size)657*4a3ba775SMasatake YAMATO static int file_try_resize (MIO *mio, size_t new_size)
658*4a3ba775SMasatake YAMATO {
659*4a3ba775SMasatake YAMATO mio_flush (mio);
660*4a3ba775SMasatake YAMATO
661*4a3ba775SMasatake YAMATO FILE *fp = mio_file_get_fp (mio);
662*4a3ba775SMasatake YAMATO
663*4a3ba775SMasatake YAMATO #ifdef MAY_HAVE_FTRUNCATE
664*4a3ba775SMasatake YAMATO if (ftruncate(fileno(fp), (off_t)new_size) < 0)
665*4a3ba775SMasatake YAMATO return false;
666*4a3ba775SMasatake YAMATO #else
667*4a3ba775SMasatake YAMATO /* See https://stackoverflow.com/questions/873454/how-to-truncate-a-file-in-c/874704#874704 */
668*4a3ba775SMasatake YAMATO if ((errno = _chsize_s(_fileno(fp), (__int64)new_size)) != 0)
669*4a3ba775SMasatake YAMATO return false;
670*4a3ba775SMasatake YAMATO #endif
671*4a3ba775SMasatake YAMATO return true;
672*4a3ba775SMasatake YAMATO }
673*4a3ba775SMasatake YAMATO
mio_try_resize(MIO * mio,size_t new_size)674ad1e690fSMasatake YAMATO int mio_try_resize (MIO *mio, size_t new_size)
675ad1e690fSMasatake YAMATO {
676*4a3ba775SMasatake YAMATO if (mio->type == MIO_TYPE_MEMORY)
677ad1e690fSMasatake YAMATO return mem_try_resize (mio, new_size);
678*4a3ba775SMasatake YAMATO else
679*4a3ba775SMasatake YAMATO return file_try_resize (mio, new_size);
680ad1e690fSMasatake YAMATO }
681ad1e690fSMasatake YAMATO
682509a47dbSJiří Techet /*
683509a47dbSJiří Techet * mem_try_ensure_space:
684509a47dbSJiří Techet * @mio: A #MIO object
685509a47dbSJiří Techet * @n: Requested size from the current (cursor) position
686509a47dbSJiří Techet *
687509a47dbSJiří Techet * Tries to ensure there is enough space for @n bytes to be written from the
688509a47dbSJiří Techet * current cursor position.
689509a47dbSJiří Techet *
690ce990805SThomas Braun * Returns: %true if there is enough space, %false otherwise.
691509a47dbSJiří Techet */
mem_try_ensure_space(MIO * mio,size_t n)692509a47dbSJiří Techet static int mem_try_ensure_space (MIO *mio, size_t n)
693509a47dbSJiří Techet {
694ce990805SThomas Braun int success = true;
695509a47dbSJiří Techet
696509a47dbSJiří Techet if (mio->impl.mem.pos + n > mio->impl.mem.size)
697509a47dbSJiří Techet success = mem_try_resize (mio, mio->impl.mem.pos + n);
698509a47dbSJiří Techet
699509a47dbSJiří Techet return success;
700509a47dbSJiří Techet }
701509a47dbSJiří Techet
702509a47dbSJiří Techet /**
703509a47dbSJiří Techet * mio_write:
704509a47dbSJiří Techet * @mio: A #MIO object
705509a47dbSJiří Techet * @ptr: Pointer to the memory to write on the stream
706509a47dbSJiří Techet * @size: Size of each block to write
707509a47dbSJiří Techet * @nmemb: Number of block to write
708509a47dbSJiří Techet *
709509a47dbSJiří Techet * Writes raw data to a #MIO stream. This function behaves the same as fwrite().
710509a47dbSJiří Techet *
711509a47dbSJiří Techet * Returns: The number of blocks actually written to the stream. This might be
712509a47dbSJiří Techet * smaller than the requested count if a write error occurs.
713509a47dbSJiří Techet */
mio_write(MIO * mio,const void * ptr,size_t size,size_t nmemb)714509a47dbSJiří Techet size_t mio_write (MIO *mio,
715509a47dbSJiří Techet const void *ptr,
716509a47dbSJiří Techet size_t size,
717509a47dbSJiří Techet size_t nmemb)
718509a47dbSJiří Techet {
719509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
720509a47dbSJiří Techet return fwrite (ptr, size, nmemb, mio->impl.file.fp);
721dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
722509a47dbSJiří Techet {
723509a47dbSJiří Techet size_t n_written = 0;
724509a47dbSJiří Techet
725509a47dbSJiří Techet if (size != 0 && nmemb != 0)
726509a47dbSJiří Techet {
727509a47dbSJiří Techet if (mem_try_ensure_space (mio, size * nmemb))
728509a47dbSJiří Techet {
729509a47dbSJiří Techet memcpy (&mio->impl.mem.buf[mio->impl.mem.pos], ptr, size * nmemb);
730509a47dbSJiří Techet mio->impl.mem.pos += size * nmemb;
731509a47dbSJiří Techet n_written = nmemb;
732509a47dbSJiří Techet }
733509a47dbSJiří Techet }
734509a47dbSJiří Techet
735509a47dbSJiří Techet return n_written;
736509a47dbSJiří Techet }
737dded9801SMasatake YAMATO else
738dded9801SMasatake YAMATO {
739dded9801SMasatake YAMATO AssertNotReached ();
740dded9801SMasatake YAMATO return 0;
741dded9801SMasatake YAMATO }
742509a47dbSJiří Techet }
743509a47dbSJiří Techet
744509a47dbSJiří Techet /**
745509a47dbSJiří Techet * mio_putc:
746509a47dbSJiří Techet * @mio: A #MIO object
747509a47dbSJiří Techet * @c: The character to write
748509a47dbSJiří Techet *
749509a47dbSJiří Techet * Writes a character to a #MIO stream. This function behaves the same as
750509a47dbSJiří Techet * fputc().
751509a47dbSJiří Techet *
752759d281dSK.Takata * Returns: The written character, or %EOF on error.
753509a47dbSJiří Techet */
mio_putc(MIO * mio,int c)754509a47dbSJiří Techet int mio_putc (MIO *mio, int c)
755509a47dbSJiří Techet {
756509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
757509a47dbSJiří Techet return fputc (c, mio->impl.file.fp);
758dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
759509a47dbSJiří Techet {
760509a47dbSJiří Techet int rv = EOF;
761509a47dbSJiří Techet
762509a47dbSJiří Techet if (mem_try_ensure_space (mio, 1))
763509a47dbSJiří Techet {
764509a47dbSJiří Techet mio->impl.mem.buf[mio->impl.mem.pos] = (unsigned char)c;
765509a47dbSJiří Techet mio->impl.mem.pos++;
766509a47dbSJiří Techet rv = (int)((unsigned char)c);
767509a47dbSJiří Techet }
768509a47dbSJiří Techet
769509a47dbSJiří Techet return rv;
770509a47dbSJiří Techet }
771dded9801SMasatake YAMATO else
772dded9801SMasatake YAMATO {
773dded9801SMasatake YAMATO AssertNotReached ();
774dded9801SMasatake YAMATO return 0;
775dded9801SMasatake YAMATO }
776509a47dbSJiří Techet }
777509a47dbSJiří Techet
778509a47dbSJiří Techet /**
779509a47dbSJiří Techet * mio_puts:
780509a47dbSJiří Techet * @mio: A #MIO object
781509a47dbSJiří Techet * @s: The string to write
782509a47dbSJiří Techet *
783509a47dbSJiří Techet * Writes a string to a #MIO object. This function behaves the same as fputs().
784509a47dbSJiří Techet *
785509a47dbSJiří Techet * Returns: A non-negative integer on success or %EOF on failure.
786509a47dbSJiří Techet */
mio_puts(MIO * mio,const char * s)787509a47dbSJiří Techet int mio_puts (MIO *mio, const char *s)
788509a47dbSJiří Techet {
789509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
790509a47dbSJiří Techet return fputs (s, mio->impl.file.fp);
791dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
792509a47dbSJiří Techet {
793509a47dbSJiří Techet int rv = EOF;
794509a47dbSJiří Techet size_t len;
795509a47dbSJiří Techet
796509a47dbSJiří Techet len = strlen (s);
797509a47dbSJiří Techet if (mem_try_ensure_space (mio, len))
798509a47dbSJiří Techet {
799509a47dbSJiří Techet memcpy (&mio->impl.mem.buf[mio->impl.mem.pos], s, len);
800509a47dbSJiří Techet mio->impl.mem.pos += len;
801509a47dbSJiří Techet rv = 1;
802509a47dbSJiří Techet }
803509a47dbSJiří Techet
804509a47dbSJiří Techet return rv;
805509a47dbSJiří Techet }
806dded9801SMasatake YAMATO else
807dded9801SMasatake YAMATO {
808dded9801SMasatake YAMATO AssertNotReached ();
809dded9801SMasatake YAMATO return 0;
810dded9801SMasatake YAMATO }
811509a47dbSJiří Techet }
812509a47dbSJiří Techet
813509a47dbSJiří Techet /**
814509a47dbSJiří Techet * mio_vprintf:
815509a47dbSJiří Techet * @mio: A #MIO object
816759d281dSK.Takata * @format: A printf format string
817509a47dbSJiří Techet * @ap: The variadic argument list for the format
818509a47dbSJiří Techet *
819509a47dbSJiří Techet * Writes a formatted string into a #MIO stream. This function behaves the same
820509a47dbSJiří Techet * as vfprintf().
821509a47dbSJiří Techet *
822509a47dbSJiří Techet * Returns: The number of bytes written in the stream, or a negative value on
823509a47dbSJiří Techet * failure.
824509a47dbSJiří Techet */
mio_vprintf(MIO * mio,const char * format,va_list ap)825509a47dbSJiří Techet int mio_vprintf (MIO *mio, const char *format, va_list ap)
826509a47dbSJiří Techet {
827509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
828509a47dbSJiří Techet return vfprintf (mio->impl.file.fp, format, ap);
829dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
830509a47dbSJiří Techet {
831509a47dbSJiří Techet int rv = -1;
832509a47dbSJiří Techet size_t n;
833509a47dbSJiří Techet size_t old_pos;
834509a47dbSJiří Techet size_t old_size;
835509a47dbSJiří Techet va_list ap_copy;
836b2caf162SJiří Techet char dummy;
837509a47dbSJiří Techet
838509a47dbSJiří Techet old_pos = mio->impl.mem.pos;
839509a47dbSJiří Techet old_size = mio->impl.mem.size;
840509a47dbSJiří Techet va_copy (ap_copy, ap);
841509a47dbSJiří Techet /* compute the size we will need into the buffer */
842646e26b7SMasatake YAMATO n = vsnprintf (&dummy, 1, format, ap_copy) + 1;
843509a47dbSJiří Techet va_end (ap_copy);
844509a47dbSJiří Techet if (mem_try_ensure_space (mio, n))
845509a47dbSJiří Techet {
846509a47dbSJiří Techet unsigned char c;
847509a47dbSJiří Techet
848509a47dbSJiří Techet /* backup character at n+1 that will be overwritten by a \0 ... */
849509a47dbSJiří Techet c = mio->impl.mem.buf[mio->impl.mem.pos + (n - 1)];
850509a47dbSJiří Techet rv = vsprintf ((char *)&mio->impl.mem.buf[mio->impl.mem.pos], format, ap);
851509a47dbSJiří Techet /* ...and restore it */
852509a47dbSJiří Techet mio->impl.mem.buf[mio->impl.mem.pos + (n - 1)] = c;
853509a47dbSJiří Techet if (rv >= 0 && (size_t)rv == (n - 1))
854509a47dbSJiří Techet {
855509a47dbSJiří Techet /* re-compute the actual size since we might have allocated one byte
856509a47dbSJiří Techet * more than needed */
857509a47dbSJiří Techet mio->impl.mem.size = MAX (old_size, old_pos + (unsigned int)rv);
858509a47dbSJiří Techet mio->impl.mem.pos += (unsigned int)rv;
859509a47dbSJiří Techet }
860509a47dbSJiří Techet else
861509a47dbSJiří Techet {
862509a47dbSJiří Techet mio->impl.mem.size = old_size;
863509a47dbSJiří Techet rv = -1;
864509a47dbSJiří Techet }
865509a47dbSJiří Techet }
866509a47dbSJiří Techet
867509a47dbSJiří Techet return rv;
868509a47dbSJiří Techet }
869dded9801SMasatake YAMATO else
870dded9801SMasatake YAMATO {
871dded9801SMasatake YAMATO AssertNotReached ();
872dded9801SMasatake YAMATO return 0;
873dded9801SMasatake YAMATO }
874509a47dbSJiří Techet }
875509a47dbSJiří Techet
876509a47dbSJiří Techet /**
877509a47dbSJiří Techet * mio_printf:
878509a47dbSJiří Techet * @mio: A #MIO object
879509a47dbSJiří Techet * @format: A print format string
880509a47dbSJiří Techet * @...: Arguments of the format
881509a47dbSJiří Techet *
882509a47dbSJiří Techet * Writes a formatted string to a #MIO stream. This function behaves the same as
883509a47dbSJiří Techet * fprintf().
884509a47dbSJiří Techet *
885509a47dbSJiří Techet * Returns: The number of bytes written to the stream, or a negative value on
886509a47dbSJiří Techet * failure.
887509a47dbSJiří Techet */
mio_printf(MIO * mio,const char * format,...)888509a47dbSJiří Techet int mio_printf (MIO *mio, const char *format, ...)
889509a47dbSJiří Techet {
890509a47dbSJiří Techet int rv;
891509a47dbSJiří Techet va_list ap;
892509a47dbSJiří Techet
893509a47dbSJiří Techet va_start (ap, format);
894509a47dbSJiří Techet rv = mio_vprintf (mio, format, ap);
895509a47dbSJiří Techet va_end (ap);
896509a47dbSJiří Techet
897509a47dbSJiří Techet return rv;
898509a47dbSJiří Techet }
899509a47dbSJiří Techet
900509a47dbSJiří Techet /**
901509a47dbSJiří Techet * mio_getc:
902509a47dbSJiří Techet * @mio: A #MIO object
903509a47dbSJiří Techet *
904509a47dbSJiří Techet * Gets the current character from a #MIO stream. This function behaves the same
905509a47dbSJiří Techet * as fgetc().
906509a47dbSJiří Techet *
907509a47dbSJiří Techet * Returns: The read character as a #int, or %EOF on error.
908509a47dbSJiří Techet */
mio_getc(MIO * mio)909509a47dbSJiří Techet int mio_getc (MIO *mio)
910509a47dbSJiří Techet {
911509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
912509a47dbSJiří Techet return fgetc (mio->impl.file.fp);
913dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
914509a47dbSJiří Techet {
915509a47dbSJiří Techet int rv = EOF;
916509a47dbSJiří Techet
917509a47dbSJiří Techet if (mio->impl.mem.ungetch != EOF)
918509a47dbSJiří Techet {
919509a47dbSJiří Techet rv = mio->impl.mem.ungetch;
920509a47dbSJiří Techet mio->impl.mem.ungetch = EOF;
921509a47dbSJiří Techet mio->impl.mem.pos++;
922509a47dbSJiří Techet }
923509a47dbSJiří Techet else if (mio->impl.mem.pos < mio->impl.mem.size)
924509a47dbSJiří Techet {
925509a47dbSJiří Techet rv = mio->impl.mem.buf[mio->impl.mem.pos];
926509a47dbSJiří Techet mio->impl.mem.pos++;
927509a47dbSJiří Techet }
928509a47dbSJiří Techet else
929ce990805SThomas Braun mio->impl.mem.eof = true;
930509a47dbSJiří Techet
931509a47dbSJiří Techet return rv;
932509a47dbSJiří Techet }
933dded9801SMasatake YAMATO else
934dded9801SMasatake YAMATO {
935dded9801SMasatake YAMATO AssertNotReached ();
936dded9801SMasatake YAMATO return 0;
937dded9801SMasatake YAMATO }
938509a47dbSJiří Techet }
939509a47dbSJiří Techet
940509a47dbSJiří Techet /**
941509a47dbSJiří Techet * mio_ungetc:
942509a47dbSJiří Techet * @mio: A #MIO object
943509a47dbSJiří Techet * @ch: Character to put back in the stream
944509a47dbSJiří Techet *
945509a47dbSJiří Techet * Puts a character back in a #MIO stream. This function behaves the sames as
946509a47dbSJiří Techet * ungetc().
947509a47dbSJiří Techet *
948509a47dbSJiří Techet * <warning><para>It is only guaranteed that one character can be but back at a
949509a47dbSJiří Techet * time, even if the implementation may allow more.</para></warning>
950509a47dbSJiří Techet * <warning><para>Using this function while the stream cursor is at offset 0 is
951509a47dbSJiří Techet * not guaranteed to function properly. As the C99 standard says, it is "an
952509a47dbSJiří Techet * obsolescent feature".</para></warning>
953509a47dbSJiří Techet *
954509a47dbSJiří Techet * Returns: The character put back, or %EOF on error.
955509a47dbSJiří Techet */
mio_ungetc(MIO * mio,int ch)956509a47dbSJiří Techet int mio_ungetc (MIO *mio, int ch)
957509a47dbSJiří Techet {
958509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
959509a47dbSJiří Techet return ungetc (ch, mio->impl.file.fp);
960dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
961509a47dbSJiří Techet {
962509a47dbSJiří Techet int rv = EOF;
963509a47dbSJiří Techet
964509a47dbSJiří Techet if (ch != EOF && mio->impl.mem.ungetch == EOF)
965509a47dbSJiří Techet {
966509a47dbSJiří Techet rv = mio->impl.mem.ungetch = ch;
967509a47dbSJiří Techet mio->impl.mem.pos--;
968ce990805SThomas Braun mio->impl.mem.eof = false;
969509a47dbSJiří Techet }
970509a47dbSJiří Techet
971509a47dbSJiří Techet return rv;
972509a47dbSJiří Techet }
973dded9801SMasatake YAMATO else
974dded9801SMasatake YAMATO {
975dded9801SMasatake YAMATO AssertNotReached ();
976dded9801SMasatake YAMATO return 0;
977dded9801SMasatake YAMATO }
978509a47dbSJiří Techet }
979509a47dbSJiří Techet
980509a47dbSJiří Techet /**
981509a47dbSJiří Techet * mio_gets:
982509a47dbSJiří Techet * @mio: A #MIO object
983509a47dbSJiří Techet * @s: A string to fill with the read data
984509a47dbSJiří Techet * @size: The maximum number of bytes to read
985509a47dbSJiří Techet *
986509a47dbSJiří Techet * Reads a string from a #MIO stream, stopping after the first new-line
987509a47dbSJiří Techet * character or at the end of the stream. This function behaves the same as
988509a47dbSJiří Techet * fgets().
989509a47dbSJiří Techet *
990509a47dbSJiří Techet * Returns: @s on success, %NULL otherwise.
991509a47dbSJiří Techet */
mio_gets(MIO * mio,char * s,size_t size)992509a47dbSJiří Techet char *mio_gets (MIO *mio, char *s, size_t size)
993509a47dbSJiří Techet {
994509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
995509a47dbSJiří Techet return fgets (s, (int)size, mio->impl.file.fp);
996dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
997509a47dbSJiří Techet {
998509a47dbSJiří Techet char *rv = NULL;
999509a47dbSJiří Techet
1000509a47dbSJiří Techet if (size > 0)
1001509a47dbSJiří Techet {
1002509a47dbSJiří Techet size_t i = 0;
1003e19b27e3SJiří Techet bool newline = false;
1004f1c2a6edSJiří Techet /* Avoiding dereference inside the for loop below improves
1005f1c2a6edSJiří Techet * performance so we do it here. */
10063e257bf7SJiří Techet size_t pos = mio->impl.mem.pos;
10073e257bf7SJiří Techet size_t buf_size = mio->impl.mem.size;
10083e257bf7SJiří Techet unsigned char *buf = mio->impl.mem.buf;
1009509a47dbSJiří Techet
1010509a47dbSJiří Techet if (mio->impl.mem.ungetch != EOF)
1011509a47dbSJiří Techet {
1012509a47dbSJiří Techet s[i] = (char)mio->impl.mem.ungetch;
1013509a47dbSJiří Techet mio->impl.mem.ungetch = EOF;
10143e257bf7SJiří Techet pos++;
1015509a47dbSJiří Techet i++;
1016509a47dbSJiří Techet }
10173e257bf7SJiří Techet for (; pos < buf_size && i < (size - 1); i++)
1018509a47dbSJiří Techet {
10193e257bf7SJiří Techet s[i] = (char)buf[pos];
10203e257bf7SJiří Techet pos++;
1021509a47dbSJiří Techet if (s[i] == '\n')
1022509a47dbSJiří Techet {
1023509a47dbSJiří Techet i++;
1024e19b27e3SJiří Techet newline = true;
1025509a47dbSJiří Techet break;
1026509a47dbSJiří Techet }
1027509a47dbSJiří Techet }
1028509a47dbSJiří Techet if (i > 0)
1029509a47dbSJiří Techet {
1030509a47dbSJiří Techet s[i] = 0;
1031509a47dbSJiří Techet rv = s;
1032509a47dbSJiří Techet }
10333e257bf7SJiří Techet if (!newline && pos >= buf_size)
1034ce990805SThomas Braun mio->impl.mem.eof = true;
10353e257bf7SJiří Techet mio->impl.mem.pos = pos;
10363e257bf7SJiří Techet mio->impl.mem.size = buf_size;
1037509a47dbSJiří Techet }
1038509a47dbSJiří Techet
1039509a47dbSJiří Techet return rv;
1040509a47dbSJiří Techet }
1041dded9801SMasatake YAMATO else
1042dded9801SMasatake YAMATO {
1043dded9801SMasatake YAMATO AssertNotReached ();
1044dded9801SMasatake YAMATO return 0;
1045dded9801SMasatake YAMATO }
1046509a47dbSJiří Techet }
1047509a47dbSJiří Techet
1048509a47dbSJiří Techet /**
1049509a47dbSJiří Techet * mio_clearerr:
1050509a47dbSJiří Techet * @mio: A #MIO object
1051509a47dbSJiří Techet *
1052509a47dbSJiří Techet * Clears the error and end-of-stream indicators of a #MIO stream. This function
1053509a47dbSJiří Techet * behaves the same as clearerr().
1054509a47dbSJiří Techet */
mio_clearerr(MIO * mio)1055509a47dbSJiří Techet void mio_clearerr (MIO *mio)
1056509a47dbSJiří Techet {
1057509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1058509a47dbSJiří Techet clearerr (mio->impl.file.fp);
1059dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1060509a47dbSJiří Techet {
1061ce990805SThomas Braun mio->impl.mem.error = false;
1062ce990805SThomas Braun mio->impl.mem.eof = false;
1063509a47dbSJiří Techet }
1064dded9801SMasatake YAMATO else
1065dded9801SMasatake YAMATO AssertNotReached ();
1066509a47dbSJiří Techet }
1067509a47dbSJiří Techet
1068509a47dbSJiří Techet /**
1069509a47dbSJiří Techet * mio_eof:
1070509a47dbSJiří Techet * @mio: A #MIO object
1071509a47dbSJiří Techet *
1072509a47dbSJiří Techet * Checks whether the end-of-stream indicator of a #MIO stream is set. This
1073509a47dbSJiří Techet * function behaves the same as feof().
1074509a47dbSJiří Techet *
1075509a47dbSJiří Techet * Returns: A non-null value if the stream reached its end, 0 otherwise.
1076509a47dbSJiří Techet */
mio_eof(MIO * mio)1077509a47dbSJiří Techet int mio_eof (MIO *mio)
1078509a47dbSJiří Techet {
1079509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1080509a47dbSJiří Techet return feof (mio->impl.file.fp);
1081dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1082ce990805SThomas Braun return mio->impl.mem.eof != false;
1083dded9801SMasatake YAMATO else
1084dded9801SMasatake YAMATO {
1085dded9801SMasatake YAMATO AssertNotReached ();
1086dded9801SMasatake YAMATO return 0;
1087dded9801SMasatake YAMATO }
1088509a47dbSJiří Techet }
1089509a47dbSJiří Techet
1090509a47dbSJiří Techet /**
1091509a47dbSJiří Techet * mio_error:
1092509a47dbSJiří Techet * @mio: A #MIO object
1093509a47dbSJiří Techet *
1094509a47dbSJiří Techet * Checks whether the error indicator of a #MIO stream is set. This function
1095509a47dbSJiří Techet * behaves the same as ferror().
1096509a47dbSJiří Techet *
1097509a47dbSJiří Techet * Returns: A non-null value if the stream have an error set, 0 otherwise.
1098509a47dbSJiří Techet */
mio_error(MIO * mio)1099509a47dbSJiří Techet int mio_error (MIO *mio)
1100509a47dbSJiří Techet {
1101509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1102509a47dbSJiří Techet return ferror (mio->impl.file.fp);
1103dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1104ce990805SThomas Braun return mio->impl.mem.error != false;
1105dded9801SMasatake YAMATO else
1106dded9801SMasatake YAMATO {
1107dded9801SMasatake YAMATO AssertNotReached ();
1108dded9801SMasatake YAMATO return 0;
1109dded9801SMasatake YAMATO }
1110509a47dbSJiří Techet }
1111509a47dbSJiří Techet
1112509a47dbSJiří Techet /**
1113509a47dbSJiří Techet * mio_seek:
1114509a47dbSJiří Techet * @mio: A #MIO object
1115509a47dbSJiří Techet * @offset: Offset of the new place, from @whence
1116509a47dbSJiří Techet * @whence: Move origin. SEEK_SET moves relative to the start of the stream,
1117509a47dbSJiří Techet * SEEK_CUR from the current position and SEEK_SET from the end of the
1118509a47dbSJiří Techet * stream.
1119509a47dbSJiří Techet *
1120759d281dSK.Takata * Sets the cursor position on a #MIO stream. This functions behaves the same as
1121509a47dbSJiří Techet * fseek(). See also mio_tell() and mio_setpos().
1122509a47dbSJiří Techet *
1123509a47dbSJiří Techet * Returns: 0 on success, -1 otherwise, in which case errno should be set to
1124509a47dbSJiří Techet * indicate the error.
1125509a47dbSJiří Techet */
mio_seek(MIO * mio,long offset,int whence)1126509a47dbSJiří Techet int mio_seek (MIO *mio, long offset, int whence)
1127509a47dbSJiří Techet {
1128509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1129509a47dbSJiří Techet return fseek (mio->impl.file.fp, offset, whence);
1130dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1131509a47dbSJiří Techet {
1132509a47dbSJiří Techet /* FIXME: should we support seeking out of bounds like lseek() seems to do? */
1133509a47dbSJiří Techet int rv = -1;
1134509a47dbSJiří Techet
1135509a47dbSJiří Techet switch (whence)
1136509a47dbSJiří Techet {
1137509a47dbSJiří Techet case SEEK_SET:
1138509a47dbSJiří Techet if (offset < 0 || (size_t)offset > mio->impl.mem.size)
1139509a47dbSJiří Techet errno = EINVAL;
1140509a47dbSJiří Techet else
1141509a47dbSJiří Techet {
1142509a47dbSJiří Techet mio->impl.mem.pos = (size_t)offset;
1143509a47dbSJiří Techet rv = 0;
1144509a47dbSJiří Techet }
1145509a47dbSJiří Techet break;
1146509a47dbSJiří Techet
1147509a47dbSJiří Techet case SEEK_CUR:
1148509a47dbSJiří Techet if ((offset < 0 && (size_t)-offset > mio->impl.mem.pos) ||
1149509a47dbSJiří Techet mio->impl.mem.pos + (size_t)offset > mio->impl.mem.size)
1150509a47dbSJiří Techet {
1151509a47dbSJiří Techet errno = EINVAL;
1152509a47dbSJiří Techet }
1153509a47dbSJiří Techet else
1154509a47dbSJiří Techet {
1155509a47dbSJiří Techet mio->impl.mem.pos = (size_t)(mio->impl.mem.pos + offset);
1156509a47dbSJiří Techet rv = 0;
1157509a47dbSJiří Techet }
1158509a47dbSJiří Techet break;
1159509a47dbSJiří Techet
1160509a47dbSJiří Techet case SEEK_END:
1161509a47dbSJiří Techet if (offset > 0 || (size_t)-offset > mio->impl.mem.size)
1162509a47dbSJiří Techet errno = EINVAL;
1163509a47dbSJiří Techet else
1164509a47dbSJiří Techet {
1165509a47dbSJiří Techet mio->impl.mem.pos = mio->impl.mem.size - (size_t)-offset;
1166509a47dbSJiří Techet rv = 0;
1167509a47dbSJiří Techet }
1168509a47dbSJiří Techet break;
1169509a47dbSJiří Techet
1170509a47dbSJiří Techet default:
1171509a47dbSJiří Techet errno = EINVAL;
1172509a47dbSJiří Techet }
1173509a47dbSJiří Techet if (rv == 0)
1174509a47dbSJiří Techet {
1175ce990805SThomas Braun mio->impl.mem.eof = false;
1176509a47dbSJiří Techet mio->impl.mem.ungetch = EOF;
1177509a47dbSJiří Techet }
1178509a47dbSJiří Techet
1179509a47dbSJiří Techet return rv;
1180509a47dbSJiří Techet }
1181dded9801SMasatake YAMATO else
1182dded9801SMasatake YAMATO {
1183dded9801SMasatake YAMATO AssertNotReached ();
1184dded9801SMasatake YAMATO return 0;
1185dded9801SMasatake YAMATO }
1186dded9801SMasatake YAMATO
1187509a47dbSJiří Techet }
1188509a47dbSJiří Techet
1189509a47dbSJiří Techet /**
1190509a47dbSJiří Techet * mio_tell:
1191509a47dbSJiří Techet * @mio: A #MIO object
1192509a47dbSJiří Techet *
1193509a47dbSJiří Techet * Gets the current cursor position of a #MIO stream. This function behaves the
1194509a47dbSJiří Techet * same as ftell().
1195509a47dbSJiří Techet *
1196509a47dbSJiří Techet * Returns: The current offset from the start of the stream, or -1 or error, in
1197509a47dbSJiří Techet * which case errno is set to indicate the error.
1198509a47dbSJiří Techet */
mio_tell(MIO * mio)1199509a47dbSJiří Techet long mio_tell (MIO *mio)
1200509a47dbSJiří Techet {
1201509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1202509a47dbSJiří Techet return ftell (mio->impl.file.fp);
1203dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1204509a47dbSJiří Techet {
1205509a47dbSJiří Techet long rv = -1;
1206509a47dbSJiří Techet
1207509a47dbSJiří Techet if (mio->impl.mem.pos > LONG_MAX)
1208509a47dbSJiří Techet {
1209509a47dbSJiří Techet #ifdef EOVERFLOW
1210509a47dbSJiří Techet errno = EOVERFLOW;
1211509a47dbSJiří Techet #endif
1212509a47dbSJiří Techet }
1213509a47dbSJiří Techet else
1214509a47dbSJiří Techet rv = (long)mio->impl.mem.pos;
1215509a47dbSJiří Techet
1216509a47dbSJiří Techet return rv;
1217509a47dbSJiří Techet }
1218dded9801SMasatake YAMATO else
1219dded9801SMasatake YAMATO {
1220dded9801SMasatake YAMATO AssertNotReached ();
1221dded9801SMasatake YAMATO return 0;
1222dded9801SMasatake YAMATO }
1223509a47dbSJiří Techet }
1224509a47dbSJiří Techet
1225509a47dbSJiří Techet /**
1226509a47dbSJiří Techet * mio_rewind:
1227509a47dbSJiří Techet * @mio: A #MIO object
1228509a47dbSJiří Techet *
1229509a47dbSJiří Techet * Resets the cursor position to 0, and also the end-of-stream and the error
1230509a47dbSJiří Techet * indicators of a #MIO stream.
1231509a47dbSJiří Techet * See also mio_seek() and mio_clearerr().
1232509a47dbSJiří Techet */
mio_rewind(MIO * mio)1233509a47dbSJiří Techet void mio_rewind (MIO *mio)
1234509a47dbSJiří Techet {
1235509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1236509a47dbSJiří Techet rewind (mio->impl.file.fp);
1237dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1238509a47dbSJiří Techet {
1239509a47dbSJiří Techet mio->impl.mem.pos = 0;
1240509a47dbSJiří Techet mio->impl.mem.ungetch = EOF;
1241ce990805SThomas Braun mio->impl.mem.eof = false;
1242ce990805SThomas Braun mio->impl.mem.error = false;
1243509a47dbSJiří Techet }
1244dded9801SMasatake YAMATO else
1245dded9801SMasatake YAMATO AssertNotReached ();
1246509a47dbSJiří Techet }
1247509a47dbSJiří Techet
1248509a47dbSJiří Techet /**
1249509a47dbSJiří Techet * mio_getpos:
1250509a47dbSJiří Techet * @mio: A #MIO stream
1251509a47dbSJiří Techet * @pos: (out): A #MIOPos object to fill-in
1252509a47dbSJiří Techet *
1253509a47dbSJiří Techet * Stores the current position (and maybe other informations about the stream
1254509a47dbSJiří Techet * state) of a #MIO stream in order to restore it later with mio_setpos(). This
1255509a47dbSJiří Techet * function behaves the same as fgetpos().
1256509a47dbSJiří Techet *
1257509a47dbSJiří Techet * Returns: 0 on success, -1 otherwise, in which case errno is set to indicate
1258509a47dbSJiří Techet * the error.
1259509a47dbSJiří Techet */
mio_getpos(MIO * mio,MIOPos * pos)1260509a47dbSJiří Techet int mio_getpos (MIO *mio, MIOPos *pos)
1261509a47dbSJiří Techet {
1262509a47dbSJiří Techet int rv = -1;
1263509a47dbSJiří Techet
1264509a47dbSJiří Techet pos->type = mio->type;
1265509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1266509a47dbSJiří Techet rv = fgetpos (mio->impl.file.fp, &pos->impl.file);
1267dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1268509a47dbSJiří Techet {
1269509a47dbSJiří Techet rv = -1;
1270509a47dbSJiří Techet
1271509a47dbSJiří Techet if (mio->impl.mem.pos == (size_t)-1)
1272509a47dbSJiří Techet {
1273509a47dbSJiří Techet /* this happens if ungetc() was called at the start of the stream */
1274509a47dbSJiří Techet #ifdef EIO
1275509a47dbSJiří Techet errno = EIO;
1276509a47dbSJiří Techet #endif
1277509a47dbSJiří Techet }
1278509a47dbSJiří Techet else
1279509a47dbSJiří Techet {
1280509a47dbSJiří Techet pos->impl.mem = mio->impl.mem.pos;
1281509a47dbSJiří Techet rv = 0;
1282509a47dbSJiří Techet }
1283509a47dbSJiří Techet }
1284dded9801SMasatake YAMATO else
1285dded9801SMasatake YAMATO AssertNotReached();
1286dded9801SMasatake YAMATO
1287509a47dbSJiří Techet #ifdef MIO_DEBUG
1288509a47dbSJiří Techet if (rv != -1)
1289509a47dbSJiří Techet {
1290509a47dbSJiří Techet pos->tag = mio;
1291509a47dbSJiří Techet }
1292509a47dbSJiří Techet #endif /* MIO_DEBUG */
1293509a47dbSJiří Techet
1294509a47dbSJiří Techet return rv;
1295509a47dbSJiří Techet }
1296509a47dbSJiří Techet
1297509a47dbSJiří Techet /**
1298509a47dbSJiří Techet * mio_setpos:
1299509a47dbSJiří Techet * @mio: A #MIO object
1300509a47dbSJiří Techet * @pos: (in): A #MIOPos object filled-in by a previous call of mio_getpos() on
1301509a47dbSJiří Techet * the same stream
1302509a47dbSJiří Techet *
1303509a47dbSJiří Techet * Restores the position and state indicators of a #MIO stream previously saved
1304509a47dbSJiří Techet * by mio_getpos().
1305509a47dbSJiří Techet *
1306509a47dbSJiří Techet * <warning><para>The #MIOPos object must have been initialized by a previous
1307509a47dbSJiří Techet * call to mio_getpos() on the same stream.</para></warning>
1308509a47dbSJiří Techet *
1309509a47dbSJiří Techet * Returns: 0 on success, -1 otherwise, in which case errno is set to indicate
1310509a47dbSJiří Techet * the error.
1311509a47dbSJiří Techet */
mio_setpos(MIO * mio,MIOPos * pos)1312509a47dbSJiří Techet int mio_setpos (MIO *mio, MIOPos *pos)
1313509a47dbSJiří Techet {
1314509a47dbSJiří Techet int rv = -1;
1315509a47dbSJiří Techet
1316509a47dbSJiří Techet #ifdef MIO_DEBUG
1317509a47dbSJiří Techet if (pos->tag != mio)
1318509a47dbSJiří Techet {
1319509a47dbSJiří Techet g_critical ("mio_setpos((MIO*)%p, (MIOPos*)%p): "
1320509a47dbSJiří Techet "Given MIOPos was not set by a previous call to mio_getpos() "
1321509a47dbSJiří Techet "on the same MIO object, which means there is a bug in "
1322509a47dbSJiří Techet "someone's code.",
1323509a47dbSJiří Techet (void *)mio, (void *)pos);
1324509a47dbSJiří Techet errno = EINVAL;
1325509a47dbSJiří Techet return -1;
1326509a47dbSJiří Techet }
1327509a47dbSJiří Techet #endif /* MIO_DEBUG */
1328509a47dbSJiří Techet
1329509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1330509a47dbSJiří Techet rv = fsetpos (mio->impl.file.fp, &pos->impl.file);
1331dded9801SMasatake YAMATO else if (mio->type == MIO_TYPE_MEMORY)
1332509a47dbSJiří Techet {
1333509a47dbSJiří Techet rv = -1;
1334509a47dbSJiří Techet
1335509a47dbSJiří Techet if (pos->impl.mem > mio->impl.mem.size)
1336509a47dbSJiří Techet errno = EINVAL;
1337509a47dbSJiří Techet else
1338509a47dbSJiří Techet {
1339509a47dbSJiří Techet mio->impl.mem.ungetch = EOF;
1340509a47dbSJiří Techet mio->impl.mem.pos = pos->impl.mem;
1341509a47dbSJiří Techet rv = 0;
1342509a47dbSJiří Techet }
1343509a47dbSJiří Techet }
1344dded9801SMasatake YAMATO else
1345dded9801SMasatake YAMATO AssertNotReached ();
1346509a47dbSJiří Techet
1347509a47dbSJiří Techet return rv;
1348509a47dbSJiří Techet }
1349509a47dbSJiří Techet
1350509a47dbSJiří Techet /**
1351509a47dbSJiří Techet * mio_flush:
1352509a47dbSJiří Techet * @mio: A #MIO object
1353509a47dbSJiří Techet *
1354509a47dbSJiří Techet * Forces a write of all user-space buffered data for the given output or update
1355509a47dbSJiří Techet * stream via the stream's underlying write function. Only applicable when using
1356509a47dbSJiří Techet * FILE back-end.
1357509a47dbSJiří Techet *
1358509a47dbSJiří Techet * Returns: 0 on success, error code otherwise.
1359509a47dbSJiří Techet */
mio_flush(MIO * mio)1360509a47dbSJiří Techet int mio_flush (MIO *mio)
1361509a47dbSJiří Techet {
1362509a47dbSJiří Techet if (mio->type == MIO_TYPE_FILE)
1363509a47dbSJiří Techet return fflush (mio->impl.file.fp);
1364509a47dbSJiří Techet return 0;
1365509a47dbSJiří Techet }
1366bf6da614SMasatake YAMATO
1367bf6da614SMasatake YAMATO
1368bf6da614SMasatake YAMATO /**
1369bf6da614SMasatake YAMATO * mio_attach_user_data:
1370bf6da614SMasatake YAMATO * @mio: A #MIO object
1371bf6da614SMasatake YAMATO * @user_data: a pointer to any data object
1372bf6da614SMasatake YAMATO * @user_data_free_func: a call back function to destroy the data object passed as @user_data
1373bf6da614SMasatake YAMATO *
1374bf6da614SMasatake YAMATO * Attach any data object to a #MIO object. Attached data can be got with
1375bf6da614SMasatake YAMATO * mio_get_user_data(). The attached data is destroyed when new data is attached to
1376bf6da614SMasatake YAMATO * the #MIO object, or #the MIO object itself is destroyed. For destroying the data
1377bf6da614SMasatake YAMATO * @user_data_free_func is used.
1378bf6da614SMasatake YAMATO *
1379bf6da614SMasatake YAMATO */
1380bf6da614SMasatake YAMATO
mio_attach_user_data(MIO * mio,void * user_data,MIODestroyNotify user_data_free_func)1381bf6da614SMasatake YAMATO void mio_attach_user_data (MIO *mio, void *user_data, MIODestroyNotify user_data_free_func)
1382bf6da614SMasatake YAMATO {
1383bf6da614SMasatake YAMATO if (mio->udata.d && mio->udata.f)
1384bf6da614SMasatake YAMATO mio->udata.f (mio->udata.d);
1385bf6da614SMasatake YAMATO
1386bf6da614SMasatake YAMATO mio->udata.d = user_data;
1387bf6da614SMasatake YAMATO mio->udata.f = user_data_free_func;
1388bf6da614SMasatake YAMATO }
1389bf6da614SMasatake YAMATO
1390bf6da614SMasatake YAMATO /**
1391bf6da614SMasatake YAMATO * mio_get_user_data:
1392bf6da614SMasatake YAMATO * @mio: A #MIO object
1393bf6da614SMasatake YAMATO *
1394bf6da614SMasatake YAMATO * Returns: user data attached with mio_attach_user_data() to a #MIO object.
1395bf6da614SMasatake YAMATO *
1396bf6da614SMasatake YAMATO */
mio_get_user_data(MIO * mio)1397bf6da614SMasatake YAMATO void *mio_get_user_data (MIO *mio)
1398bf6da614SMasatake YAMATO {
1399bf6da614SMasatake YAMATO return mio->udata.d;
1400bf6da614SMasatake YAMATO }
1401