/*
 *  libzvbi - Export modules
 *
 *  Copyright (C) 2001, 2002 Michael H. Schimek
 *
 *  Based on code from AleVT 1.5.1
 *  Copyright (C) 1998, 1999 Edgar Toernig <froese@gmx.de>
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* $Id: export.c,v 1.25 2006/05/25 08:10:03 mschimek Exp $ */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#include <sys/stat.h>
#include <iconv.h>

#include "export.h"
#include "vbi.h" /* vbi_asprintf */

extern const char _zvbi_intl_domainname[];

/**
 * @addtogroup Export Exporting formatted Teletext and Closed Caption pages
 * @ingroup HiDec
 * 
 * Once libzvbi received, decoded and formatted a Teletext or Closed
 * Caption page you will want to render it on screen, print it as
 * text or store it in various formats.
 *
 * Fortunately you don't have to do it all by yourself. libzvbi provides
 * export modules converting a vbi_page into the desired format or
 * rendering directly into memory.
 *
 * A minimalistic export example:
 *
 * @code
 * static void
 * export_my_page (vbi_page *pg)
 * {
 *         vbi_export *ex;
 *         char *errstr;
 *
 *         if (!(ex = vbi_export_new ("html", &errstr))) {
 *                 fprintf (stderr, "Cannot export as HTML: %s\n", errstr);
 *                 free (errstr);
 *                 return;
 *         }
 *
 *         if (!vbi_export_file (ex, "my_page.html", pg))
 *                 puts (vbi_export_errstr (ex));
 *
 *         vbi_export_delete (ex);
 * }
 * @endcode
 */

/**
 * @addtogroup Exmod Internal export module interface
 * @ingroup Export
 *
 * This is the private interface between the public libzvbi export
 * functions and export modules. libzvbi client applications
 * don't use this.
 *
 * Export modules include @c "export.h" to get these
 * definitions. See example module exp-templ.c.
 */

/**
 * @addtogroup Render Teletext and Closed Caption page render functions
 * @ingroup Export
 * 
 * These are functions to render Teletext and Closed Caption pages
 * directly into memory, essentially a more direct interface to the
 * functions of some important export modules.
 */

static vbi_bool initialized;
static vbi_export_class *vbi_export_modules;

/**
 * @param new_module Static pointer to initialized vbi_export_class structure.
 * 
 * Registers a new export module.
 */
void
vbi_register_export_module(vbi_export_class *new_module)
{
	vbi_export_class **xcp;

	if (0)
		fprintf(stderr, "libzvbi:vbi_register_export_module(\"%s\")\n",
		        new_module->_public->keyword);

	for (xcp = &vbi_export_modules; *xcp; xcp = &(*xcp)->next)
		if (strcmp(new_module->_public->keyword, (*xcp)->_public->keyword) < 0)
			break;

	new_module->next = *xcp;
	*xcp = new_module;
}

extern vbi_export_class vbi_export_class_ppm;
extern vbi_export_class vbi_export_class_png;
extern vbi_export_class vbi_export_class_html;
extern vbi_export_class vbi_export_class_tmpl;
extern vbi_export_class vbi_export_class_text;
extern vbi_export_class vbi_export_class_vtx;

/* AUTOREG not reliable, sigh. */
static void
initialize(void)
{
	static vbi_export_class *modules[] = {
		&vbi_export_class_ppm,
#ifdef HAVE_LIBPNG
		&vbi_export_class_png,
#endif
		&vbi_export_class_html,
		&vbi_export_class_text,
		&vbi_export_class_vtx,
		NULL,
		&vbi_export_class_tmpl,
	};

	vbi_export_class **xcp;

	pthread_once (&vbi_init_once, vbi_init);

	if (!vbi_export_modules)
		for (xcp = modules; *xcp; xcp++)
			vbi_register_export_module(*xcp);

	initialized = TRUE;
}

/**
 * Helper function for export modules, since iconv seems
 * undecided what it really wants (not every iconv supports
 * UCS-2LE/BE).
 * 
 * @return 
 * 1 if iconv "UCS-2" is BE on this machine, 0 if LE,
 * -1 if unknown.
 */
