1/*
2 * "$Id: xml.c,v 1.43 2010/08/04 00:33:57 rlk Exp $"
3 *
4 *   XML parser - process Gutenprint XML data with mxml.
5 *
6 *   Copyright 2002-2003 Roger Leigh (rleigh@debian.org)
7 *
8 *   This program is free software; you can redistribute it and/or modify it
9 *   under the terms of the GNU General Public License as published by the Free
10 *   Software Foundation; either version 2 of the License, or (at your option)
11 *   any later version.
12 *
13 *   This program is distributed in the hope that it will be useful, but
14 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 *   for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27#include <gutenprint/gutenprint.h>
28#include "gutenprint-internal.h"
29#include <gutenprint/gutenprint-intl-internal.h>
30#include <ctype.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <math.h>
35#include <errno.h>
36#ifdef HAVE_LIMITS_H
37#include <limits.h>
38#endif
39#if defined(HAVE_VARARGS_H) && !defined(HAVE_STDARG_H)
40#include <varargs.h>
41#else
42#include <stdarg.h>
43#endif
44
45typedef struct
46{
47  char *name;
48  stp_xml_parse_func parse_func;
49} stpi_xml_parse_registry;
50
51static stp_list_t *stpi_xml_registry;
52
53static stp_list_t *stpi_xml_preloads;
54
55static const char *
56xml_registry_namefunc(const void *item)
57{
58  const stpi_xml_parse_registry *xmlp = (const stpi_xml_parse_registry *) item;
59  return xmlp->name;
60}
61
62static void
63xml_registry_freefunc(void *item)
64{
65  stpi_xml_parse_registry *xmlp = (stpi_xml_parse_registry *) item;
66  stp_free(xmlp->name);
67  stp_free(xmlp);
68}
69
70static const char *
71xml_preload_namefunc(const void *item)
72{
73  return (const char *) item;
74}
75
76static void
77xml_preload_freefunc(void *item)
78{
79  stp_free(item);
80}
81
82void
83stp_register_xml_parser(const char *name, stp_xml_parse_func parse_func)
84{
85  stpi_xml_parse_registry *xmlp;
86  stp_list_item_t *item = stp_list_get_item_by_name(stpi_xml_registry, name);
87  if (item)
88    xmlp = (stpi_xml_parse_registry *) stp_list_item_get_data(item);
89  else
90    {
91      xmlp = stp_malloc(sizeof(stpi_xml_parse_registry));
92      xmlp->name = stp_strdup(name);
93      stp_list_item_create(stpi_xml_registry, NULL, xmlp);
94    }
95  xmlp->parse_func = parse_func;
96}
97
98void
99stp_unregister_xml_parser(const char *name)
100{
101  stp_list_item_t *item = stp_list_get_item_by_name(stpi_xml_registry, name);
102  if (item)
103    stp_list_item_destroy(stpi_xml_registry, item);
104}
105
106void
107stp_register_xml_preload(const char *filename)
108{
109  stp_list_item_t *item = stp_list_get_item_by_name(stpi_xml_preloads, filename);
110  if (!item)
111    {
112      char *the_filename = stp_strdup(filename);
113      stp_list_item_create(stpi_xml_preloads, NULL, the_filename);
114    }
115}
116
117void
118stp_unregister_xml_preload(const char *name)
119{
120  stp_list_item_t *item = stp_list_get_item_by_name(stpi_xml_preloads, name);
121  if (item)
122    stp_list_item_destroy(stpi_xml_preloads, item);
123}
124
125
126static void stpi_xml_process_gutenprint(stp_mxml_node_t *gutenprint, const char *file);
127
128static char *saved_locale;                 /* Saved LC_ALL */
129static int xml_is_initialised;                 /* Flag for init */
130
131void
132stp_xml_preinit(void)
133{
134  static int xml_is_preinitialized = 0;
135  if (!xml_is_preinitialized)
136    {
137      stpi_xml_registry = stp_list_create();
138      stp_list_set_freefunc(stpi_xml_registry, xml_registry_freefunc);
139      stp_list_set_namefunc(stpi_xml_registry, xml_registry_namefunc);
140      stpi_xml_preloads = stp_list_create();
141      stp_list_set_freefunc(stpi_xml_preloads, xml_preload_freefunc);
142      stp_list_set_namefunc(stpi_xml_preloads, xml_preload_namefunc);
143    }
144}
145
146/*
147 * Call before using any of the static functions in this file.  All
148 * public functions should call this before using any mxml
149 * functions.
150 */
151void
152stp_xml_init(void)
153{
154  stp_deprintf(STP_DBG_XML, "stp_xml_init: entering at level %d\n",
155	       xml_is_initialised);
156  if (xml_is_initialised >= 1)
157    {
158      xml_is_initialised++;
159      return;
160    }
161
162  /* Set some locale facets to "C" */
163#ifdef HAVE_LOCALE_H
164  saved_locale = stp_strdup(setlocale(LC_ALL, NULL));
165  stp_deprintf(STP_DBG_XML, "stp_xml_init: saving locale %s\n", saved_locale);
166  setlocale(LC_ALL, "C");
167#endif
168
169  xml_is_initialised = 1;
170}
171
172/*
173 * Call after using any of the static functions in this file.  All
174 * public functions should call this after using any mxml functions.
175 */
176void
177stp_xml_exit(void)
178{
179  stp_deprintf(STP_DBG_XML, "stp_xml_exit: entering at level %d\n",
180	       xml_is_initialised);
181  if (xml_is_initialised > 1) /* don't restore original state */
182    {
183      xml_is_initialised--;
184      return;
185    }
186  else if (xml_is_initialised < 1)
187    return;
188
189  /* Restore locale */
190#ifdef HAVE_LOCALE_H
191  stp_deprintf(STP_DBG_XML, "stp_xml_init: restoring locale %s\n", saved_locale);
192  setlocale(LC_ALL, saved_locale);
193  stp_free(saved_locale);
194  saved_locale = NULL;
195#endif
196  xml_is_initialised = 0;
197}
198
199void
200stp_xml_parse_file_named(const char *name)
201{
202  stp_list_t *file_list = stpi_list_files_on_data_path(name); /* List of XML files */
203  stp_list_item_t *item;                 /* Pointer to current list item */
204  item = stp_list_get_start(file_list);
205  while (item)
206    {
207      stp_deprintf(STP_DBG_XML,
208		   "stp_xml_parse_file_named: source file: %s\n",
209		   (const char *) stp_list_item_get_data(item));
210      stp_xml_parse_file((const char *) stp_list_item_get_data(item));
211      item = stp_list_item_next(item);
212    }
213  stp_list_destroy(file_list);
214}
215
216
217/*
218 * Read all available XML files.
219 */
220int
221stp_xml_init_defaults(void)
222{
223  stp_list_item_t *item;                 /* Pointer to current list item */
224
225  stp_xml_init();
226
227  /* Parse each XML file */
228  item = stp_list_get_start(stpi_xml_preloads);
229  while (item)
230    {
231      stp_deprintf(STP_DBG_XML, "stp_xml_init_defaults: source file: %s\n",
232		   (const char *) stp_list_item_get_data(item));
233      stp_xml_parse_file_named((const char *) stp_list_item_get_data(item));
234      item = stp_list_item_next(item);
235    }
236  stp_list_destroy(stpi_xml_preloads);
237
238  stp_xml_exit();
239
240  return 0;
241}
242
243
244/*
245 * Parse a single XML file.
246 */
247int
248stp_xml_parse_file(const char *file) /* File to parse */
249{
250  stp_mxml_node_t *doc;
251  stp_mxml_node_t *cur;
252  FILE *fp;
253
254  stp_deprintf(STP_DBG_XML, "stp_xml_parse_file: reading  `%s'...\n", file);
255
256  fp = fopen(file, "r");
257  if (!fp)
258    {
259      stp_erprintf("stp_xml_parse_file: unable to open %s: %s\n", file,
260		   strerror(errno));
261      return 1;
262    }
263
264  stp_xml_init();
265
266  doc = stp_mxmlLoadFile(NULL, fp, STP_MXML_NO_CALLBACK);
267  fclose(fp);
268
269  cur = doc->child;
270  while (cur &&
271	 (cur->type != STP_MXML_ELEMENT ||
272	  (strcmp(cur->value.element.name, "gutenprint") != 0 &&
273	   strcmp(cur->value.element.name, "gimp-print") != 0)))
274    cur = cur->next;
275
276  if (cur == NULL || cur->type != STP_MXML_ELEMENT)
277    {
278      stp_erprintf("stp_xml_parse_file: %s: parse error\n", file);
279      stp_mxmlDelete(doc);
280      return 1;
281    }
282
283  if (strcmp(cur->value.element.name, "gutenprint") != 0 &&
284      strcmp(cur->value.element.name, "gimp-print") != 0)
285    {
286      stp_erprintf
287	("XML file of the wrong type, root node is %s != (gutenprint || gimp-print)",
288	 cur->value.element.name);
289      stp_mxmlDelete(doc);
290      return 1;
291    }
292
293  /* The XML file was read and is the right format */
294
295  stpi_xml_process_gutenprint(cur, file);
296  stp_mxmlDelete(doc);
297
298  stp_xml_exit();
299
300  return 0;
301}
302
303/*
304 * Convert a text string into an integer.
305 */
306long
307stp_xmlstrtol(const char *textval)
308{
309  long val; /* The value to return */
310  val = strtol(textval, (char **)NULL, 0);
311
312  return val;
313}
314
315/*
316 * Convert a text string into an unsigned int.
317 */
318unsigned long
319stp_xmlstrtoul(const char *textval)
320{
321  unsigned long val; /* The value to return */
322  val = strtoul(textval, (char **)NULL, 0);
323
324  return val;
325}
326
327/*
328 * Convert a text string into a double.
329 */
330double
331stp_xmlstrtod(const char *textval)
332{
333  double val; /* The value to return */
334  val = strtod(textval, (char **)NULL);
335
336  return val;
337}
338
339/*
340 * Convert an encoded text string into a raw.
341 */
342stp_raw_t *
343stp_xmlstrtoraw(const char *textval)
344{
345  size_t tcount;
346  stp_raw_t *raw;
347  unsigned char *answer;
348  unsigned char *aptr;
349  if (! textval || *textval == 0)
350    return NULL;
351  tcount = strlen(textval);
352  raw = stp_zalloc(sizeof(stp_raw_t));
353  answer = stp_malloc(tcount + 1); /* Worst case -- we may not need it all */
354  aptr = answer;
355  raw->data = answer;
356  while (*textval)
357    {
358      if (*textval != '\\')
359	{
360	  *aptr++ = *textval++;
361	  raw->bytes++;
362	}
363      else
364	{
365	  textval++;
366	  if (textval[0] >= '0' && textval[0] <= '3' &&
367	      textval[1] >= '0' && textval[1] <= '7' &&
368	      textval[2] >= '0' && textval[2] <= '7')
369	    {
370	      *aptr++ = (((textval[0] - '0') << 6) +
371			 ((textval[1] - '0') << 3) +
372			 ((textval[2] - '0') << 0));
373	      raw->bytes++;
374	      textval += 3;
375	    }
376	  else if (textval[0] == '\0' || textval[1] == '\0' || textval[2] == '\0')
377	    break;
378	  else
379	    textval += 3;
380	}
381    }
382  *aptr = '\0';
383  return raw;
384}
385
386char *
387stp_rawtoxmlstr(const stp_raw_t *raw)
388{
389  if (raw && raw->bytes > 0)
390    {
391      int i;
392      const unsigned char *data = (const unsigned char *) (raw->data);
393      char *answer = stp_malloc((raw->bytes * 4) + 1); /* \012 */
394      unsigned char *aptr = (unsigned char *) answer;
395      for (i = 0; i < raw->bytes; i++)
396	{
397	  if (data[i] > ' ' && data[i] < '\177' && data[i] != '\\' &&
398	      data[i] != '<' && data[i] != '>' && data[i] != '&')
399	    *aptr++ = data[i];
400	  else
401	    {
402	      *aptr++ = '\\';
403	      *aptr++ = '0' + ((data[i] & '\300') >> 6);
404	      *aptr++ = '0' + ((data[i] & '\070') >> 3);
405	      *aptr++ = '0' + ((data[i] & '\007') >> 0);
406	    }
407	}
408      *aptr = '\0';
409      return answer;
410    }
411  return NULL;
412}
413
414char *
415stp_strtoxmlstr(const char *str)
416{
417  if (str && strlen(str) > 0)
418    {
419      int i;
420      int bytes = strlen(str);
421      const unsigned char *data = (const unsigned char *) (str);
422      char *answer = stp_malloc((bytes * 4) + 1); /* "\012" is worst case */
423      unsigned char *aptr = (unsigned char *) answer;
424      for (i = 0; i < bytes; i++)
425	{
426	  if (data[i] > ' ' && data[i] < '\177' && data[i] != '\\' &&
427	      data[i] != '<' && data[i] != '>' && data[i] != '&')
428	    *aptr++ = data[i];
429	  else
430	    {
431	      *aptr++ = '\\';
432	      *aptr++ = '0' + ((data[i] & '\300') >> 6);
433	      *aptr++ = '0' + ((data[i] & '\070') >> 3);
434	      *aptr++ = '0' + ((data[i] & '\007') >> 0);
435	    }
436	}
437      *aptr = '\0';
438      return answer;
439    }
440  return NULL;
441}
442
443void
444stp_prtraw(const stp_raw_t *raw, FILE *fp)
445{
446  if (raw && raw->bytes > 0)
447    {
448      int i;
449      const unsigned char *data = (const unsigned char *) (raw->data);
450      for (i = 0; i < raw->bytes; i++)
451	{
452	  if (data[i] > ' ' && data[i] < '\177' && data[i] != '\\' &&
453	      data[i] != '<' && data[i] != '>' && data[i] != '&')
454	    fputc(data[i], fp);
455	  else
456	    {
457	      fputc('\\', fp);
458	      fputc('0' + ((data[i] & '\300') >> 6), fp);
459	      fputc('0' + ((data[i] & '\070') >> 3), fp);
460	      fputc('0' + ((data[i] & '\007') >> 0), fp);
461	    }
462	}
463    }
464}
465
466
467/*
468 * Find a node in an XML tree.  This function takes an xmlNodePtr,
469 * followed by a NULL-terminated list of nodes which are required.
470 * For example stp_xml_get_node(myroot, "gutenprint", "dither") will
471 * return the first dither node in the tree.  Additional dither nodes
472 * cannot be accessed with this function.
473 */
474stp_mxml_node_t *
475stp_xml_get_node(stp_mxml_node_t *xmlroot, ...)
476{
477  stp_mxml_node_t *child;
478  va_list ap;
479  const char *target = NULL;
480
481  va_start(ap, xmlroot);
482
483  child = xmlroot;
484  target = va_arg(ap, const char *);
485
486  while (target && child)
487    {
488      child = stp_mxmlFindElement(child, child, target, NULL, NULL, STP_MXML_DESCEND);
489      target = va_arg(ap, const char *);
490    }
491  va_end(ap);
492  return child;
493}
494
495static void
496stpi_xml_process_node(stp_mxml_node_t *node, const char *file)
497{
498  stp_list_item_t *item =
499    stp_list_get_item_by_name(stpi_xml_registry, node->value.element.name);
500  if (item)
501    {
502      stpi_xml_parse_registry *xmlp =
503	(stpi_xml_parse_registry *) stp_list_item_get_data(item);
504      (xmlp->parse_func)(node, file);
505    }
506}
507
508/*
509 * Parse the <gutenprint> root node.
510 */
511static void
512stpi_xml_process_gutenprint(stp_mxml_node_t *cur, const char *file) /* The node to parse */
513{
514  stp_mxml_node_t *child;                       /* Child node pointer */
515
516  child = cur->child;
517  while (child)
518    {
519      /* process nodes with corresponding parser */
520      if (child->type == STP_MXML_ELEMENT)
521	stpi_xml_process_node(child, file);
522      child = child->next;
523    }
524}
525
526/*
527 * Create a basic gutenprint XML document tree root
528 */
529stp_mxml_node_t *
530stp_xmldoc_create_generic(void)
531{
532  stp_mxml_node_t *doc;
533  stp_mxml_node_t *rootnode;
534
535  /* Create the XML tree */
536  doc = stp_mxmlNewElement(NULL, "?xml");
537  stp_mxmlElementSetAttr(doc, "version", "1.0");
538
539  rootnode = stp_mxmlNewElement(doc, "gutenprint");
540  stp_mxmlElementSetAttr
541    (rootnode, "xmlns", "http://gimp-print.sourceforge.net/xsd/gp.xsd-1.0");
542  stp_mxmlElementSetAttr
543    (rootnode, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
544  stp_mxmlElementSetAttr
545    (rootnode, "xsi:schemaLocation",
546     "http://gimp-print.sourceforge.net/xsd/gp.xsd-1.0 gutenprint.xsd");
547
548  return doc;
549}
550
551
552
553