569670b771
Moved the parsers to MIO, which allows both in-memory and file parsing. git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@5555 ea778897-0a13-0410-b9d1-a72fbfd435f5
622 lines
16 KiB
C
622 lines
16 KiB
C
/*
|
|
* MIO, an I/O abstraction layer replicating C file I/O API.
|
|
* Copyright (C) 2010 Colomban Wendling <ban@herbesfolles.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
#include "mio.h"
|
|
#include "mio-file.c"
|
|
#include "mio-memory.c"
|
|
|
|
#include <glib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
/**
|
|
* mio_new_file_full:
|
|
* @filename: Filename to open, passed as-is to @open_func as the first argument
|
|
* @mode: Mode in which open the file, passed as-is to @open_func as the second
|
|
* argument
|
|
* @open_func: A function with the fopen() semantic to use to open the file
|
|
* @close_func: A function with the fclose() semantic to close the file when
|
|
* the #MIO object is destroyed, or %NULL not to close the #FILE
|
|
* object
|
|
*
|
|
* Creates a new #MIO object working on a file, from a filename and an opening
|
|
* function. See also mio_new_file().
|
|
*
|
|
* This function is generally overkill and mio_new_file() should often be used
|
|
* instead, but it allows to specify a custom function to open a file, as well
|
|
* as a close function. The former is useful e.g. if you need to wrap fopen()
|
|
* for some reason (like filename encoding conversion for example), and the
|
|
* latter allows you both to match your custom open function and to choose
|
|
* whether the underlying #FILE object should or not be closed when mio_free()
|
|
* is called on the returned object.
|
|
*
|
|
* Free-function: mio_free()
|
|
*
|
|
* Returns: A new #MIO on success, or %NULL on failure.
|
|
*/
|
|
MIO *
|
|
mio_new_file_full (const gchar *filename,
|
|
const gchar *mode,
|
|
MIOFOpenFunc open_func,
|
|
MIOFCloseFunc close_func)
|
|
{
|
|
MIO *mio;
|
|
|
|
/* we need to create the MIO object first, because we may not be able to close
|
|
* the opened file if the user passed NULL as the close function, which means
|
|
* that everything must succeed if we've opened the file successfully */
|
|
mio = g_slice_alloc (sizeof *mio);
|
|
if (mio) {
|
|
FILE *fp = open_func (filename, mode);
|
|
|
|
if (! fp) {
|
|
g_slice_free1 (sizeof *mio, mio);
|
|
mio = NULL;
|
|
} else {
|
|
mio->type = MIO_TYPE_FILE;
|
|
mio->impl.file.fp = fp;
|
|
mio->impl.file.close_func = close_func;
|
|
/* function table filling */
|
|
FILE_SET_VTABLE (mio);
|
|
}
|
|
}
|
|
|
|
return mio;
|
|
}
|
|
|
|
/**
|
|
* mio_new_file:
|
|
* @filename: Filename to open, same as the fopen()'s first argument
|
|
* @mode: Mode in which open the file, fopen()'s second argument
|
|
*
|
|
* Creates a new #MIO object working on a file from a filename; wrapping
|
|
* fopen().
|
|
* This function simply calls mio_new_file_full() with the libc's fopen() and
|
|
* fclose() functions.
|
|
*
|
|
* Free-function: mio_free()
|
|
*
|
|
* Returns: A new #MIO on success, or %NULL on failure.
|
|
*/
|
|
MIO *
|
|
mio_new_file (const gchar *filename,
|
|
const gchar *mode)
|
|
{
|
|
return mio_new_file_full (filename, mode, fopen, fclose);
|
|
}
|
|
|
|
/**
|
|
* mio_new_fp:
|
|
* @fp: An opened #FILE object
|
|
* @close_func: (allow-none): Function used to close @fp when the #MIO object
|
|
* gets destroyed, or %NULL not to close the #FILE object
|
|
*
|
|
* Creates a new #MIO object working on a file, from an already opened #FILE
|
|
* object.
|
|
*
|
|
* <example>
|
|
* <title>Typical use of this function</title>
|
|
* <programlisting>
|
|
* MIO *mio = mio_new_fp (fp, fclose);
|
|
* </programlisting>
|
|
* </example>
|
|
*
|
|
* Free-function: mio_free()
|
|
*
|
|
* Returns: A new #MIO on success or %NULL on failure.
|
|
*/
|
|
MIO *
|
|
mio_new_fp (FILE *fp,
|
|
MIOFCloseFunc close_func)
|
|
{
|
|
MIO *mio;
|
|
|
|
mio = g_slice_alloc (sizeof *mio);
|
|
if (mio) {
|
|
mio->type = MIO_TYPE_FILE;
|
|
mio->impl.file.fp = fp;
|
|
mio->impl.file.close_func = close_func;
|
|
/* function table filling */
|
|
FILE_SET_VTABLE (mio);
|
|
}
|
|
|
|
return mio;
|
|
}
|
|
|
|
/**
|
|
* mio_new_memory:
|
|
* @data: Initial data (may be %NULL)
|
|
* @size: Length of @data in bytes
|
|
* @realloc_func: A function with the realloc() semantic used to grow the
|
|
* buffer, or %NULL to disable buffer growing
|
|
* @free_func: A function with the free() semantic to destroy the data together
|
|
* with the object, or %NULL not to destroy the data
|
|
*
|
|
* Creates a new #MIO object working on memory.
|
|
*
|
|
* To allow the buffer to grow, you must provide a @realloc_func, otherwise
|
|
* trying to write after the end of the current data will fail.
|
|
*
|
|
* If you want the buffer to be freed together with the #MIO object, you must
|
|
* give a @free_func; otherwise the data will still live after #MIO object
|
|
* termination.
|
|
*
|
|
* <example>
|
|
* <title>Basic creation of a non-growable, freeable #MIO object</title>
|
|
* <programlisting>
|
|
* MIO *mio = mio_new_memory (data, size, NULL, g_free);
|
|
* </programlisting>
|
|
* </example>
|
|
*
|
|
* <example>
|
|
* <title>Basic creation of an empty growable and freeable #MIO object</title>
|
|
* <programlisting>
|
|
* MIO *mio = mio_new_memory (NULL, 0, g_try_realloc, g_free);
|
|
* </programlisting>
|
|
* </example>
|
|
*
|
|
* Free-function: mio_free()
|
|
*
|
|
* Returns: A new #MIO on success, or %NULL on failure.
|
|
*/
|
|
MIO *
|
|
mio_new_memory (guchar *data,
|
|
gsize size,
|
|
MIOReallocFunc realloc_func,
|
|
GDestroyNotify free_func)
|
|
{
|
|
MIO *mio;
|
|
|
|
mio = g_slice_alloc (sizeof *mio);
|
|
if (mio) {
|
|
mio->type = MIO_TYPE_MEMORY;
|
|
mio->impl.mem.buf = data;
|
|
mio->impl.mem.ungetch = EOF;
|
|
mio->impl.mem.pos = 0;
|
|
mio->impl.mem.size = size;
|
|
mio->impl.mem.allocated_size = size;
|
|
mio->impl.mem.realloc_func = realloc_func;
|
|
mio->impl.mem.free_func = free_func;
|
|
mio->impl.mem.eof = FALSE;
|
|
mio->impl.mem.error = FALSE;
|
|
/* function table filling */
|
|
MEM_SET_VTABLE (mio);
|
|
}
|
|
|
|
return mio;
|
|
}
|
|
|
|
/**
|
|
* mio_file_get_fp:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Gets the underlying #FILE object associated with a #MIO file stream.
|
|
*
|
|
* <warning><para>The returned object may become invalid after a call to
|
|
* mio_free() if the stream was configured to close the file when
|
|
* destroyed.</para></warning>
|
|
*
|
|
* Returns: The underlying #FILE object of the given stream, or %NULL if the
|
|
* stream is not a file stream.
|
|
*/
|
|
FILE *
|
|
mio_file_get_fp (MIO *mio)
|
|
{
|
|
FILE *fp = NULL;
|
|
|
|
if (mio->type == MIO_TYPE_FILE) {
|
|
fp = mio->impl.file.fp;
|
|
}
|
|
|
|
return fp;
|
|
}
|
|
|
|
/**
|
|
* mio_memory_get_data:
|
|
* @mio: A #MIO object
|
|
* @size: (allow-none) (out): Return location for the length of the returned
|
|
* memory, or %NULL
|
|
*
|
|
* Gets the underlying memory buffer associated with a #MIO memory stream.
|
|
*
|
|
* <warning><para>The returned pointer and size may become invalid after a
|
|
* successful write on the stream or after a call to mio_free() if the stream
|
|
* was configured to free the memory when destroyed.</para></warning>
|
|
*
|
|
* Returns: The memory buffer of the given #MIO stream, or %NULL if the stream
|
|
* is not a memory stream.
|
|
*/
|
|
guchar *
|
|
mio_memory_get_data (MIO *mio,
|
|
gsize *size)
|
|
{
|
|
guchar *ptr = NULL;
|
|
|
|
if (mio->type == MIO_TYPE_MEMORY) {
|
|
ptr = mio->impl.mem.buf;
|
|
if (size) *size = mio->impl.mem.size;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/**
|
|
* mio_free:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Destroys a #MIO object.
|
|
*/
|
|
void
|
|
mio_free (MIO *mio)
|
|
{
|
|
if (mio) {
|
|
mio->v_free (mio);
|
|
g_slice_free1 (sizeof *mio, mio);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mio_read:
|
|
* @mio: A #MIO object
|
|
* @ptr: Pointer to the memory to fill with the read data
|
|
* @size: Size of each block to read
|
|
* @nmemb: Number o blocks to read
|
|
*
|
|
* Reads raw data from a #MIO stream. This function behave the same as fread().
|
|
*
|
|
* Returns: The number of actually read blocks. If an error occurs or if the
|
|
* end of the stream is reached, the return value may be smaller than
|
|
* the requested block count, or even 0. This function doesn't
|
|
* distinguish between end-of-stream and an error, you should then use
|
|
* mio_eof() and mio_error() to determine which occurred.
|
|
*/
|
|
gsize
|
|
mio_read (MIO *mio,
|
|
void *ptr,
|
|
gsize size,
|
|
gsize nmemb)
|
|
{
|
|
return mio->v_read (mio, ptr, size, nmemb);
|
|
}
|
|
|
|
/**
|
|
* mio_write:
|
|
* @mio: A #MIO object
|
|
* @ptr: Pointer to the memory to write on the stream
|
|
* @size: Size of each block to write
|
|
* @nmemb: Number of block to write
|
|
*
|
|
* Writes raw data to a #MIO stream. This function behaves the same as fwrite().
|
|
*
|
|
* Returns: The number of blocks actually written to the stream. This might be
|
|
* smaller than the requested count if a write error occurs.
|
|
*/
|
|
gsize
|
|
mio_write (MIO *mio,
|
|
const void *ptr,
|
|
gsize size,
|
|
gsize nmemb)
|
|
{
|
|
return mio->v_write (mio, ptr, size, nmemb);
|
|
}
|
|
|
|
/**
|
|
* mio_putc:
|
|
* @mio: A #MIO object
|
|
* @c: The character to write
|
|
*
|
|
* Writes a character to a #MIO stream. This function behaves the same as
|
|
* fputc().
|
|
*
|
|
* Returns: The written wharacter, or %EOF on error.
|
|
*/
|
|
gint
|
|
mio_putc (MIO *mio,
|
|
gint c)
|
|
{
|
|
return mio->v_putc (mio, c);
|
|
}
|
|
|
|
/**
|
|
* mio_puts:
|
|
* @mio: A #MIO object
|
|
* @s: The string to write
|
|
*
|
|
* Writes a string to a #MIO object. This function behaves the same as fputs().
|
|
*
|
|
* Returns: A non-negative integer on success or %EOF on failure.
|
|
*/
|
|
gint
|
|
mio_puts (MIO *mio,
|
|
const gchar *s)
|
|
{
|
|
return mio->v_puts (mio, s);
|
|
}
|
|
|
|
/**
|
|
* mio_vprintf:
|
|
* @mio: A #MIO object
|
|
* @format: A printf fomrat string
|
|
* @ap: The variadic argument list for the format
|
|
*
|
|
* Writes a formatted string into a #MIO stream. This function behaves the same
|
|
* as vfprintf().
|
|
*
|
|
* Returns: The number of bytes written in the stream, or a negative value on
|
|
* failure.
|
|
*/
|
|
gint
|
|
mio_vprintf (MIO *mio,
|
|
const gchar *format,
|
|
va_list ap)
|
|
{
|
|
return mio->v_vprintf (mio, format, ap);
|
|
}
|
|
|
|
/**
|
|
* mio_printf:
|
|
* @mio: A #MIO object
|
|
* @format: A print format string
|
|
* @...: Arguments of the format
|
|
*
|
|
* Writes a formatted string to a #MIO stream. This function behaves the same as
|
|
* fprintf().
|
|
*
|
|
* Returns: The number of bytes written to the stream, or a negative value on
|
|
* failure.
|
|
*/
|
|
gint
|
|
mio_printf (MIO *mio,
|
|
const gchar *format,
|
|
...)
|
|
{
|
|
gint rv;
|
|
va_list ap;
|
|
|
|
va_start (ap, format);
|
|
rv = mio->v_vprintf (mio, format, ap);
|
|
va_end (ap);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* mio_getc:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Gets the current character from a #MIO stream. This function behaves the same
|
|
* as fgetc().
|
|
*
|
|
* Returns: The read character as a #gint, or %EOF on error.
|
|
*/
|
|
gint
|
|
mio_getc (MIO *mio)
|
|
{
|
|
return mio->v_getc (mio);
|
|
}
|
|
|
|
/**
|
|
* mio_ungetc:
|
|
* @mio: A #MIO object
|
|
* @ch: Character to put back in the stream
|
|
*
|
|
* Puts a character back in a #MIO stream. This function behaves the sames as
|
|
* ungetc().
|
|
*
|
|
* <warning><para>It is only guaranteed that one character can be but back at a
|
|
* time, even if the implementation may allow more.</para></warning>
|
|
* <warning><para>Using this function while the stream cursor is at offset 0 is
|
|
* not guaranteed to function properly. As the C99 standard says, it is "an
|
|
* obsolescent feature".</para></warning>
|
|
*
|
|
* Returns: The character put back, or %EOF on error.
|
|
*/
|
|
gint
|
|
mio_ungetc (MIO *mio,
|
|
gint ch)
|
|
{
|
|
return mio->v_ungetc (mio, ch);
|
|
}
|
|
|
|
/**
|
|
* mio_gets:
|
|
* @mio: A #MIO object
|
|
* @s: A string to fill with the read data
|
|
* @size: The maximum number of bytes to read
|
|
*
|
|
* Reads a string from a #MIO stream, stopping after the first new-line
|
|
* character or at the end of the stream. This function behaves the same as
|
|
* fgets().
|
|
*
|
|
* Returns: @s on success, %NULL otherwise.
|
|
*/
|
|
gchar *
|
|
mio_gets (MIO *mio,
|
|
gchar *s,
|
|
gsize size)
|
|
{
|
|
return mio->v_gets (mio, s, size);
|
|
}
|
|
|
|
/**
|
|
* mio_clearerr:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Clears the error and end-of-stream indicators of a #MIO stream. This function
|
|
* behaves the same as clearerr().
|
|
*/
|
|
void
|
|
mio_clearerr (MIO *mio)
|
|
{
|
|
mio->v_clearerr (mio);
|
|
}
|
|
|
|
/**
|
|
* mio_eof:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Checks whether the end-of-stream indicator of a #MIO stream is set. This
|
|
* function behaves the same as feof().
|
|
*
|
|
* Returns: A non-null value if the stream reached its end, 0 otherwise.
|
|
*/
|
|
gint
|
|
mio_eof (MIO *mio)
|
|
{
|
|
return mio->v_eof (mio);
|
|
}
|
|
|
|
/**
|
|
* mio_error:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Checks whether the error indicator of a #MIO stream is set. This function
|
|
* behaves the same as ferror().
|
|
*
|
|
* Returns: A non-null value if the stream have an error set, 0 otherwise.
|
|
*/
|
|
gint
|
|
mio_error (MIO *mio)
|
|
{
|
|
return mio->v_error (mio);
|
|
}
|
|
|
|
/**
|
|
* mio_seek:
|
|
* @mio: A #MIO object
|
|
* @offset: Offset of the new place, from @whence
|
|
* @whence: Move origin. SEEK_SET moves relative to the start of the stream,
|
|
* SEEK_CUR from the current position and SEEK_SET from the end of the
|
|
* stream.
|
|
*
|
|
* Sets the curosr position on a #MIO stream. This functions behaves the same as
|
|
* fseek(). See also mio_tell() and mio_setpos().
|
|
*
|
|
* Returns: 0 on success, -1 otherwise, in which case errno should be set to
|
|
* indicate the error.
|
|
*/
|
|
gint
|
|
mio_seek (MIO *mio,
|
|
glong offset,
|
|
gint whence)
|
|
{
|
|
return mio->v_seek (mio, offset, whence);
|
|
}
|
|
|
|
/**
|
|
* mio_tell:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Gets the current cursor position of a #MIO stream. This function behaves the
|
|
* same as ftell().
|
|
*
|
|
* Returns: The current offset from the start of the stream, or -1 or error, in
|
|
* which case errno is set to indicate the error.
|
|
*/
|
|
glong
|
|
mio_tell (MIO *mio)
|
|
{
|
|
return mio->v_tell (mio);
|
|
}
|
|
|
|
/**
|
|
* mio_rewind:
|
|
* @mio: A #MIO object
|
|
*
|
|
* Resets the cursor position to 0, and also the end-of-stream and the error
|
|
* indicators of a #MIO stream.
|
|
* See also mio_seek() and mio_clearerr().
|
|
*/
|
|
void
|
|
mio_rewind (MIO *mio)
|
|
{
|
|
mio->v_rewind (mio);
|
|
}
|
|
|
|
/**
|
|
* mio_getpos:
|
|
* @mio: A #MIO stream
|
|
* @pos: (out): A #MIOPos object to fill-in
|
|
*
|
|
* Stores the current position (and maybe other informations about the stream
|
|
* state) of a #MIO stream in order to restore it later with mio_setpos(). This
|
|
* function behaves the same as fgetpos().
|
|
*
|
|
* Returns: 0 on success, -1 otherwise, in which case errno is set to indicate
|
|
* the error.
|
|
*/
|
|
gint
|
|
mio_getpos (MIO *mio,
|
|
MIOPos *pos)
|
|
{
|
|
gint rv = -1;
|
|
|
|
pos->type = mio->type;
|
|
rv = mio->v_getpos (mio, pos);
|
|
#ifdef MIO_DEBUG
|
|
if (rv != -1) {
|
|
pos->tag = mio;
|
|
}
|
|
#endif /* MIO_DEBUG */
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* mio_setpos:
|
|
* @mio: A #MIO object
|
|
* @pos: (in): A #MIOPos object filled-in by a previous call of mio_getpos() on
|
|
* the same stream
|
|
*
|
|
* Restores the position and state indicators of a #MIO stream previously saved
|
|
* by mio_getpos().
|
|
*
|
|
* <warning><para>The #MIOPos object must have been initialized by a previous
|
|
* call to mio_getpos() on the same stream.</para></warning>
|
|
*
|
|
* Returns: 0 on success, -1 otherwise, in which case errno is set to indicate
|
|
* the error.
|
|
*/
|
|
gint
|
|
mio_setpos (MIO *mio,
|
|
MIOPos *pos)
|
|
{
|
|
gint rv = -1;
|
|
|
|
#ifdef MIO_DEBUG
|
|
if (pos->tag != mio) {
|
|
g_critical ("mio_setpos((MIO*)%p, (MIOPos*)%p): "
|
|
"Given MIOPos was not set by a previous call to mio_getpos() "
|
|
"on the same MIO object, which means there is a bug in "
|
|
"someone's code.",
|
|
(void *)mio, (void *)pos);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
#endif /* MIO_DEBUG */
|
|
rv = mio->v_setpos (mio, pos);
|
|
|
|
return rv;
|
|
}
|