/* XXX provide a vbi_iconv wrapper. */
int
vbi_ucs2be(void)
{
	iconv_t cd;
	char c = 'b', *cp = &c;
	char uc[2] = { 'a', 'a' }, *up = uc;
	size_t in = sizeof(c), out = sizeof(uc);
	int endianess = -1;

	if ((cd = iconv_open("UCS-2", /* from */ "ISO-8859-1")) != (iconv_t) -1) {
		iconv(cd, (void *) &cp, &in, (void *) &up, &out);

		if (uc[0] == 0 && uc[1] == 'b')
			endianess = 1;
		else if (uc[1] == 0 && uc[0] == 'b')
			endianess = 0;

		iconv_close(cd);
	}

	return endianess;
}

/*
 *  This is old stuff, we'll see if it's still needed.
 */

#if 0 

static char *
hexnum(char *buf, unsigned int num)
{
    char *p = buf + 5;

    num &= 0xffff;
    *--p = 0;
    do
    {
	*--p = "0123456789abcdef"[num % 16];
	num /= 16;
    } while (num);
    return p;
}

static char *
adjust(char *p, char *str, char fill, int width, int deq)
{
    int c, l = width - strlen(str);

    while (l-- > 0)
	*p++ = fill;
    while ((c = *str++)) {
	if (deq && strchr(" /?*", c))
    	    c = '_';
	*p++ = c;
    }
    return p;
}

char *
vbi_export_mkname(vbi_export *e, char *fmt,
	int pgno, int subno, char *usr)
{
    char bbuf[1024];
    char *s = bbuf;

    while ((*s = *fmt++))
	if (*s++ == '%')
	{
	    char buf[32], buf2[32];
	    int width = 0;

	    s--;
	    while (*fmt >= '0' && *fmt <= '9')
		width = width*10 + *fmt++ - '0';

	    switch (*fmt++)
	    {
		case '%':
		    s = adjust(s, "%", '%', width, 0);
		    break;
		case 'e':	// extension
		    s = adjust(s, e->mod->extension, '.', width, 1);
		    break;
		case 'n':	// network label
		    s = adjust(s, e->network.label, ' ', width, 1);
		    break;
		case 'p':	// pageno[.subno]
		    if (subno)
			s = adjust(s,strcat(strcat(hexnum(buf, pgno),
				"-"), hexnum(buf2, subno)), ' ', width, 0);
		    else
			s = adjust(s, hexnum(buf, pgno), ' ', width, 0);
		    break;
		case 'S':	// subno
		    s = adjust(s, hexnum(buf, subno), '0', width, 0);
		    break;
		case 'P':	// pgno
		    s = adjust(s, hexnum(buf, pgno), '0', width, 0);
		    break;
		case 's':	// user strin
		    s = adjust(s, usr, ' ', width, 0);
		    break;
		//TODO: add date, ...
	    }
	}
    s = strdup(bbuf);
    if (! s)
	vbi_export_error_printf(e, "out of memory");
    return s;
}

#endif /* OLD STUFF */

static vbi_option_info
generic_options[] = {
	VBI_OPTION_STRING_INITIALIZER
	  ("creator", NULL, PACKAGE " " VERSION, NULL),
	VBI_OPTION_STRING_INITIALIZER
	  ("network", NULL, "", NULL),
	VBI_OPTION_BOOL_INITIALIZER
	  ("reveal", NULL, FALSE, NULL)
};

#define GENERIC (sizeof(generic_options) / sizeof(generic_options[0]))

static void
reset_error(vbi_export *e)
{
	if (e->errstr) {
		free(e->errstr);
		e->errstr = NULL;
	}
}

/**
 * @param index Index into the export module list, 0 ... n.
 *
 * Enumerates all available export modules. You should start at index 0,
 * incrementing.
 *
 * Some modules may depend on machine features or the presence of certain
 * libraries, thus the list can vary from session to session.
 *
 * @return
 * Static pointer to a vbi_export_info structure (no need to be freed),
 * @c NULL if the index is out of bounds.
 */
vbi_export_info *
vbi_export_info_enum(int index)
{
	vbi_export_class *xc;

	if (!initialized)
		initialize();

	for (xc = vbi_export_modules; xc && index > 0; xc = xc->next, index--);

	return xc ? xc->_public : NULL;
}

