1 /*
2  *  MIO, an I/O abstraction layer replicating C file I/O API.
3  *  Copyright (C) 2010  Colomban Wendling <ban@herbesfolles.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  */
20 
21 #ifndef READTAGS_DSL
22 #include "general.h"  /* must always come first */
23 
24 #include "routines.h"
25 #include "debug.h"
26 #else
27 
28 #if defined (HAVE_CONFIG_H)
29 #include <config.h>
30 #endif
31 
32 #ifdef HAVE_STDBOOL_H
33 #include <stdbool.h>
34 #endif
35 #endif	/* READTAGS_DSL */
36 
37 #include "mio.h"
38 
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <limits.h>
45 
46 #ifdef READTAGS_DSL
47 #define xMalloc(n,Type)    (Type *)eMalloc((size_t)(n) * sizeof (Type))
48 #define xRealloc(p,n,Type) (Type *)eRealloc((p), (n) * sizeof (Type))
49 
eMalloc(const size_t size)50 static void *eMalloc (const size_t size)
51 {
52 	void *buffer = malloc (size);
53 
54 	if (buffer == NULL)
55 	{
56 		fprintf(stderr, "out of memory");
57 		abort ();
58 	}
59 
60 	return buffer;
61 }
62 
eRealloc(void * const ptr,const size_t size)63 static void *eRealloc (void *const ptr, const size_t size)
64 {
65 	void *buffer;
66 	if (ptr == NULL)
67 		buffer = eMalloc (size);
68 	else
69 	{
70 		buffer = realloc (ptr, size);
71 		if (buffer == NULL)
72 		{
73 			fprintf(stderr, "out of memory");
74 			abort ();
75 		}
76 	}
77 	return buffer;
78 }
79 
eFree(void * const ptr)80 static void eFree (void *const ptr)
81 {
82 	free (ptr);
83 }
84 #define eFreeNoNullCheck eFree
85 
86 #  define Assert(c) do {} while(0)
87 #  define AssertNotReached() do {} while(0)
88 #endif	/* READTAGS_DSL */
89 
90 /* minimal reallocation chunk size */
91 #define MIO_CHUNK_SIZE 4096
92 
93 #define MAX(a, b)  (((a) > (b)) ? (a) : (b))
94 
95 
96 /**
97  * SECTION:mio
98  * @short_description: The MIO object
99  * @include: mio/mio.h
100  *
101  * The #MIO object replicates the C file I/O API with support of both standard
102  * file based operations and in-memory operations. Its goal is to ease the port
103  * of an application that uses C file I/O API to perform in-memory operations.
104  *
105  * A #MIO object is created using mio_new_file(), mio_new_memory() or mio_new_mio(),
106  * depending on whether you want file or in-memory operations.
107  * Its life is managed by reference counting. Just after calling one of functions
108  * for creating, the count is 1. mio_ref() increments the counter. mio_unref()
109  * decrements it. When the counter becomes 0, the #MIO object will be destroyed
110  * in mio_unref(). There is also some other convenient API to create file-based
111  * #MIO objects for more complex cases, such as mio_new_file_full() and
112  * mio_new_fp().
113  *
114  * Once the #MIO object is created, you can perform standard I/O operations on
115  * it transparently without the need to care about the effective underlying
116  * operations.
117  *
118  * The I/O API is almost exactly a replication of the standard C file I/O API
119  * with the significant difference that the first parameter is always the #MIO
120  * object to work on.
121  */
122 
123 
124 typedef struct _MIOUserData MIOUserData;
125 struct _MIOUserData {
126 	void *d;
127 	MIODestroyNotify f;
128 };
129 
130 /**
131  * MIO:
132  *
133  * An object representing a #MIO stream. No assumptions should be made about
134  * what compose this object, and none of its fields should be accessed directly.
135  */
136 struct _MIO {
137 	/*< private >*/
138 	MIOType type;
139 	unsigned int refcount;
140 	union {
141 		struct {
142 			FILE *fp;
143 			MIOFCloseFunc close_func;
144 		} file;
145 		struct {
146 			unsigned char *buf;
147 			int ungetch;
148 			size_t pos;
149 			size_t size;
150 			size_t allocated_size;
151 			MIOReallocFunc realloc_func;
152 			MIODestroyNotify free_func;
153 			bool error;
154 			bool eof;
155 		} mem;
156 	} impl;
157 	MIOUserData udata;
158 };
159 
160 
161 /**
162  * mio_new_file_full:
163  * @filename: Filename to open, passed as-is to @open_func as the first argument
164  * @mode: Mode in which open the file, passed as-is to @open_func as the second
165  *        argument
166  * @open_func: A function with the fopen() semantic to use to open the file
167  * @close_func: A function with the fclose() semantic to close the file when
168  *              the #MIO object is destroyed, or %NULL not to close the #FILE
169  *              object
170  *
171  * Creates a new #MIO object working on a file, from a filename and an opening
172  * function. See also mio_new_file().
173  *
174  * This function is generally overkill and mio_new_file() should often be used
175  * instead, but it allows to specify a custom function to open a file, as well
176  * as a close function. The former is useful e.g. if you need to wrap fopen()
177  * for some reason (like filename encoding conversion for example), and the
178  * latter allows you both to match your custom open function and to choose
179  * whether the underlying #FILE object should or not be closed when mio_unref()
180  * is called on the returned object.
181  *
182  * Free-function: mio_unref()
183  *
184  * Returns: A new #MIO on success, or %NULL on failure.
185  */
mio_new_file_full(const char * filename,const char * mode,MIOFOpenFunc open_func,MIOFCloseFunc close_func)186 MIO *mio_new_file_full (const char *filename,
187 						const char *mode,
188 						MIOFOpenFunc open_func,
189 						MIOFCloseFunc close_func)
190 {
191 	MIO *mio;
192 
193 	/* we need to create the MIO object first, because we may not be able to close
194 	 * the opened file if the user passed NULL as the close function, which means
195 	 * that everything must succeed if we've opened the file successfully */
196 	mio = xMalloc (1, MIO);
197 	if (mio)
198 	{
199 		FILE *fp = open_func (filename, mode);
200 
201 		if (! fp)
202 		{
203 			eFree (mio);
204 			mio = NULL;
205 		}
206 		else
207 		{
208 			mio->type = MIO_TYPE_FILE;
209 			mio->impl.file.fp = fp;
210 			mio->impl.file.close_func = close_func;
211 			mio->refcount = 1;
212 			mio->udata.d = NULL;
213 			mio->udata.f = NULL;
214 		}
215 	}
216 
217 	return mio;
218 }
219 
220 /**
221  * mio_new_file:
222  * @filename: Filename to open, same as the fopen()'s first argument
223  * @mode: Mode in which open the file, fopen()'s second argument
224  *
225  * Creates a new #MIO object working on a file from a filename; wrapping
226  * fopen().
227  * This function simply calls mio_new_file_full() with the libc's fopen() and
228  * fclose() functions.
229  *
230  * Free-function: mio_unref()
231  *
232  * Returns: A new #MIO on success, or %NULL on failure.
233  */
mio_new_file(const char * filename,const char * mode)234 MIO *mio_new_file (const char *filename, const char *mode)
235 {
236 	return mio_new_file_full (filename, mode, fopen, fclose);
237 }
238 
239 /**
240  * mio_new_fp:
241  * @fp: An opened #FILE object
242  * @close_func: (allow-none): Function used to close @fp when the #MIO object
243  *              gets destroyed, or %NULL not to close the #FILE object
244  *
245  * Creates a new #MIO object working on a file, from an already opened #FILE
246  * object.
247  *
248  * <example>
249  * <title>Typical use of this function</title>
250  * <programlisting>
251  * MIO *mio = mio_new_fp (fp, fclose);
252  * </programlisting>
253  * </example>
254  *
255  * Free-function: mio_unref()
256  *
257  * Returns: A new #MIO on success or %NULL on failure.
258  */
mio_new_fp(FILE * fp,MIOFCloseFunc close_func)259 MIO *mio_new_fp (FILE *fp, MIOFCloseFunc close_func)
260 {
261 	MIO *mio;
262 
263 	if (!fp)
264 		return NULL;
265 
266 	mio = xMalloc (1, MIO);
267 	if (mio)
268 	{
269 		mio->type = MIO_TYPE_FILE;
270 		mio->impl.file.fp = fp;
271 		mio->impl.file.close_func = close_func;
272 		mio->refcount = 1;
273 		mio->udata.d = NULL;
274 		mio->udata.f = NULL;
275 	}
276 
277 	return mio;
278 }
279 
280 /**
281  * mio_new_memory:
282  * @data: Initial data (may be %NULL)
283  * @size: Length of @data in bytes
284  * @realloc_func: A function with the realloc() semantic used to grow the
285  *                buffer, or %NULL to disable buffer growing
286  * @free_func: A function with the free() semantic to destroy the data together
287  *             with the object, or %NULL not to destroy the data
288  *
289  * Creates a new #MIO object working on memory.
290  *
291  * To allow the buffer to grow, you must provide a @realloc_func, otherwise
292  * trying to write after the end of the current data will fail.
293  *
294  * If you want the buffer to be freed together with the #MIO object, you must
295  * give a @free_func; otherwise the data will still live after #MIO object
296  * termination.
297  *
298  * <example>
299  * <title>Basic creation of a non-growable, freeable #MIO object</title>
300  * <programlisting>
301  * MIO *mio = mio_new_memory (data, size, NULL, g_free);
302  * </programlisting>
303  * </example>
304  *
305  * <example>
306  * <title>Basic creation of an empty growable and freeable #MIO object</title>
307  * <programlisting>
308  * MIO *mio = mio_new_memory (NULL, 0, g_try_realloc, g_free);
309  * </programlisting>
310  * </example>
311  *
312  * Free-function: mio_unref()
313  *
314  * Returns: A new #MIO on success, or %NULL on failure.
315  */
mio_new_memory(unsigned char * data,size_t size,MIOReallocFunc realloc_func,MIODestroyNotify free_func)316 MIO *mio_new_memory (unsigned char *data,
317 					 size_t size,
318 					 MIOReallocFunc realloc_func,
319 					 MIODestroyNotify free_func)
320 {
321 	MIO *mio;
322 
323 	mio = xMalloc (1, MIO);
324 	if (mio)
325 	{
326 		mio->type = MIO_TYPE_MEMORY;
327 		mio->impl.mem.buf = data;
328 		mio->impl.mem.ungetch = EOF;
329 		mio->impl.mem.pos = 0;
330 		mio->impl.mem.size = size;
331 		mio->impl.mem.allocated_size = size;
332 		mio->impl.mem.realloc_func = realloc_func;
333 		mio->impl.mem.free_func = free_func;
334 		mio->impl.mem.eof = false;
335 		mio->impl.mem.error = false;
336 		mio->refcount = 1;
337 		mio->udata.d = NULL;
338 		mio->udata.f = NULL;
339 	}
340 
341 	return mio;
342 }
343 
344 /**
345  * mio_new_mio:
346  * @base: The original mio
347  * @start: stream offset of the @base where new mio starts
348  * @size: the length of the data copied from @base to new mio
349  *
350  * Creates a new #MIO object by copying data from existing #MIO (@base).
351  * The range for copying is given with @start and @size.
352  * Copying data at the range from @start to the end of @base is
353  * done if -1 is given as @size.
354  *
355  * If @size is larger than the length from @start to the end of
356  * @base, %NULL is returned.
357  *
358  * The function doesn't move the file position of @base.
359  *
360  * Free-function: mio_unref()
361  *
362  */
363 
mio_new_mio(MIO * base,long start,long size)364 MIO *mio_new_mio (MIO *base, long start, long size)
365 {
366 	unsigned char *data;
367 	long original_pos;
368 	MIO *submio;
369 	size_t r;
370 
371 	original_pos = mio_tell (base);
372 
373 	if (size == -1)
374 	{
375 		long end;
376 
377 		if (mio_seek (base, 0, SEEK_END) != 0)
378 			return NULL;
379 		end = mio_tell (base);
380 		Assert (end >= start);
381 		size = end - start;
382 	}
383 
384 	if (mio_seek (base, start, SEEK_SET) != 0)
385 		return NULL;
386 
387 	data = xMalloc (size, unsigned char);
388 	r= mio_read (base, data, 1, size);
389 	mio_seek (base, original_pos, SEEK_SET);
390 
391 	if (r != size)
392 		goto cleanup;
393 
394 	submio = mio_new_memory (data, size, eRealloc, eFreeNoNullCheck);
395 	if (! submio)
396 		goto cleanup;
397 
398 	return submio;
399 
400 cleanup:
401 	eFree (data);
402 	return NULL;
403 }
404 
405 /**
406  * mio_ref:
407  * @mio: A #MIO object
408  *
409  * Increments the reference counter of a #MIO.
410  *
411  * Returns: passed @mio.
412  */
mio_ref(MIO * mio)413 MIO *mio_ref        (MIO *mio)
414 {
415 	mio->refcount++;
416 	return mio;
417 }
418 
419 /**
420  * mio_file_get_fp:
421  * @mio: A #MIO object
422  *
423  * Gets the underlying #FILE object associated with a #MIO file stream.
424  *
425  * <warning><para>The returned object may become invalid after a call to
426  * mio_unref() if the stream was configured to close the file when
427  * destroyed.</para></warning>
428  *
429  * Returns: The underlying #FILE object of the given stream, or %NULL if the
430  *          stream is not a file stream.
431  */
mio_file_get_fp(MIO * mio)432 FILE *mio_file_get_fp (MIO *mio)
433 {
434 	FILE *fp = NULL;
435 
436 	if (mio->type == MIO_TYPE_FILE)
437 		fp = mio->impl.file.fp;
438 
439 	return fp;
440 }
441 
442 /**
443  * mio_memory_get_data:
444  * @mio: A #MIO object
445  * @size: (allow-none) (out): Return location for the length of the returned
446  *        memory, or %NULL
447  *
448  * Gets the underlying memory buffer associated with a #MIO memory stream.
449  *
450  * <warning><para>The returned pointer and size may become invalid after a
451  * successful write on the stream or after a call to mio_unref() if the stream
452  * was configured to free the memory when destroyed.</para></warning>
453  *
454  * Returns: The memory buffer of the given #MIO stream, or %NULL if the stream
455  *          is not a memory stream.
456  */
mio_memory_get_data(MIO * mio,size_t * size)457 unsigned char *mio_memory_get_data (MIO *mio, size_t *size)
458 {
459 	unsigned char *ptr = NULL;
460 
461 	if (mio->type == MIO_TYPE_MEMORY)
462 	{
463 		ptr = mio->impl.mem.buf;
464 		if (size)
465 			*size = mio->impl.mem.size;
466 	}
467 
468 	return ptr;
469 }
470 
471 /**
472  * mio_unref:
473  * @mio: A #MIO object
474  *
475  * Decrements the reference counter of a #MIO and destroys the #MIO
476  * object if its counter becomes 0.
477  *
478  * Returns: Error code obtained from the registered MIOFCloseFunc or 0 on success.
479  */
mio_unref(MIO * mio)480 int mio_unref (MIO *mio)
481 {
482 	int rv = 0;
483 
484 	if (mio)
485 	{
486 		if (--mio->refcount)
487 			return 0;
488 
489 		if (mio->udata.d && mio->udata.f)
490 			mio->udata.f (mio->udata.d);
491 
492 		if (mio->type == MIO_TYPE_FILE)
493 		{
494 			if (mio->impl.file.close_func)
495 				rv = mio->impl.file.close_func (mio->impl.file.fp);
496 			mio->impl.file.close_func = NULL;
497 			mio->impl.file.fp = NULL;
498 		}
499 		else if (mio->type == MIO_TYPE_MEMORY)
500 		{
501 			if (mio->impl.mem.free_func)
502 				mio->impl.mem.free_func (mio->impl.mem.buf);
503 			mio->impl.mem.buf = NULL;
504 			mio->impl.mem.pos = 0;
505 			mio->impl.mem.size = 0;
506 			mio->impl.mem.allocated_size = 0;
507 			mio->impl.mem.realloc_func = NULL;
508 			mio->impl.mem.free_func = NULL;
509 			mio->impl.mem.eof = false;
510 			mio->impl.mem.error = false;
511 		}
512 		else
513 			AssertNotReached ();
514 
515 		eFree (mio);
516 	}
517 
518 	return rv;
519 }
520 
521 /**
522  * mio_read:
523  * @mio: A #MIO object
524  * @ptr: Pointer to the memory to fill with the read data
525  * @size: Size of each block to read
526  * @nmemb: Number o blocks to read
527  *
528  * Reads raw data from a #MIO stream. This function behave the same as fread().
529  *
530  * Returns: The number of actually read blocks. If an error occurs or if the
531  *          end of the stream is reached, the return value may be smaller than
532  *          the requested block count, or even 0. This function doesn't
533  *          distinguish between end-of-stream and an error, you should then use
534  *          mio_eof() and mio_error() to determine which occurred.
535  */
mio_read(MIO * mio,void * ptr_,size_t size,size_t nmemb)536 size_t mio_read (MIO *mio,
537 				 void *ptr_,
538 				 size_t size,
539 				 size_t nmemb)
540 {
541 	if (mio->type == MIO_TYPE_FILE)
542 		return fread (ptr_, size, nmemb, mio->impl.file.fp);
543 	else if (mio->type == MIO_TYPE_MEMORY)
544 	{
545 		size_t n_read = 0;
546 
547 		if (size != 0 && nmemb != 0)
548 		{
549 			size_t size_avail = mio->impl.mem.size - mio->impl.mem.pos;
550 			size_t copy_bytes = size * nmemb;
551 			unsigned char *ptr = ptr_;
552 
553 			if (size_avail < copy_bytes)
554 				copy_bytes = size_avail;
555 
556 			if (copy_bytes > 0)
557 			{
558 				n_read = copy_bytes / size;
559 
560 				if (mio->impl.mem.ungetch != EOF)
561 				{
562 					*ptr = (unsigned char) mio->impl.mem.ungetch;
563 					mio->impl.mem.ungetch = EOF;
564 					copy_bytes--;
565 					mio->impl.mem.pos++;
566 					ptr++;
567 				}
568 
569 				memcpy (ptr, &mio->impl.mem.buf[mio->impl.mem.pos], copy_bytes);
570 				mio->impl.mem.pos += copy_bytes;
571 			}
572 			if (mio->impl.mem.pos >= mio->impl.mem.size)
573 				mio->impl.mem.eof = true;
574 		}
575 
576 		return n_read;
577 	}
578 	else
579 	{
580 		AssertNotReached ();
581 		return 0;
582 	}
583 }
584 
585 /*
586  * mem_try_resize:
587  * @mio: A #MIO object of the type %MIO_TYPE_MEMORY
588  * @new_size: Requested new size
589  *
590  * Tries to resize the underlying buffer of an in-memory #MIO object.
591  * This supports both growing and shrinking.
592  *
593  * Returns: %true on success, %false otherwise.
594  */
mem_try_resize(MIO * mio,size_t new_size)595 static int mem_try_resize (MIO *mio, size_t new_size)
596 {
597 	int success = false;
598 
599 	if (mio->impl.mem.realloc_func)
600 	{
601 		if (new_size == ULONG_MAX)
602 		{
603 #ifdef EOVERFLOW
604 			errno = EOVERFLOW;
605 #endif
606 		}
607 		else
608 		{
609 			if (new_size > mio->impl.mem.size)
610 			{
611 				if (new_size <= mio->impl.mem.allocated_size)
612 				{
613 					mio->impl.mem.size = new_size;
614 					success = true;
615 				}
616 				else
617 				{
618 					size_t newsize;
619 					unsigned char *newbuf;
620 
621 					newsize = MAX (mio->impl.mem.allocated_size + MIO_CHUNK_SIZE,
622 								   new_size);
623 					newbuf = mio->impl.mem.realloc_func (mio->impl.mem.buf, newsize);
624 					if (newbuf)
625 					{
626 						mio->impl.mem.buf = newbuf;
627 						mio->impl.mem.allocated_size = newsize;
628 						mio->impl.mem.size = new_size;
629 						success = true;
630 					}
631 				}
632 			}
633 			else
634 			{
635 				unsigned char *newbuf;
636 
637 				newbuf = mio->impl.mem.realloc_func (mio->impl.mem.buf, new_size);
638 				if (newbuf || new_size == 0)
639 				{
640 					mio->impl.mem.buf = newbuf;
641 					mio->impl.mem.allocated_size = new_size;
642 					mio->impl.mem.size = new_size;
643 					success = true;
644 				}
645 			}
646 		}
647 	}
648 
649 	return success;
650 }
651 
mio_try_resize(MIO * mio,size_t new_size)652 int mio_try_resize (MIO *mio, size_t new_size)
653 {
654 	return mem_try_resize (mio, new_size);
655 }
656 
657 /*
658  * mem_try_ensure_space:
659  * @mio: A #MIO object
660  * @n: Requested size from the current (cursor) position
661  *
662  * Tries to ensure there is enough space for @n bytes to be written from the
663  * current cursor position.
664  *
665  * Returns: %true if there is enough space, %false otherwise.
666  */
mem_try_ensure_space(MIO * mio,size_t n)667 static int mem_try_ensure_space (MIO *mio, size_t n)
668 {
669 	int success = true;
670 
671 	if (mio->impl.mem.pos + n > mio->impl.mem.size)
672 		success = mem_try_resize (mio, mio->impl.mem.pos + n);
673 
674 	return success;
675 }
676 
677 /**
678  * mio_write:
679  * @mio: A #MIO object
680  * @ptr: Pointer to the memory to write on the stream
681  * @size: Size of each block to write
682  * @nmemb: Number of block to write
683  *
684  * Writes raw data to a #MIO stream. This function behaves the same as fwrite().
685  *
686  * Returns: The number of blocks actually written to the stream. This might be
687  *          smaller than the requested count if a write error occurs.
688  */
mio_write(MIO * mio,const void * ptr,size_t size,size_t nmemb)689 size_t mio_write (MIO *mio,
690 				  const void *ptr,
691 				  size_t size,
692 				  size_t nmemb)
693 {
694 	if (mio->type == MIO_TYPE_FILE)
695 		return fwrite (ptr, size, nmemb, mio->impl.file.fp);
696 	else if (mio->type == MIO_TYPE_MEMORY)
697 	{
698 		size_t n_written = 0;
699 
700 		if (size != 0 && nmemb != 0)
701 		{
702 			if (mem_try_ensure_space (mio, size * nmemb))
703 			{
704 				memcpy (&mio->impl.mem.buf[mio->impl.mem.pos], ptr, size * nmemb);
705 				mio->impl.mem.pos += size * nmemb;
706 				n_written = nmemb;
707 			}
708 		}
709 
710 		return n_written;
711 	}
712 	else
713 	{
714 		AssertNotReached ();
715 		return 0;
716 	}
717 }
718 
719 /**
720  * mio_putc:
721  * @mio: A #MIO object
722  * @c: The character to write
723  *
724  * Writes a character to a #MIO stream. This function behaves the same as
725  * fputc().
726  *
727  * Returns: The written character, or %EOF on error.
728  */
mio_putc(MIO * mio,int c)729 int mio_putc (MIO *mio, int c)
730 {
731 	if (mio->type == MIO_TYPE_FILE)
732 		return fputc (c, mio->impl.file.fp);
733 	else if (mio->type == MIO_TYPE_MEMORY)
734 	{
735 		int rv = EOF;
736 
737 		if (mem_try_ensure_space (mio, 1))
738 		{
739 			mio->impl.mem.buf[mio->impl.mem.pos] = (unsigned char)c;
740 			mio->impl.mem.pos++;
741 			rv = (int)((unsigned char)c);
742 		}
743 
744 		return rv;
745 	}
746 	else
747 	{
748 		AssertNotReached ();
749 		return 0;
750 	}
751 }
752 
753 /**
754  * mio_puts:
755  * @mio: A #MIO object
756  * @s: The string to write
757  *
758  * Writes a string to a #MIO object. This function behaves the same as fputs().
759  *
760  * Returns: A non-negative integer on success or %EOF on failure.
761  */
mio_puts(MIO * mio,const char * s)762 int mio_puts (MIO *mio, const char *s)
763 {
764 	if (mio->type == MIO_TYPE_FILE)
765 		return fputs (s, mio->impl.file.fp);
766 	else if (mio->type == MIO_TYPE_MEMORY)
767 	{
768 		int rv = EOF;
769 		size_t len;
770 
771 		len = strlen (s);
772 		if (mem_try_ensure_space (mio, len))
773 		{
774 			memcpy (&mio->impl.mem.buf[mio->impl.mem.pos], s, len);
775 			mio->impl.mem.pos += len;
776 			rv = 1;
777 		}
778 
779 		return rv;
780 	}
781 	else
782 	{
783 		AssertNotReached ();
784 		return 0;
785 	}
786 }
787 
788 /**
789  * mio_vprintf:
790  * @mio: A #MIO object
791  * @format: A printf format string
792  * @ap: The variadic argument list for the format
793  *
794  * Writes a formatted string into a #MIO stream. This function behaves the same
795  * as vfprintf().
796  *
797  * Returns: The number of bytes written in the stream, or a negative value on
798  *          failure.
799  */
mio_vprintf(MIO * mio,const char * format,va_list ap)800 int mio_vprintf (MIO *mio, const char *format, va_list ap)
801 {
802 	if (mio->type == MIO_TYPE_FILE)
803 		return vfprintf (mio->impl.file.fp, format, ap);
804 	else if (mio->type == MIO_TYPE_MEMORY)
805 	{
806 		int rv = -1;
807 		size_t n;
808 		size_t old_pos;
809 		size_t old_size;
810 		va_list ap_copy;
811 		char dummy;
812 
813 		old_pos = mio->impl.mem.pos;
814 		old_size = mio->impl.mem.size;
815 		va_copy (ap_copy, ap);
816 		/* compute the size we will need into the buffer */
817 		n = vsnprintf (&dummy, 1, format, ap_copy) + 1;
818 		va_end (ap_copy);
819 		if (mem_try_ensure_space (mio, n))
820 		{
821 			unsigned char c;
822 
823 			/* backup character at n+1 that will be overwritten by a \0 ... */
824 			c = mio->impl.mem.buf[mio->impl.mem.pos + (n - 1)];
825 			rv = vsprintf ((char *)&mio->impl.mem.buf[mio->impl.mem.pos], format, ap);
826 			/* ...and restore it */
827 			mio->impl.mem.buf[mio->impl.mem.pos + (n - 1)] = c;
828 			if (rv >= 0 && (size_t)rv == (n - 1))
829 			{
830 				/* re-compute the actual size since we might have allocated one byte
831 				 * more than needed */
832 				mio->impl.mem.size = MAX (old_size, old_pos + (unsigned int)rv);
833 				mio->impl.mem.pos += (unsigned int)rv;
834 			}
835 			else
836 			{
837 				mio->impl.mem.size = old_size;
838 				rv = -1;
839 			}
840 		}
841 
842 		return rv;
843 	}
844 	else
845 	{
846 		AssertNotReached ();
847 		return 0;
848 	}
849 }
850 
851 /**
852  * mio_printf:
853  * @mio: A #MIO object
854  * @format: A print format string
855  * @...: Arguments of the format
856  *
857  * Writes a formatted string to a #MIO stream. This function behaves the same as
858  * fprintf().
859  *
860  * Returns: The number of bytes written to the stream, or a negative value on
861  *          failure.
862  */
mio_printf(MIO * mio,const char * format,...)863 int mio_printf (MIO *mio, const char *format, ...)
864 {
865 	int rv;
866 	va_list ap;
867 
868 	va_start (ap, format);
869 	rv = mio_vprintf (mio, format, ap);
870 	va_end (ap);
871 
872 	return rv;
873 }
874 
875 /**
876  * mio_getc:
877  * @mio: A #MIO object
878  *
879  * Gets the current character from a #MIO stream. This function behaves the same
880  * as fgetc().
881  *
882  * Returns: The read character as a #int, or %EOF on error.
883  */
mio_getc(MIO * mio)884 int mio_getc (MIO *mio)
885 {
886 	if (mio->type == MIO_TYPE_FILE)
887 		return fgetc (mio->impl.file.fp);
888 	else if (mio->type == MIO_TYPE_MEMORY)
889 	{
890 		int rv = EOF;
891 
892 		if (mio->impl.mem.ungetch != EOF)
893 		{
894 			rv = mio->impl.mem.ungetch;
895 			mio->impl.mem.ungetch = EOF;
896 			mio->impl.mem.pos++;
897 		}
898 		else if (mio->impl.mem.pos < mio->impl.mem.size)
899 		{
900 			rv = mio->impl.mem.buf[mio->impl.mem.pos];
901 			mio->impl.mem.pos++;
902 		}
903 		else
904 			mio->impl.mem.eof = true;
905 
906 		return rv;
907 	}
908 	else
909 	{
910 		AssertNotReached ();
911 		return 0;
912 	}
913 }
914 
915 /**
916  * mio_ungetc:
917  * @mio: A #MIO object
918  * @ch: Character to put back in the stream
919  *
920  * Puts a character back in a #MIO stream. This function behaves the sames as
921  * ungetc().
922  *
923  * <warning><para>It is only guaranteed that one character can be but back at a
924  * time, even if the implementation may allow more.</para></warning>
925  * <warning><para>Using this function while the stream cursor is at offset 0 is
926  * not guaranteed to function properly. As the C99 standard says, it is "an
927  * obsolescent feature".</para></warning>
928  *
929  * Returns: The character put back, or %EOF on error.
930  */
mio_ungetc(MIO * mio,int ch)931 int mio_ungetc (MIO *mio, int ch)
932 {
933 	if (mio->type == MIO_TYPE_FILE)
934 		return ungetc (ch, mio->impl.file.fp);
935 	else if (mio->type == MIO_TYPE_MEMORY)
936 	{
937 		int rv = EOF;
938 
939 		if (ch != EOF && mio->impl.mem.ungetch == EOF)
940 		{
941 			rv = mio->impl.mem.ungetch = ch;
942 			mio->impl.mem.pos--;
943 			mio->impl.mem.eof = false;
944 		}
945 
946 		return rv;
947 	}
948 	else
949 	{
950 		AssertNotReached ();
951 		return 0;
952 	}
953 }
954 
955 /**
956  * mio_gets:
957  * @mio: A #MIO object
958  * @s: A string to fill with the read data
959  * @size: The maximum number of bytes to read
960  *
961  * Reads a string from a #MIO stream, stopping after the first new-line
962  * character or at the end of the stream. This function behaves the same as
963  * fgets().
964  *
965  * Returns: @s on success, %NULL otherwise.
966  */
mio_gets(MIO * mio,char * s,size_t size)967 char *mio_gets (MIO *mio, char *s, size_t size)
968 {
969 	if (mio->type == MIO_TYPE_FILE)
970 		return fgets (s, (int)size, mio->impl.file.fp);
971 	else if (mio->type == MIO_TYPE_MEMORY)
972 	{
973 		char *rv = NULL;
974 
975 		if (size > 0)
976 		{
977 			size_t i = 0;
978 			bool newline = false;
979 			/* Avoiding dereference inside the for loop below improves
980 			 * performance so we do it here. */
981 			size_t pos = mio->impl.mem.pos;
982 			size_t buf_size = mio->impl.mem.size;
983 			unsigned char *buf = mio->impl.mem.buf;
984 
985 			if (mio->impl.mem.ungetch != EOF)
986 			{
987 				s[i] = (char)mio->impl.mem.ungetch;
988 				mio->impl.mem.ungetch = EOF;
989 				pos++;
990 				i++;
991 			}
992 			for (; pos < buf_size && i < (size - 1); i++)
993 			{
994 				s[i] = (char)buf[pos];
995 				pos++;
996 				if (s[i] == '\n')
997 				{
998 					i++;
999 					newline = true;
1000 					break;
1001 				}
1002 			}
1003 			if (i > 0)
1004 			{
1005 				s[i] = 0;
1006 				rv = s;
1007 			}
1008 			if (!newline && pos >= buf_size)
1009 				mio->impl.mem.eof = true;
1010 			mio->impl.mem.pos = pos;
1011 			mio->impl.mem.size = buf_size;
1012 		}
1013 
1014 		return rv;
1015 	}
1016 	else
1017 	{
1018 		AssertNotReached ();
1019 		return 0;
1020 	}
1021 }
1022 
1023 /**
1024  * mio_clearerr:
1025  * @mio: A #MIO object
1026  *
1027  * Clears the error and end-of-stream indicators of a #MIO stream. This function
1028  * behaves the same as clearerr().
1029  */
mio_clearerr(MIO * mio)1030 void mio_clearerr (MIO *mio)
1031 {
1032 	if (mio->type == MIO_TYPE_FILE)
1033 		clearerr (mio->impl.file.fp);
1034 	else if (mio->type == MIO_TYPE_MEMORY)
1035 	{
1036 		mio->impl.mem.error = false;
1037 		mio->impl.mem.eof = false;
1038 	}
1039 	else
1040 		AssertNotReached ();
1041 }
1042 
1043 /**
1044  * mio_eof:
1045  * @mio: A #MIO object
1046  *
1047  * Checks whether the end-of-stream indicator of a #MIO stream is set. This
1048  * function behaves the same as feof().
1049  *
1050  * Returns: A non-null value if the stream reached its end, 0 otherwise.
1051  */
mio_eof(MIO * mio)1052 int mio_eof (MIO *mio)
1053 {
1054 	if (mio->type == MIO_TYPE_FILE)
1055 		return feof (mio->impl.file.fp);
1056 	else if (mio->type == MIO_TYPE_MEMORY)
1057 		return mio->impl.mem.eof != false;
1058 	else
1059 	{
1060 		AssertNotReached ();
1061 		return 0;
1062 	}
1063 }
1064 
1065 /**
1066  * mio_error:
1067  * @mio: A #MIO object
1068  *
1069  * Checks whether the error indicator of a #MIO stream is set. This function
1070  * behaves the same as ferror().
1071  *
1072  * Returns: A non-null value if the stream have an error set, 0 otherwise.
1073  */
mio_error(MIO * mio)1074 int mio_error (MIO *mio)
1075 {
1076 	if (mio->type == MIO_TYPE_FILE)
1077 		return ferror (mio->impl.file.fp);
1078 	else if (mio->type == MIO_TYPE_MEMORY)
1079 		return mio->impl.mem.error != false;
1080 	else
1081 	{
1082 		AssertNotReached ();
1083 		return 0;
1084 	}
1085 }
1086 
1087 /**
1088  * mio_seek:
1089  * @mio: A #MIO object
1090  * @offset: Offset of the new place, from @whence
1091  * @whence: Move origin. SEEK_SET moves relative to the start of the stream,
1092  *          SEEK_CUR from the current position and SEEK_SET from the end of the
1093  *          stream.
1094  *
1095  * Sets the cursor position on a #MIO stream. This functions behaves the same as
1096  * fseek(). See also mio_tell() and mio_setpos().
1097  *
1098  * Returns: 0 on success, -1 otherwise, in which case errno should be set to
1099  *          indicate the error.
1100  */
mio_seek(MIO * mio,long offset,int whence)1101 int mio_seek (MIO *mio, long offset, int whence)
1102 {
1103 	if (mio->type == MIO_TYPE_FILE)
1104 		return fseek (mio->impl.file.fp, offset, whence);
1105 	else if (mio->type == MIO_TYPE_MEMORY)
1106 	{
1107 		/* FIXME: should we support seeking out of bounds like lseek() seems to do? */
1108 		int rv = -1;
1109 
1110 		switch (whence)
1111 		{
1112 			case SEEK_SET:
1113 				if (offset < 0 || (size_t)offset > mio->impl.mem.size)
1114 					errno = EINVAL;
1115 				else
1116 				{
1117 					mio->impl.mem.pos = (size_t)offset;
1118 					rv = 0;
1119 				}
1120 				break;
1121 
1122 			case SEEK_CUR:
1123 				if ((offset < 0 && (size_t)-offset > mio->impl.mem.pos) ||
1124 					mio->impl.mem.pos + (size_t)offset > mio->impl.mem.size)
1125 				{
1126 					errno = EINVAL;
1127 				}
1128 				else
1129 				{
1130 					mio->impl.mem.pos = (size_t)(mio->impl.mem.pos + offset);
1131 					rv = 0;
1132 				}
1133 				break;
1134 
1135 			case SEEK_END:
1136 				if (offset > 0 || (size_t)-offset > mio->impl.mem.size)
1137 					errno = EINVAL;
1138 				else
1139 				{
1140 					mio->impl.mem.pos = mio->impl.mem.size - (size_t)-offset;
1141 					rv = 0;
1142 				}
1143 				break;
1144 
1145 			default:
1146 				errno = EINVAL;
1147 		}
1148 		if (rv == 0)
1149 		{
1150 			mio->impl.mem.eof = false;
1151 			mio->impl.mem.ungetch = EOF;
1152 		}
1153 
1154 		return rv;
1155 	}
1156 	else
1157 	{
1158 		AssertNotReached ();
1159 		return 0;
1160 	}
1161 
1162 }
1163 
1164 /**
1165  * mio_tell:
1166  * @mio: A #MIO object
1167  *
1168  * Gets the current cursor position of a #MIO stream. This function behaves the
1169  * same as ftell().
1170  *
1171  * Returns: The current offset from the start of the stream, or -1 or error, in
1172  *          which case errno is set to indicate the error.
1173  */
mio_tell(MIO * mio)1174 long mio_tell (MIO *mio)
1175 {
1176 	if (mio->type == MIO_TYPE_FILE)
1177 		return ftell (mio->impl.file.fp);
1178 	else if (mio->type == MIO_TYPE_MEMORY)
1179 	{
1180 		long rv = -1;
1181 
1182 		if (mio->impl.mem.pos > LONG_MAX)
1183 		{
1184 #ifdef EOVERFLOW
1185 			errno = EOVERFLOW;
1186 #endif
1187 		}
1188 		else
1189 			rv = (long)mio->impl.mem.pos;
1190 
1191 		return rv;
1192 	}
1193 	else
1194 	{
1195 		AssertNotReached ();
1196 		return 0;
1197 	}
1198 }
1199 
1200 /**
1201  * mio_rewind:
1202  * @mio: A #MIO object
1203  *
1204  * Resets the cursor position to 0, and also the end-of-stream and the error
1205  * indicators of a #MIO stream.
1206  * See also mio_seek() and mio_clearerr().
1207  */
mio_rewind(MIO * mio)1208 void mio_rewind (MIO *mio)
1209 {
1210 	if (mio->type == MIO_TYPE_FILE)
1211 		rewind (mio->impl.file.fp);
1212 	else if (mio->type == MIO_TYPE_MEMORY)
1213 	{
1214 		mio->impl.mem.pos = 0;
1215 		mio->impl.mem.ungetch = EOF;
1216 		mio->impl.mem.eof = false;
1217 		mio->impl.mem.error = false;
1218 	}
1219 	else
1220 		AssertNotReached ();
1221 }
1222 
1223 /**
1224  * mio_getpos:
1225  * @mio: A #MIO stream
1226  * @pos: (out): A #MIOPos object to fill-in
1227  *
1228  * Stores the current position (and maybe other informations about the stream
1229  * state) of a #MIO stream in order to restore it later with mio_setpos(). This
1230  * function behaves the same as fgetpos().
1231  *
1232  * Returns: 0 on success, -1 otherwise, in which case errno is set to indicate
1233  *          the error.
1234  */
mio_getpos(MIO * mio,MIOPos * pos)1235 int mio_getpos (MIO *mio, MIOPos *pos)
1236 {
1237 	int rv = -1;
1238 
1239 	pos->type = mio->type;
1240 	if (mio->type == MIO_TYPE_FILE)
1241 		rv = fgetpos (mio->impl.file.fp, &pos->impl.file);
1242 	else if (mio->type == MIO_TYPE_MEMORY)
1243 	{
1244 		rv = -1;
1245 
1246 		if (mio->impl.mem.pos == (size_t)-1)
1247 		{
1248 			/* this happens if ungetc() was called at the start of the stream */
1249 #ifdef EIO
1250 			errno = EIO;
1251 #endif
1252 		}
1253 		else
1254 		{
1255 			pos->impl.mem = mio->impl.mem.pos;
1256 			rv = 0;
1257 		}
1258 	}
1259 	else
1260 		AssertNotReached();
1261 
1262 #ifdef MIO_DEBUG
1263 	if (rv != -1)
1264 	{
1265 		pos->tag = mio;
1266 	}
1267 #endif /* MIO_DEBUG */
1268 
1269 	return rv;
1270 }
1271 
1272 /**
1273  * mio_setpos:
1274  * @mio: A #MIO object
1275  * @pos: (in): A #MIOPos object filled-in by a previous call of mio_getpos() on
1276  *       the same stream
1277  *
1278  * Restores the position and state indicators of a #MIO stream previously saved
1279  * by mio_getpos().
1280  *
1281  * <warning><para>The #MIOPos object must have been initialized by a previous
1282  * call to mio_getpos() on the same stream.</para></warning>
1283  *
1284  * Returns: 0 on success, -1 otherwise, in which case errno is set to indicate
1285  *          the error.
1286  */
mio_setpos(MIO * mio,MIOPos * pos)1287 int mio_setpos (MIO *mio, MIOPos *pos)
1288 {
1289 	int rv = -1;
1290 
1291 #ifdef MIO_DEBUG
1292 	if (pos->tag != mio)
1293 	{
1294 		g_critical ("mio_setpos((MIO*)%p, (MIOPos*)%p): "
1295 					"Given MIOPos was not set by a previous call to mio_getpos() "
1296 					"on the same MIO object, which means there is a bug in "
1297 					"someone's code.",
1298 					(void *)mio, (void *)pos);
1299 		errno = EINVAL;
1300 		return -1;
1301 	}
1302 #endif /* MIO_DEBUG */
1303 
1304 	if (mio->type == MIO_TYPE_FILE)
1305 		rv = fsetpos (mio->impl.file.fp, &pos->impl.file);
1306 	else if (mio->type == MIO_TYPE_MEMORY)
1307 	{
1308 		rv = -1;
1309 
1310 		if (pos->impl.mem > mio->impl.mem.size)
1311 			errno = EINVAL;
1312 		else
1313 		{
1314 			mio->impl.mem.ungetch = EOF;
1315 			mio->impl.mem.pos = pos->impl.mem;
1316 			rv = 0;
1317 		}
1318 	}
1319 	else
1320 		AssertNotReached ();
1321 
1322 	return rv;
1323 }
1324 
1325 /**
1326  * mio_flush:
1327  * @mio: A #MIO object
1328  *
1329  * Forces a write of all user-space buffered data for the given output or update
1330  * stream via the stream's underlying write function. Only applicable when using
1331  * FILE back-end.
1332  *
1333  * Returns: 0 on success, error code otherwise.
1334  */
mio_flush(MIO * mio)1335 int mio_flush (MIO *mio)
1336 {
1337 	if (mio->type == MIO_TYPE_FILE)
1338 		return fflush (mio->impl.file.fp);
1339 	return 0;
1340 }
1341 
1342 
1343 /**
1344  * mio_attach_user_data:
1345  * @mio: A #MIO object
1346  * @user_data: a pointer to any data object
1347  * @user_data_free_func: a call back function to destroy the data object passed as @user_data
1348  *
1349  * Attach any data object to a #MIO object. Attached data can be got with
1350  * mio_get_user_data(). The attached data is destroyed when new data is attached to
1351  * the #MIO object, or #the MIO object itself is destroyed. For destroying the data
1352  * @user_data_free_func is used.
1353  *
1354  */
1355 
mio_attach_user_data(MIO * mio,void * user_data,MIODestroyNotify user_data_free_func)1356 void  mio_attach_user_data (MIO *mio, void *user_data, MIODestroyNotify user_data_free_func)
1357 {
1358 	if (mio->udata.d && mio->udata.f)
1359 		mio->udata.f (mio->udata.d);
1360 
1361 	mio->udata.d = user_data;
1362 	mio->udata.f = user_data_free_func;
1363 }
1364 
1365 /**
1366  * mio_get_user_data:
1367  * @mio: A #MIO object
1368  *
1369  * Returns: user data attached with mio_attach_user_data() to a #MIO object.
1370  *
1371  */
mio_get_user_data(MIO * mio)1372 void *mio_get_user_data (MIO *mio)
1373 {
1374 	return mio->udata.d;
1375 }
1376