1/* 2 * "xmlppd.c" 3 * 4 * PPD to XML converter. 5 * 6 * Copyright 2007 by Michael R Sweet and Robert Krawitz 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License 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., 51 Franklin Street, Fifth Floor, Boston, MA 21 * 02110-1301 USA 22 */ 23 24#include <stdio.h> 25#include <gutenprint/mxml.h> 26#include <gutenprint/util.h> 27#include <gutenprint/string-list.h> 28#include <stdlib.h> 29#include "xmlppd.h" 30 31typedef struct 32{ 33 int order; 34 const char *name; 35} order_t; 36 37static int 38order_compare(const void *a, const void *b) 39{ 40 const order_t *aa = (const order_t *)a; 41 const order_t *bb = (const order_t *)b; 42 if (aa->order < bb->order) 43 return -1; 44 else if (aa->order > bb->order) 45 return 1; 46 else 47 return strcmp(aa->name, bb->name); 48} 49 50 51static stp_mxml_node_t * 52find_element_named(stp_mxml_node_t *root, const char *name, const char *what) 53{ 54 stp_mxml_node_t *element; 55 if (root && name) 56 { 57 for (element = stp_mxmlFindElement(root, root, what, NULL, NULL, 58 STP_MXML_DESCEND); 59 element; 60 element = stp_mxmlFindElement(element, root, what, NULL, NULL, 61 STP_MXML_DESCEND)) 62 { 63 if (!strcmp(stp_mxmlElementGetAttr(element, "name"), name)) 64 return element; 65 } 66 } 67 return NULL; 68} 69 70static stp_mxml_node_t * 71find_element_index(stp_mxml_node_t *root, int idx, const char *what) 72{ 73 stp_mxml_node_t *element; 74 int count = 0; 75 if (root && idx >= 0) 76 { 77 for (element = stp_mxmlFindElement(root, root, what, NULL, NULL, 78 STP_MXML_DESCEND); 79 element; 80 element = stp_mxmlFindElement(element, root, what, NULL, NULL, 81 STP_MXML_DESCEND)) 82 { 83 if (count++ == idx) 84 return element; 85 } 86 } 87 return NULL; 88} 89 90static size_t 91find_element_count(stp_mxml_node_t *root, const char *what) 92{ 93 stp_mxml_node_t *element; 94 size_t count = 0; 95 if (root) 96 { 97 for (element = stp_mxmlFindElement(root, root, what, NULL, NULL, 98 STP_MXML_DESCEND); 99 element; 100 element = stp_mxmlFindElement(element, root, what, NULL, NULL, 101 STP_MXML_DESCEND)) 102 count++; 103 } 104 return count; 105} 106 107stp_mxml_node_t * 108stpi_xmlppd_find_group_named(stp_mxml_node_t *root, const char *name) 109{ 110 return find_element_named(root, name, "group"); 111} 112 113stp_mxml_node_t * 114stpi_xmlppd_find_group_index(stp_mxml_node_t *root, int idx) 115{ 116 return find_element_index(root, idx, "group"); 117} 118 119int 120stpi_xmlppd_find_group_count(stp_mxml_node_t *root) 121{ 122 return find_element_count(root, "group"); 123} 124 125stp_mxml_node_t * 126stpi_xmlppd_find_option_named(stp_mxml_node_t *root, const char *name) 127{ 128 return find_element_named(root, name, "option"); 129} 130 131stp_mxml_node_t * 132stpi_xmlppd_find_option_index(stp_mxml_node_t *root, int idx) 133{ 134 return find_element_index(root, idx, "option"); 135} 136 137int 138stpi_xmlppd_find_option_count(stp_mxml_node_t *root) 139{ 140 return find_element_count(root, "option"); 141} 142 143stp_mxml_node_t * 144stpi_xmlppd_find_choice_named(stp_mxml_node_t *option, const char *name) 145{ 146 return find_element_named(option, name, "choice"); 147} 148 149stp_mxml_node_t * 150stpi_xmlppd_find_choice_index(stp_mxml_node_t *option, int idx) 151{ 152 return find_element_index(option, idx, "choice"); 153} 154 155int 156stpi_xmlppd_find_choice_count(stp_mxml_node_t *option) 157{ 158 return find_element_count(option, "choice"); 159} 160 161stp_mxml_node_t * 162stpi_xmlppd_find_page_size(stp_mxml_node_t *root, const char *name) 163{ 164 return stpi_xmlppd_find_choice_named(stpi_xmlppd_find_option_named(root, "PageSize"), name); 165} 166 167static void 168parse_values(const char **data, int limit, char *value) 169{ 170 int dptr = 0; 171 char *where = value; 172 char *end = value; 173 for (dptr = 0; dptr < limit; dptr++) 174 data[dptr] = NULL; 175 for (dptr = 0; *where && dptr < limit; dptr++) 176 { 177 where = end; 178 while (*where && isspace(*where)) 179 where++; 180 end = where; 181 while (*end && !isspace(*end)) 182 end++; 183 *end++ = '\0'; 184 data[dptr] = where; 185 } 186} 187 188/* 189 * 'read_ppd_file()' - Read a PPD file into XML data. 190 */ 191 192stp_mxml_node_t * /* O - PPD file as XML */ 193stpi_xmlppd_read_ppd_file(const char *filename) /* I - PPD file */ 194{ 195 stp_mxml_node_t *ppd, /* Root node of "ppd" group */ 196 *group, /* Current group */ 197 *option, /* Current option */ 198 *choice; /* Current choice */ 199 FILE *fp; /* PPD file */ 200 int ch, /* Current character */ 201 sawcolon, /* Saw a colon? */ 202 inquote, /* In a quoted string? */ 203 num_choices = 0; 204 char buffer[32768], /* Line buffer */ 205 *bufptr, /* Pointer into line */ 206 *xptr, /* Pointer into line */ 207 option_name[42], /* Option name */ 208 stp_option_data_name[64], /* Option name */ 209 *keyword, /* Pointer to option keyword */ 210 *text, /* Pointer to text */ 211 *value; /* Pointer to value */ 212 order_t *order_array; /* Precedence order of options */ 213 int i; 214 int option_count; 215 int order_length; 216 char *order_list; 217 int in_comment; 218 stp_string_list_t *ialist = stp_string_list_create(); 219 stp_string_list_t *pdlist = stp_string_list_create(); 220 221 222 /* 223 * Open the file... 224 */ 225 226 if ((fp = fopen(filename, "rb")) == NULL) 227 { 228 perror(filename); 229 return (NULL); 230 } 231 232 /* 233 * Create the base PPD file tree; the completed tree will look like: 234 * 235 * <?xml version="1.0"?> 236 * <ppd color="0/1" level="1/2/3"> 237 * <option name="..." text="..." default="..." section="..." order="..." 238 * type="..."> 239 * <choice name="..." text="...">code</choice> 240 * ... 241 * </option> 242 * ... 243 * <group name="..." text="..."> 244 * <option ...> 245 * <choice ...>code</choice> 246 * </option> 247 * </group> 248 * </ppd> 249 */ 250 251/* xml = stp_mxmlNewXML("1.0"); */ 252/* ppd = stp_mxmlNewElement(xml, "ppd"); */ 253 ppd = stp_mxmlNewElement(STP_MXML_NO_PARENT, "ppd"); 254 255 stp_mxmlElementSetAttr(ppd, "color", "0"); 256 stp_mxmlElementSetAttr(ppd, "level", "2"); 257 258 /* 259 * Read all of the lines of the form: 260 * 261 * *% comment 262 * *Keyword: value 263 * *Keyword OptionKeyword: value 264 * *Keyword OptionKeyword/Text: value 265 * 266 * Only save groups, options, and choices, along with specific metadata 267 * from the file... 268 */ 269 270 group = NULL; 271 option = NULL; 272 option_name[0] = '\0'; 273 274 while ((ch = getc(fp)) != EOF) 275 { 276 /* 277 * Read the line... 278 */ 279 280 buffer[0] = ch; 281 bufptr = buffer + 1; 282 inquote = 0; 283 sawcolon = 0; 284 in_comment = 0; 285 if (ch == '*') 286 { 287 if ((ch = getc(fp)) == '%') 288 { 289 while (1) 290 { 291 ch = getc(fp); 292 if (ch == '\n' || ch == EOF) 293 break; 294 } 295 continue; 296 } 297 else 298 ungetc(ch, fp); 299 } 300 301 while ((ch != '\n' || inquote) && bufptr < (buffer + sizeof(buffer) - 1)) 302 { 303 if ((ch = getc(fp)) == '\r') 304 { 305 if ((ch = getc(fp)) != '\n') 306 ungetc(ch, fp); 307 } 308 309 *bufptr++ = ch; 310 311 if (ch == ':' && !sawcolon) 312 sawcolon = 1; 313 else if (ch == '\"' && sawcolon) 314 inquote = !inquote; 315 } 316 317 /* 318 * Strip trailing whitespace... 319 */ 320 321 for (bufptr --; bufptr > buffer && isspace(bufptr[-1] & 255); bufptr --); 322 323 *bufptr = '\0'; 324 325 /* 326 * Now parse it... 327 */ 328 329 if (!strncmp(buffer, "*%", 2) || !buffer[0]) 330 continue; 331 332 if ((value = strchr(buffer, ':')) == NULL) 333 continue; 334 335 for (*value++ = '\0'; 336 *value && (isspace(*value & 255) || (*value & 255) == '"'); 337 value ++); 338 for (xptr = value; 339 *xptr && (*xptr & 255) != '"'; 340 xptr ++); 341 if (*xptr == '"') 342 *xptr = '\0'; 343 344 for (keyword = buffer; *keyword && !isspace(*keyword & 255); keyword ++); 345 346 while (isspace(*keyword & 255)) 347 *keyword++ = '\0'; 348 349 if ((text = strchr(keyword, '/')) != NULL) 350 *text++ = '\0'; 351 352 /* 353 * And then use the parsed values... 354 */ 355 356 if (!strcmp(buffer, "*ColorDevice")) 357 { 358 /* 359 * Color support... 360 */ 361 362 if (!strcasecmp(value, "true")) 363 stp_mxmlElementSetAttr(ppd, "color", "1"); 364 else 365 stp_mxmlElementSetAttr(ppd, "color", "0"); 366 } 367 else if (!strcmp(buffer, "*LanguageLevel") && atoi(value) > 0) 368 { 369 /* 370 * PostScript language level... 371 */ 372 373 stp_mxmlElementSetAttr(ppd, "level", value); 374 } 375 else if (!strcmp(buffer, "*StpDriverName")) 376 stp_mxmlElementSetAttr(ppd, "driver", value); 377 else if (!strcmp(buffer, "*ModelName")) 378 stp_mxmlElementSetAttr(ppd, "modelname", value); 379 else if (!strcmp(buffer, "*ShortNickName")) 380 stp_mxmlElementSetAttr(ppd, "shortnickname", value); 381 else if (!strcmp(buffer, "*NickName")) 382 stp_mxmlElementSetAttr(ppd, "nickname", value); 383 else if (!strcmp(buffer, "*OpenGroup")) 384 { 385 if ((text = strchr(value, '/')) != NULL) 386 *text++ = '\0'; 387 388 group = stp_mxmlNewElement(ppd, "group"); 389 stp_mxmlElementSetAttr(group, "name", value); 390 stp_mxmlElementSetAttr(group, "text", text ? text : value); 391 } 392 else if (!strcmp(buffer, "*CloseGroup")) 393 group = NULL; 394 else if ((!strcmp(buffer, "*OpenUI") || !strcmp(buffer, "*JCLOpenUI")) && 395 keyword[0] == '*' && keyword[1]) 396 { 397 /* 398 * Start a new option... 399 */ 400 401 option = stp_mxmlNewElement(group ? group : ppd, "option"); 402 stp_mxmlElementSetAttr(option, "name", keyword + 1); 403 stp_mxmlElementSetAttr(option, "text", text ? text : keyword + 1); 404 stp_mxmlElementSetAttr(option, "ui", value); 405 406 strncpy(option_name, keyword, sizeof(option_name) - 1); 407 option_name[sizeof(option_name) - 1] = '\0'; 408 strcpy(stp_option_data_name, "*Stp"); 409 strcpy(stp_option_data_name + 4, option_name + 1); 410 if (group) 411 { 412 stp_mxmlElementSetAttr(option, "groupname", stp_mxmlElementGetAttr(group, "name")); 413 stp_mxmlElementSetAttr(option, "grouptext", stp_mxmlElementGetAttr(group, "text")); 414 } 415 num_choices = 0; 416 } 417 else if (option && !strcmp(buffer, stp_option_data_name)) 418 { 419 const char *data[8]; 420 parse_values(data, 8, value); 421 if (data[7]) 422 { 423 stp_mxmlElementSetAttr(option, "stptype", data[0]); 424 stp_mxmlElementSetAttr(option, "stpmandatory", data[1]); 425 stp_mxmlElementSetAttr(option, "stpclass", data[2]); 426 stp_mxmlElementSetAttr(option, "stplevel", data[3]); 427 stp_mxmlElementSetAttr(option, "stpchannel", data[4]); 428 stp_mxmlElementSetAttr(option, "stplower", data[5]); 429 stp_mxmlElementSetAttr(option, "stpupper", data[6]); 430 stp_mxmlElementSetAttr(option, "stpdefault", data[7]); 431 stp_mxmlElementSetAttr(option, "stpname", stp_option_data_name + 7); 432 } 433 } 434 else if (!strcmp(buffer, "*OrderDependency") && option) 435 { 436 /* 437 * Get order and section for option 438 */ 439 440 char order[256], /* Order number */ 441 section[256]; /* Section name */ 442 443 444 if (sscanf(value, "%255s%255s", order, section) == 2) 445 { 446 stp_mxmlElementSetAttr(option, "order", order); 447 stp_mxmlElementSetAttr(option, "section", section); 448 } 449 } 450 else if (!strncmp(buffer, "*Default", 8) && option && 451 !strcmp(buffer + 8, option_name + 1)) 452 stp_mxmlElementSetAttr(option, "default", value); 453 else if (!strcmp(buffer, "*CloseUI") || !strcmp(buffer, "*JCLCloseUI")) 454 { 455 char buf[64]; 456 (void) sprintf(buf, "%d", num_choices); 457 stp_mxmlElementSetAttr(option, "num_choices", buf); 458 option = NULL; 459 stp_option_data_name[0] = '\0'; 460 } 461 else if (option && !strcmp(buffer, option_name)) 462 { 463 /* 464 * A choice... 465 */ 466 467 choice = stp_mxmlNewElement(option, "choice"); 468 stp_mxmlElementSetAttr(choice, "name", keyword); 469 stp_mxmlElementSetAttr(choice, "text", text ? text : keyword); 470 471 if (value[0] == '\"') 472 value ++; 473 474 if (bufptr > buffer && bufptr[-1] == '\"') 475 { 476 bufptr --; 477 *bufptr = '\0'; 478 } 479 480 stp_mxmlNewOpaque(choice, value); 481 num_choices++; 482 } 483 else if (!option && !strcmp(buffer, "*ImageableArea")) 484 { 485 stp_string_list_add_string(ialist, keyword, value); 486 } 487 else if (!option && !strcmp(buffer, "*PaperDimension")) 488 { 489 stp_string_list_add_string(pdlist, keyword, value); 490 } 491 } 492 if (option) 493 { 494 char buf[64]; 495 (void) sprintf(buf, "%d", num_choices); 496 stp_mxmlElementSetAttr(option, "num_choices", buf); 497 option = NULL; 498 stp_option_data_name[0] = '\0'; 499 } 500 501 for (i = 0; i < stp_string_list_count(ialist); i++) 502 { 503 stp_param_string_t *pstr = stp_string_list_param(ialist, i); 504 stp_mxml_node_t *psize = stpi_xmlppd_find_page_size(ppd, pstr->name); 505 if (psize) 506 { 507 const char *data[4]; 508 value = stp_strdup(pstr->text); 509 parse_values(data, 4, value); 510 if (data[3]) 511 { 512 stp_mxmlElementSetAttr(psize, "left", data[0]); 513 stp_mxmlElementSetAttr(psize, "bottom", data[1]); 514 stp_mxmlElementSetAttr(psize, "right", data[2]); 515 stp_mxmlElementSetAttr(psize, "top", data[3]); 516 } 517 stp_free(value); 518 } 519 } 520 stp_string_list_destroy(ialist); 521 for (i = 0; i < stp_string_list_count(pdlist); i++) 522 { 523 stp_param_string_t *pstr = stp_string_list_param(pdlist, i); 524 stp_mxml_node_t *psize = stpi_xmlppd_find_page_size(ppd, pstr->name); 525 if (psize) 526 { 527 const char *data[2]; 528 value = stp_strdup(pstr->text); 529 parse_values(data, 2, value); 530 if (data[1]) 531 { 532 stp_mxmlElementSetAttr(psize, "width", data[0]); 533 stp_mxmlElementSetAttr(psize, "height", data[1]); 534 } 535 stp_free(value); 536 } 537 } 538 stp_string_list_destroy(pdlist); 539 option_count = stpi_xmlppd_find_option_count(ppd); 540 order_length = 1; /* Terminating null */ 541 order_array = malloc(sizeof(order_t) * option_count); 542 i = 0; 543 for (option = stp_mxmlFindElement(ppd, ppd, "option", NULL, NULL, 544 STP_MXML_DESCEND); 545 option && i < option_count; 546 option = stp_mxmlFindElement(option, ppd, "option", NULL, NULL, 547 STP_MXML_DESCEND)) 548 { 549 if (stp_mxmlElementGetAttr(option, "order")) 550 { 551 order_array[i].name = stp_mxmlElementGetAttr(option, "name"); 552 order_length += strlen(order_array[i].name) + 1; 553 order_array[i].order = atoi(stp_mxmlElementGetAttr(option, "order")); 554 i++; 555 } 556 } 557 option_count = i; 558 qsort(order_array, option_count, sizeof(order_t), &order_compare); 559 order_list = malloc(order_length); 560 order_length = 0; 561 for (i = 0; i < option_count; i++) 562 { 563 if (i > 0) 564 order_list[order_length++] = ' '; 565 strcpy(order_list + order_length, order_array[i].name); 566 order_length += strlen(order_array[i].name); 567 } 568 stp_mxmlElementSetAttr(ppd, "optionorder", order_list); 569 free(order_list); 570 free(order_array); 571 return (ppd); 572} 573 574/* 575 * End of "xmlppd.c". 576 */ 577