/**
 * @param keyword Export module identifier as in vbi_export_info and
 *           vbi_export_new().
 * 
 * Similar to vbi_export_info_enum(), but this function attempts to find an
 * export module by keyword.
 * 
 * @return
 * Static pointer to a vbi_export_info structure, @c NULL if the named export
 * module has not been found.
 */
vbi_export_info *
vbi_export_info_keyword(const char *keyword)
{
	vbi_export_class *xc;
	int keylen;

	if (!keyword)
		return NULL;

	if (!initialized)
		initialize();

	for (keylen = 0; keyword[keylen]; keylen++)
		if (keyword[keylen] == ';' || keyword[keylen] == ',')
			break;

	for (xc = vbi_export_modules; xc; xc = xc->next)
		if (strncmp(keyword, xc->_public->keyword, keylen) == 0)
			return xc->_public;

	return NULL;
}

/**
 * @param export Pointer to a vbi_export object previously allocated with
 *   vbi_export_new().
 *
 * Returns the export module info for the given @a export object.
 *
 * @return
 * A static vbi_export_info pointer or @c NULL if @a export
 * is @c NULL.
 */
vbi_export_info *
vbi_export_info_export(vbi_export *export)
{
	if (!export)
		return NULL;

	return export->_class->_public;
}

static void
reset_options(vbi_export *e)
{
	vbi_option_info *oi;
	int i;

	for (i = 0; (oi = vbi_export_option_info_enum(e, i)); i++)
		switch (oi->type) {
		case VBI_OPTION_BOOL:
		case VBI_OPTION_INT:
			if (oi->menu.num)
				vbi_export_option_set(e, oi->keyword, oi->menu.num[oi->def.num]);
			else
				vbi_export_option_set(e, oi->keyword, oi->def.num);
			break;

		case VBI_OPTION_REAL:
			if (oi->menu.dbl)
				vbi_export_option_set(e, oi->keyword, oi->menu.dbl[oi->def.num]);
			else
				vbi_export_option_set(e, oi->keyword, oi->def.dbl);
			break;

		case VBI_OPTION_STRING:
			if (oi->menu.str)
				vbi_export_option_set(e, oi->keyword, oi->menu.str[oi->def.num]);
			else
				vbi_export_option_set(e, oi->keyword, oi->def.str);
			break;

		case VBI_OPTION_MENU:
			vbi_export_option_set(e, oi->keyword, oi->def.num);
			break;

		default:
			fprintf(stderr,	"%s: unknown export option type %d\n",
				__PRETTY_FUNCTION__, oi->type);
			exit(EXIT_FAILURE);
		}
}

static vbi_bool
option_string(vbi_export *e, const char *s2)
{
	vbi_option_info *oi;
	char *s, *s1, *keyword, *string, quote;
	vbi_bool r = TRUE;

	if (!(s = s1 = vbi_export_strdup(e, NULL, s2)))
		return FALSE;

	do {
		while (isspace(*s))
			s++;

		if (*s == ',' || *s == ';') {
			s++;
			continue;
		}

		if (!*s) {
			free(s1);
			return TRUE;
		}

		keyword = s;

		while (isalnum(*s) || *s == '_')
			s++;

		if (!*s)
			goto invalid;

		*s++ = 0;

		while (isspace(*s) || *s == '=')
			s++;

		if (!*s) {
 invalid:
			vbi_export_error_printf(e, _("Invalid option string \"%s\"."), s2);
			break;
		}

		if (!(oi = vbi_export_option_info_keyword(e, keyword)))
			break;

		switch (oi->type) {
		case VBI_OPTION_BOOL:
		case VBI_OPTION_INT:
		case VBI_OPTION_MENU:
			r = vbi_export_option_set(e, keyword, (int) strtol(s, &s, 0));
			break;

		case VBI_OPTION_REAL:
			r = vbi_export_option_set(e, keyword, (double) strtod(s, &s));
			break;

		case VBI_OPTION_STRING:
			quote = 0;
			if (*s == '\'' || *s == '\"')
				quote = *s++;
			string = s;

			while (*s && *s != quote
			       && (quote || (*s != ',' && *s != ';')))
				s++;
			if (*s)
				*s++ = 0;

			r = vbi_export_option_set(e, keyword, string);
			break;

		default:
			fprintf(stderr, "%s: unknown export option type %d\n",
				__PRETTY_FUNCTION__, oi->type);
			exit(EXIT_FAILURE);
		}

	} while (r);

	free(s1);

	return FALSE;
}

