/*===========================================================================*
* *
* sflxmll.c - *
* *
* Copyright (c) 1991-2003 iMatix Corporation *
* *
* ------------------ GPL Licensed Source Code ------------------ *
* iMatix makes this software available under the GNU General *
* Public License (GPL) license for open source projects. For *
* details of the GPL license please see www.gnu.org or read the *
* file license.gpl provided in this package. *
* *
* 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 in the file 'license.gpl'; if *
* not, write to the Free Software Foundation, Inc., 59 Temple *
* Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* You can also license this software under iMatix's General Terms *
* of Business (GTB) for commercial projects. If you have not *
* explicitly licensed this software under the iMatix GTB you may *
* only use it under the terms of the GNU General Public License. *
* *
* For more information, send an email to info@imatix.com. *
* -------------------------------------------------------------- *
*===========================================================================*/
/*
Synopsis: Loads XML file into memory, to/from disk files.
*/
#include "prelude.h" /* Universal include file */
#include "sfldate.h" /* Date and time functions */
#include "sflfile.h" /* File access functions */
#include "sfllist.h" /* List access functions */
#include "sflmem.h" /* Memory allocation functions */
#include "sflstr.h" /* String access functions */
#include "sflxml.h" /* XML definitions */
#include "sflsymb.h"
#include "sflhttp.h" /* Meta-char encoding/decoding */
#include "sflxmll.h" /* Include prototype data */
#include "sflxmll.d" /* Include dialog data */
/* Function prototypes */
static int xml_save_file_item (FILE *xmlfile, XML_ITEM *item,
int generation);
static void xml_save_string_item (char *xml_string, XML_ITEM *item);
static size_t xml_item_size (XML_ITEM *item);
static void init_charmaps (void);
static void build_charmap (byte flag, char *chars);
static int xml_start_dialog (void);
static void expect_token (char expect);
static char get_next_char (void);
static char get_next_non_white_char (void);
static int collect_name (void);
static char *collect_literal (char terminator);
static void error_exception (char *format, ...);
/*- Global variables used in this source file only --------------------------*/
static int
feedback, /* Feedback for calling program */
char_nbr, /* Current read position in line */
line_nbr, /* Input line nbr from file */
generation; /* How many levels of children */
static const char
*ppath, /* XML file path as specified */
*pname; /* XML file name as specified */
static char
*fname, /* Full file name of XML file */
*literal, /* String of any length */
*xmltext, /* Pointer to XML string */
*xmlline, /* Current line w/space for EOL */
token [LINE_MAX + 1], /* Token from input stream */
name [LINE_MAX + 1], /* Saved name */
error_message [LINE_MAX + 1]; /* Saved name */
static FILE
*xmlfile;
static XML_ITEM
*item, /* Current XML item */
*top_item, /* Current top-level XML item */
*root; /* Root XML item */
static Bool
extended; /* Extended or normal load? */
/* Character classification tables and macros */
static byte
cmap [256]; /* Character classification tables */
#define CMAP_NAME 1 /* Normal name character */
#define CMAP_NAME_OPEN 2 /* Valid character to start name */
#define CMAP_QUOTE 4 /* Possible string delimiters */
#define CMAP_PRINTABLE 8 /* Valid characters in literal */
#define CMAP_DECIMAL 16 /* Decimal digits */
#define CMAP_HEX 32 /* Hexadecimal digits */
/* Macros for character mapping: */
#define is_name(ch) (cmap [(byte) (ch)] & CMAP_NAME)
#define is_name_open(ch) (cmap [(byte) (ch)] & CMAP_NAME_OPEN)
#define is_quote(ch) (cmap [(byte) (ch)] & CMAP_QUOTE)
#define is_printable(ch) (cmap [(byte) (ch)] & CMAP_PRINTABLE)
#define is_decimal(ch) (cmap [(byte) (ch)] & CMAP_DECIMAL)
#define is_hex(ch) (cmap [(byte) (ch)] & CMAP_HEX)
/* ---------------------------------------------------------------------[<]-
Function: xml_save_file
Synopsis: Saves an XML tree to the specified file. Returns XML_NOERROR
or XML_FILEERROR.
---------------------------------------------------------------------[>]-*/
int
xml_save_file (
XML_ITEM *item,
const char *filename)
{
FILE
*xmlfile; /* XML output stream */
int
count; /* How many symbols did we save? */
ASSERT (item);
ASSERT (filename);
init_charmaps (); /* Initialise character maps */
if ((xmlfile = file_open (filename, 'w')) == NULL)
return XML_FILEERROR; /* No permission to write file */
/* Write XML file header */
fprintf (xmlfile, "\n");
/* Output XML root */
count = xml_save_file_item (xmlfile, item, 0);
/* Output a final carriage return */
fprintf (xmlfile, "\n");
file_close (xmlfile);
return XML_NOERROR;
}
/* -------------------------------------------------------------------------
* init_charmaps
*
* Initialise character map bit tables. These are used to speed-up
* token recognition and collection.
*/
static void
init_charmaps (void)
{
memset (cmap, 0, sizeof (cmap)); /* Clear all bitmaps */
/* Name ::= (Letter | '_' | ':') (NameChar)* */
/* NameChar ::= Letter | Digit | MiscName */
/* Map fixed character sets to various bitmaps */
build_charmap (CMAP_NAME, "abcdefghijklmnopqrstuvwxyz");
build_charmap (CMAP_NAME, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
build_charmap (CMAP_NAME, "0123456789");
build_charmap (CMAP_NAME, "_:.-");
build_charmap (CMAP_NAME_OPEN, "abcdefghijklmnopqrstuvwxyz");
build_charmap (CMAP_NAME_OPEN, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
build_charmap (CMAP_NAME_OPEN, "_:");
build_charmap (CMAP_QUOTE, "\"'");
/* Printable characters. ??? */
build_charmap (CMAP_PRINTABLE, "abcdefghijklmnopqrstuvwxyz");
build_charmap (CMAP_PRINTABLE, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
build_charmap (CMAP_PRINTABLE, "0123456789");
build_charmap (CMAP_PRINTABLE, "!@#$%^&*()-_=+[]{}\\|;:'\"<>,./?`~ ");
build_charmap (CMAP_DECIMAL, "0123456789");
build_charmap (CMAP_HEX, "abcdefghijklmnopqrstuvwxyz");
build_charmap (CMAP_HEX, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
build_charmap (CMAP_HEX, "0123456789");
}
/* -------------------------------------------------------------------------
* build_charmap
*
* Encode character string and flag into character map table. Flag should
* be a 1-bit value from 1 to 128 (character map is 8 bits wide).
*/
static void
build_charmap (byte flag, char *string)
{
for (; *string; string++)
cmap [(byte) *string] |= flag;
}
static int
xml_save_file_item (FILE *xmlfile, XML_ITEM *item, int generation)
{
int
count = 1; /* Count 1 for current item */
XML_ITEM
*child,
*sibling;
XML_ATTR
*attr;
char
*item_name,
*attr_name,
*ptr;
Bool
pretty;
/* First output item name and attributes */
item_name = xml_item_name (item);
if (item_name)
{
fprintf (xmlfile, "<%s", item_name);
FORATTRIBUTES (attr, item)
{
attr_name = xml_attr_name (attr);
ptr = xml_attr_value (attr);
http_encode_meta (token, &ptr, LINE_MAX, FALSE);
fprintf (xmlfile, "\n%*s%s = \"%s", (generation + 1) * 4, "",
attr_name, token);
while (*ptr)
{
http_encode_meta (token, &ptr, LINE_MAX, FALSE);
fprintf (xmlfile, "\n%s", token);
}
fprintf (xmlfile, "\"");
}
/* If value or children exist, use long form, otherwise short form */
if ((child = xml_first_child (item)))
{
fprintf (xmlfile, ">");
pretty = TRUE;
for (sibling = child ;
sibling != NULL ;
sibling = xml_next_sibling (sibling))
if (! xml_item_name (sibling))
pretty = FALSE;
else
break;
for ( ; child != NULL; child = xml_next_sibling (child))
{
if (pretty)
fprintf (xmlfile, "\n%*s", (generation + 1) * 4, "");
count += xml_save_file_item (xmlfile, child,
generation + 1);
if (xml_item_name (child))
{
pretty = TRUE;
for (sibling = xml_next_sibling (child) ;
sibling != NULL ;
sibling = xml_next_sibling (sibling))
if (! xml_item_name (sibling))
pretty = FALSE;
else
break;
}
}
if (pretty)
fprintf (xmlfile, "\n%*s", generation * 4, "");
fprintf (xmlfile, "%s>", item_name);
}
else
fprintf (xmlfile, "/>");
}
else /* Not name => this is a value node */
{
ptr = xml_item_value (item);
if (ptr)
while (*ptr)
{
http_encode_meta (token, &ptr, LINE_MAX, FALSE);
fprintf (xmlfile, "%s", token);
}
}
return (count);
}
/* ---------------------------------------------------------------------[<]-
Function: xml_save_string
Synopsis: Saves an XML tree to a string. Returns a freshly-allocated
string containing the XML tree, or NULL if there was insufficient memory
to allocate a new string.
---------------------------------------------------------------------[>]-*/
char *
xml_save_string (
XML_ITEM *item)
{
size_t
xml_size;
char
*xml_string;
ASSERT (item);
init_charmaps (); /* Initialise character maps */
xml_size = xml_item_size (item);
xml_string = mem_alloc (xml_size + 1000);
if (xml_string)
xml_save_string_item (xml_string, item);
return (xml_string);
}
/* Return string size of XML item and all its children */
static size_t
xml_item_size (XML_ITEM *item)
{
size_t
item_size = 0;
XML_ITEM
*child = NULL;
XML_ATTR
*attr;
char
*item_name,
*attr_name,
*ptr;
/* First output item name and attributes */
item_name = xml_item_name (item);
if (item_name)
{
/* Count '