xref: /Universal-ctags/main/mio.c (revision 4a3ba775e2c9a7ca1c7660d13ace6bdd69140d57)
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