/**
 * @param keyword Export module identifier as in vbi_export_info.
 * @param errstr If not @c NULL this function stores a pointer to an error
 *   description here. You must free() this string when no longer needed.
 * 
 * Creates a new export module instance to export a vbi_page in
 * the respective module format. As a special service you can
 * initialize options by appending to the @param keyword like this:
 * 
 * @code
 * vbi_export_new ("keyword; quality=75.5, comment=\"example text\"");
 * @endcode
 * 
 * @return
 * Pointer to a newly allocated vbi_export object which must be
 * freed by calling vbi_export_delete(). @c NULL is returned and
 * the @a errstr may be set (else @a NULL) if some problem occurred.
 */
vbi_export *
vbi_export_new(const char *keyword, char **errstr)
{
	char key[256];
	vbi_export_class *xc;
	vbi_export *e;
	unsigned int keylen;

	if (!initialized)
		initialize();

	if (!keyword)
		keyword = "";

	for (keylen = 0; keyword[keylen] && keylen < (sizeof(key) - 1)
		     && keyword[keylen] != ';' && keyword[keylen] != ','; keylen++)
		     key[keylen] = keyword[keylen];
	key[keylen] = 0;

	for (xc = vbi_export_modules; xc; xc = xc->next)
		if (strcmp(key, xc->_public->keyword) == 0)
			break;

	if (!xc) {
		asprintf(errstr, _("Unknown export module '%s'."), key);
		return NULL;
	}

	if (!xc->_new)
		e = calloc(1, sizeof(*e));
	else
		e = xc->_new();

	if (!e) {
		asprintf(errstr, _("Cannot initialize export module '%s', "
				   "probably lack of memory."),
			 xc->_public->label ? xc->_public->label : keyword);
		return NULL;
	}

	e->_class = xc;
	e->errstr = NULL;

	e->name = NULL;

	reset_options(e);

	if (keyword[keylen] && !option_string(e, keyword + keylen + 1)) {
		if (errstr)
			*errstr = strdup(vbi_export_errstr(e));
		vbi_export_delete(e);
		return NULL;
	}

	if (errstr)
		errstr = NULL;

	return e;
}

/**
 * @param export Pointer to a vbi_export object previously allocated with
 *	     vbi_export_new(). Can be @c NULL.
 * 
 * This function frees all resources associated with the vbi_export
 * object.
 */
