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