void
vbi_export_delete(vbi_export *export)
{
	vbi_export_class *xc;

	if (!export)
		return;

	if (export->errstr)
		free(export->errstr);

	if (export->network)
		free(export->network);
	if (export->creator)
		free(export->creator);

	xc = export->_class;

	if (xc->_new && xc->_delete)
		xc->_delete(export);
	else
		free(export);
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param index Index in the option table 0 ... n.
 *
 * Enumerates the options available for the given export module. You
 * should start at index 0, incrementing.
 *
 * @return Static pointer to a vbi_option_info structure,
 * @c NULL if @a index is out of bounds.
 */
/* XXX unsigned index */
vbi_option_info *
vbi_export_option_info_enum(vbi_export *export, int index)
{
	vbi_export_class *xc;

	if (!export)
		return NULL;

	reset_error(export);

	if (index < (int) GENERIC)
		return generic_options + index;

	xc = export->_class;

	if (xc->option_enum)
		return xc->option_enum(export, index - GENERIC);
	else
		return NULL;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param keyword Keyword of the option as in vbi_option_info.
 *
 * Similar to vbi_export_option_info_enum(), but tries to find the
 * option info based on the given keyword.
 *
 * @return Static pointer to a vbi_option_info structure,
 * @c NULL if the keyword wasn't found.
 */
vbi_option_info *
vbi_export_option_info_keyword(vbi_export *export, const char *keyword)
{
	vbi_export_class *xc;
	vbi_option_info *oi;
	unsigned int i;

	if (!export || !keyword)
		return NULL;

	reset_error(export);

	for (i = 0; i < GENERIC; i++)
		if (strcmp(keyword, generic_options[i].keyword) == 0)
			return generic_options + i;

	xc = export->_class;

	if (!xc->option_enum)
		return NULL;

	for (i = 0; (oi = xc->option_enum(export, i)); i++)
		if (strcmp(keyword, oi->keyword) == 0)
			return oi;

	vbi_export_unknown_option(export, keyword);

	return NULL;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param keyword Keyword identifying the option, as in vbi_option_info.
 * @param value A place to store the current option value.
 *
 * This function queries the current value of the named option.
 * When the option is of type VBI_OPTION_STRING @a value.str must be
 * freed with free() when you don't need it any longer. When the
 * option is of type VBI_OPTION_MENU then @a value.num contains the
 * selected entry.
 *
 * @return @c TRUE on success, otherwise @a value unchanged.
 */
vbi_bool
vbi_export_option_get(vbi_export *export, const char *keyword,
		      vbi_option_value *value)
{
	vbi_export_class *xc;
	vbi_bool r = TRUE;

	if (!export || !keyword || !value)
		return FALSE;

	reset_error(export);

	if (strcmp(keyword, "reveal") == 0) {
		value->num = export->reveal;
	} else if (strcmp(keyword, "network") == 0) {
		if (!(value->str = vbi_export_strdup(export, NULL,
						     export->network ? : "")))
			return FALSE;
	} else if (strcmp(keyword, "creator") == 0) {
		if (!(value->str = vbi_export_strdup(export, NULL, export->creator)))
			return FALSE;
	} else {
		xc = export->_class;

		if (xc->option_get)
			r = xc->option_get(export, keyword, value);
		else {
			vbi_export_unknown_option(export, keyword);
			r = FALSE;
		}
	}

	return r;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param keyword Keyword identifying the option, as in vbi_option_info.
 * @param ... New value to set.
 *
 * Sets the value of the named option. Make sure the value is casted
 * to the correct type (int, double, char *).
 *
 * Typical usage of vbi_export_option_set():
 * @code
 * vbi_export_option_set (export, "quality", 75.5);
 * @endcode
 *
 * Mind that options of type @c VBI_OPTION_MENU must be set by menu
 * entry number (int), all other options by value. If necessary it will
 * be replaced by the closest value possible. Use function
 * vbi_export_option_menu_set() to set options with menu
 * by menu entry.  
 *
 * @return
 * @c TRUE on success, otherwise the option is not changed.
 */
vbi_bool
vbi_export_option_set(vbi_export *export, const char *keyword, ...)
{
	vbi_export_class *xc;
	vbi_bool r = TRUE;
	va_list args;

	if (!export || !keyword)
		return FALSE;

	reset_error(export);

	va_start(args, keyword);

	if (strcmp(keyword, "reveal") == 0) {
		export->reveal = !!va_arg(args, int);
	} else if (strcmp(keyword, "network") == 0) {
		char *network = va_arg(args, char *);
		if (!network || !network[0]) {
			if (export->network) {
				free(export->network);
				export->network = NULL;
			}
		} else if (!vbi_export_strdup(export, &export->network, network)) {
			return FALSE;
		}
	} else if (strcmp(keyword, "creator") == 0) {
		char *creator = va_arg(args, char *);
		if (!vbi_export_strdup(export, &export->creator, creator))
			return FALSE;
	} else {
		xc = export->_class;

		if (xc->option_set)
			r = xc->option_set(export, keyword, args);
		else
			r = FALSE;
	}

	va_end(args);

	return r;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param keyword Keyword identifying the option, as in vbi_option_info.
 * @param entry A place to store the current menu entry.
 * 
 * Similar to vbi_export_option_get() this function queries the current
 * value of the named option, but returns this value as number of the
 * corresponding menu entry. Naturally this must be an option with
 * menu.
 * 
 * @return 
 * @c TRUE on success, otherwise @a value remained unchanged.
 */
vbi_bool
vbi_export_option_menu_get(vbi_export *export, const char *keyword,
			   int *entry)
{
	vbi_option_info *oi;
	vbi_option_value val;
	vbi_bool r;
	int i;

	if (!export || !keyword || !entry)
		return FALSE;

	reset_error(export);

	if (!(oi = vbi_export_option_info_keyword(export, keyword)))
		return FALSE;

	if (!vbi_export_option_get(export, keyword, &val))
		return FALSE;

	r = FALSE;

	for (i = oi->min.num; i <= oi->max.num; i++) {
		switch (oi->type) {
		case VBI_OPTION_BOOL:
		case VBI_OPTION_INT:
			if (!oi->menu.num)
				return FALSE;
			r = (oi->menu.num[i] == val.num);
			break;

		case VBI_OPTION_REAL:
			if (!oi->menu.dbl)
				return FALSE;
			/* XXX unsafe */
			r = (oi->menu.dbl[i] == val.dbl);
			break;

		case VBI_OPTION_MENU:
			r = (i == val.num);
			break;

		default:
			fprintf(stderr,	"%s: unknown export option type %d\n",
				__PRETTY_FUNCTION__, oi->type);
			exit(EXIT_FAILURE);
		}

		if (r) {
			*entry = i;
			break;
		}
	}

	return r;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param keyword Keyword identifying the option, as in vbi_option_info.
 * @param entry Menu entry to be selected.
 * 
 * Similar to vbi_export_option_set() this function sets the value of
 * the named option, however it does so by number of the corresponding
 * menu entry. Naturally this must be an option with menu.
 *
 * @return 
 * @c TRUE on success, otherwise the option is not changed.
 */
vbi_bool
vbi_export_option_menu_set(vbi_export *export, const char *keyword,
			   int entry)
{
	vbi_option_info *oi;

	if (!export || !keyword)
		return FALSE;

	reset_error(export);

	if (!(oi = vbi_export_option_info_keyword(export, keyword)))
		return FALSE;

	if (entry < oi->min.num || entry > oi->max.num)
		return FALSE;

	switch (oi->type) {
	case VBI_OPTION_BOOL:
	case VBI_OPTION_INT:
		if (!oi->menu.num)
			return FALSE;
		return vbi_export_option_set(export, keyword, oi->menu.num[entry]);

	case VBI_OPTION_REAL:
		if (!oi->menu.dbl)
			return FALSE;
		return vbi_export_option_set(export, keyword, oi->menu.dbl[entry]);

	case VBI_OPTION_MENU:
		return vbi_export_option_set(export, keyword, entry);

	default:
		fprintf(stderr, "%s: unknown export option type %d\n",
			__PRETTY_FUNCTION__, oi->type);
		exit(EXIT_FAILURE);
	}
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param fp Buffered i/o stream to write to.
 * @param pg Page to be exported.
 * 
 * This function writes the @a pg contents, converted to the respective
 * export module format, to the stream @a fp. The caller is responsible for
 * opening and closing the stream, don't forget to check for i/o
 * errors after closing. Note this function may write incomplete files
 * when an error occurs.
 *
 * You can call this function as many times as you want, it does not
 * change vbi_export state or the vbi_page.
 * 
 * @return 
 * @c TRUE on success.
 */
vbi_bool
vbi_export_stdio(vbi_export *export, FILE *fp, vbi_page *pg)
{
	vbi_bool success;

	if (!export || !fp || !pg)
		return FALSE;

	reset_error(export);
	clearerr(fp);

	success = export->_class->export(export, fp, pg);

	if (success && ferror(fp)) {
		vbi_export_write_error(export);
		success = FALSE;
	}

	return success;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param name File to be created.
 * @param pg Page to be exported.
 * 
 * This function writes the @a pg contents, converted to the respective
 * export format, into a new file of the given @a name. When an error
 * occured the incomplete file will be deleted.
 * 
 * You can call this function as many times as you want, it does not
 * change vbi_export state or the vbi_page.
 * 
 * @return 
 * @c TRUE on success.
 */
vbi_bool
vbi_export_file(vbi_export *export, const char *name,
		vbi_page *pg)
{
	struct stat st;
	vbi_bool success;
	FILE *fp;

	if (!export || !name || !pg)
		return FALSE;

	reset_error(export);

	if (!(fp = fopen(name, "w"))) {
		vbi_export_error_printf(export,
					_("Cannot create file '%s': %s."),
					name, strerror(errno));
		return FALSE;
	}

	export->name = (char *) name;

	success = export->_class->export(export, fp, pg);

	if (success && ferror(fp)) {
		vbi_export_write_error(export);
		success = FALSE;
	}

	if (fclose(fp))
		if (success) {
			vbi_export_write_error(export);
			success = FALSE;
		}

	if (!success && stat(name, &st) == 0 && S_ISREG(st.st_mode))
		remove(name);

	export->name = NULL;

	return success;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param templ See printf().
 * @param ... See printf(). 
 * 
 * Store an error description in the @a export object. Including the current
 * error description (to append or prepend) is safe.
 */
void
vbi_export_error_printf(vbi_export *export, const char *templ, ...)
{
	char buf[512];
	va_list ap;

	if (!export)
		return;

	va_start(ap, templ);
	vsnprintf(buf, sizeof(buf) - 1, templ, ap);
	va_end(ap);

	reset_error(export);

	export->errstr = strdup(buf);
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * 
 * Similar to vbi_export_error_printf this function stores an error
 * description in the @a export object, after examining the errno
 * variable and choosing an appropriate message. Only export
 * modules call this function.
 */
void
vbi_export_write_error(vbi_export *export)
{
	char *t, buf[256];

	if (!export)
		return;

	if (export->name)
		snprintf(t = buf, sizeof(buf),
			_("Error while writing file '%s'"), export->name);
	else
 		t = _("Error while writing file");

	if (errno) {
		vbi_export_error_printf(export, "%s: Error %d, %s", t,
					errno, strerror(errno));
	} else {
		vbi_export_error_printf(export, "%s.", t);
	}
}

static char *
module_name			(vbi_export *		export)
{
	vbi_export_class *xc = export->_class;

	if (xc->_public->label)
		return _(xc->_public->label);
	else
		return xc->_public->keyword;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param keyword Name of the unknown option.
 * 
 * Store an error description in the @a export object.
 */
void
vbi_export_unknown_option(vbi_export *export, const char *keyword)
{
	vbi_export_error_printf (export, _("Export module '%s' has no option '%s'."),
				 module_name (export), keyword);
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param keyword Name of the unknown option.
 * @param ... Invalid value, type depending on the option.
 * 
 * Store an error description in the @a export object.
 */
void
vbi_export_invalid_option(vbi_export *export, const char *keyword, ...)
{
	char buf[256];
	vbi_option_info *oi;

	if ((oi = vbi_export_option_info_keyword(export, keyword))) {
		va_list args;
		char *s;

		va_start(args, keyword);

		switch (oi->type) {
		case VBI_OPTION_BOOL:
		case VBI_OPTION_INT:
		case VBI_OPTION_MENU:
			snprintf(buf, sizeof(buf) - 1, "'%d'", va_arg(args, int));
			break;
		case VBI_OPTION_REAL:
			snprintf(buf, sizeof(buf) - 1, "'%f'", va_arg(args, double));
			break;
		case VBI_OPTION_STRING:
			s = va_arg(args, char *);
			if (s == NULL)
				strcpy(buf, "NULL");
			else
				snprintf(buf, sizeof(buf) - 1, "'%s'", s);
			break;
		default:
			fprintf(stderr, "%s: unknown export option type %d\n",
				__PRETTY_FUNCTION__, oi->type);
			strcpy(buf, "?");
			break;
		}

		va_end(args);
	} else
		buf[0] = 0;

	vbi_export_error_printf (export, _("Invalid argument %s for option %s of export module %s."),
				 buf, keyword, module_name (export));
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * @param d If non-zero, store pointer to allocated string here. When *d
 *   is non-zero, free(*d) the old string first.
 * @param s String to be duplicated.
 * 
 * Helper function for export modules.
 *
 * Same as the libc strdup(), except for @a d argument and setting
 * the @a export error string on failure.
 * 
 * @return 
 * @c NULL on failure, pointer to malloc()ed string otherwise.
 */
char *
vbi_export_strdup(vbi_export *export, char **d, const char *s)
{
	char *new = strdup(s ? s : "");

	if (!new) {
		vbi_export_error_printf (export, _("Out of memory in export module '%s'."),
					 module_name (export));
		errno = ENOMEM;
		return NULL;
	}

	if (d) {
		if (*d)
			free(*d);
		*d = new;
	}

	return new;
}

/**
 * @param export Pointer to a initialized vbi_export object.
 * 
 * @return 
 * After an export function failed, this function returns a pointer
 * to a more detailed error description. Do not free this string. It
 * remains valid until the next call of an export function.
 */
char *
vbi_export_errstr(vbi_export *export)
{
	if (!export || !export->errstr)
		return _("Unknown error.");

	return export->errstr;
}


syntax highlighted by Code2HTML, v. 0.9.1