1/* 2 * "$Id: xmltotest.c 3650 2012-02-13 18:13:46Z msweet $" 3 * 4 * IANA XML registration to test file generator for CUPS. 5 * 6 * Copyright 2011-2012 by Apple Inc. 7 * 8 * These coded instructions, statements, and computer programs are the 9 * property of Apple Inc. and are protected by Federal copyright 10 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 11 * which should have been included with this file. If this file is 12 * file is missing or damaged, see the license at "http://www.cups.org/". 13 * 14 * This file is subject to the Apple OS-Developed Software exception. 15 * 16 * Usage: 17 * 18 * ./xmltotest [--ref standard] {--job|--printer} [XML file/URL] >file.test 19 * 20 * If not specified, loads the XML registrations from: 21 * 22 * http://www.iana.org/assignments/ipp-registrations/ipp-registrations.xml 23 * 24 * "Standard" is of the form "rfcNNNN" or "pwgNNNN.N". 25 * 26 * Contents: 27 * 28 * main() - Process command-line arguments. 29 * compare_reg() - Compare two registrations. 30 * load_xml() - Load the XML registration file or URL. 31 * match_xref() - Compare the xref against the named standard. 32 * new_reg() - Create a new registration record. 33 * usage() - Show usage message. 34 * write_expect() - Write an EXPECT test for an attribute. 35 */ 36 37 38#include <config.h> 39#include <cups/cups.h> 40#include <unistd.h> 41#include <fcntl.h> 42 43#ifdef HAVE_MXML_H 44# include <mxml.h> 45/* 46 * Local types... 47 */ 48 49typedef struct _cups_reg_s /**** Registration data ****/ 50{ 51 char *name, /* Attribute name */ 52 *member, /* Member attribute name */ 53 *sub_member, /* Sub-member attribute name */ 54 *syntax; /* Attribute syntax */ 55} _cups_reg_t; 56 57 58/* 59 * Local functions... 60 */ 61 62static int compare_reg(_cups_reg_t *a, _cups_reg_t *b); 63static mxml_node_t *load_xml(const char *reg_file); 64static int match_xref(mxml_node_t *xref, const char *standard); 65static _cups_reg_t *new_reg(mxml_node_t *name, mxml_node_t *member, 66 mxml_node_t *sub_member, mxml_node_t *syntax); 67static int usage(void); 68static void write_expect(_cups_reg_t *reg, ipp_tag_t group); 69 70 71/* 72 * 'main()' - Process command-line arguments. 73 */ 74 75int 76main(int argc, /* I - Number of command-line args */ 77 char *argv[]) /* I - Command-line arguments */ 78{ 79 int i; /* Looping var */ 80 const char *reg_file = NULL, /* Registration file/URL to use */ 81 *reg_standard = NULL; /* Which standard to extract */ 82 mxml_node_t *reg_xml, /* Registration XML data */ 83 *reg_2, /* ipp-registrations-2 */ 84 *reg_record, /* <record> */ 85 *reg_collection, /* <collection> */ 86 *reg_name, /* <name> */ 87 *reg_member, /* <member_attribute> */ 88 *reg_sub_member, /* <sub-member_attribute> */ 89 *reg_syntax, /* <syntax> */ 90 *reg_xref; /* <xref> */ 91 cups_array_t *attrs; /* Attribute registrations */ 92 _cups_reg_t *current; /* Current attribute registration */ 93 ipp_tag_t group = IPP_TAG_ZERO, /* Which attributes to test */ 94 reg_group; /* Group for registration */ 95 96 97 /* 98 * Parse command-line... 99 */ 100 101 for (i = 1; i < argc; i ++) 102 { 103 if (!strcmp(argv[i], "--job") && group == IPP_TAG_ZERO) 104 group = IPP_TAG_JOB; 105 else if (!strcmp(argv[i], "--ref")) 106 { 107 i ++; 108 if (i >= argc) 109 return (usage()); 110 111 reg_standard = argv[i]; 112 } 113 else if (!strcmp(argv[i], "--printer") && group == IPP_TAG_ZERO) 114 group = IPP_TAG_PRINTER; 115 else if (argv[i][0] == '-' || reg_file) 116 return (usage()); 117 else 118 reg_file = argv[i]; 119 } 120 121 if (group == IPP_TAG_ZERO) 122 return (usage()); 123 124 /* 125 * Read registrations... 126 */ 127 128 if (!reg_file) 129 reg_file = "http://www.iana.org/assignments/ipp-registrations/" 130 "ipp-registrations.xml"; 131 132 if ((reg_xml = load_xml(reg_file)) == NULL) 133 return (1); 134 135 /* 136 * Scan registrations for attributes... 137 */ 138 139 if ((reg_2 = mxmlFindElement(reg_xml, reg_xml, "registry", "id", 140 "ipp-registrations-2", 141 MXML_DESCEND)) == NULL) 142 { 143 fprintf(stderr, "xmltotest: No IPP attribute registrations in \"%s\".\n", 144 reg_file); 145 return (1); 146 } 147 148 attrs = cupsArrayNew((cups_array_func_t)compare_reg, NULL); 149 150 for (reg_record = mxmlFindElement(reg_2, reg_2, "record", NULL, NULL, 151 MXML_DESCEND); 152 reg_record; 153 reg_record = mxmlFindElement(reg_record, reg_2, "record", NULL, NULL, 154 MXML_NO_DESCEND)) 155 { 156 /* 157 * Get the values from the current record... 158 */ 159 160 reg_collection = mxmlFindElement(reg_record, reg_record, "collection", 161 NULL, NULL, MXML_DESCEND); 162 reg_name = mxmlFindElement(reg_record, reg_record, "name", NULL, NULL, 163 MXML_DESCEND); 164 reg_member = mxmlFindElement(reg_record, reg_record, "member_attribute", 165 NULL, NULL, MXML_DESCEND); 166 reg_sub_member = mxmlFindElement(reg_record, reg_record, 167 "sub-member_attribute", NULL, NULL, 168 MXML_DESCEND); 169 reg_syntax = mxmlFindElement(reg_record, reg_record, "syntax", NULL, 170 NULL, MXML_DESCEND); 171 reg_xref = mxmlFindElement(reg_record, reg_record, "xref", NULL, NULL, 172 MXML_DESCEND); 173 174 if (!reg_collection || !reg_name || !reg_syntax || !reg_xref) 175 continue; 176 177 /* 178 * Filter based on group and standard... 179 */ 180 181 if (!strcmp(reg_collection->child->value.opaque, "Printer Description")) 182 reg_group = IPP_TAG_PRINTER; 183 else if (!strcmp(reg_collection->child->value.opaque, "Job Description")) 184 reg_group = IPP_TAG_JOB; 185 else if (!strcmp(reg_collection->child->value.opaque, "Job Template")) 186 { 187 if (strstr(reg_name->child->value.opaque, "-default") || 188 strstr(reg_name->child->value.opaque, "-supported")) 189 reg_group = IPP_TAG_PRINTER; 190 else 191 reg_group = IPP_TAG_JOB; 192 } 193 else 194 reg_group = IPP_TAG_ZERO; 195 196 if (reg_group != group) 197 continue; 198 199 if (reg_standard && !match_xref(reg_xref, reg_standard)) 200 continue; 201 202 /* 203 * Add the record to the array... 204 */ 205 206 if ((current = new_reg(reg_name, reg_member, reg_sub_member, 207 reg_syntax)) != NULL) 208 cupsArrayAdd(attrs, current); 209 } 210 211 /* 212 * Write out a test for all of the selected attributes... 213 */ 214 215 puts("{"); 216 217 if (group == IPP_TAG_PRINTER) 218 { 219 puts("\tOPERATION Get-Printer-Attributes"); 220 puts("\tGROUP operation-attributes-tag"); 221 puts("\tATTR charset attributes-charset utf-8"); 222 puts("\tATTR naturalLanguage attributes-natural-language en"); 223 puts("\tATTR uri printer-uri $uri"); 224 puts("\tATTR name requesting-user-name $user"); 225 puts("\tATTR keyword requested-attributes all,media-col-database"); 226 puts(""); 227 puts("\tSTATUS successful-ok"); 228 puts("\tSTATUS successful-ok-ignored-or-substituted-attributes"); 229 puts(""); 230 } 231 else 232 { 233 puts("\tOPERATION Get-Job-Attributes"); 234 puts("\tGROUP operation-attributes-tag"); 235 puts("\tATTR charset attributes-charset utf-8"); 236 puts("\tATTR naturalLanguage attributes-natural-language en"); 237 puts("\tATTR uri printer-uri $uri"); 238 puts("\tATTR integer job-id $job-id"); 239 puts("\tATTR name requesting-user-name $user"); 240 puts(""); 241 puts("\tSTATUS successful-ok"); 242 puts(""); 243 } 244 245 for (current = cupsArrayFirst(attrs); 246 current; 247 current = cupsArrayNext(attrs)) 248 write_expect(current, group); 249 250 puts("}"); 251 252 return (0); 253} 254 255 256/* 257 * 'compare_reg()' - Compare two registrations. 258 */ 259 260static int /* O - Result of comparison */ 261compare_reg(_cups_reg_t *a, /* I - First registration */ 262 _cups_reg_t *b) /* I - Second registration */ 263{ 264 int retval; /* Return value */ 265 266 267 if ((retval = strcmp(a->name, b->name)) != 0) 268 return (retval); 269 270 if (a->member && b->member) 271 retval = strcmp(a->member, b->member); 272 else if (a->member) 273 retval = 1; 274 else if (b->member) 275 retval = -1; 276 277 if (retval) 278 return (retval); 279 280 if (a->sub_member && b->sub_member) 281 retval = strcmp(a->sub_member, b->sub_member); 282 else if (a->sub_member) 283 retval = 1; 284 else if (b->sub_member) 285 retval = -1; 286 287 return (retval); 288} 289 290 291/* 292 * 'load_xml()' - Load the XML registration file or URL. 293 */ 294 295static mxml_node_t * /* O - XML file or NULL */ 296load_xml(const char *reg_file) /* I - Filename or URL */ 297{ 298 mxml_node_t *xml; /* XML file */ 299 char scheme[256], /* Scheme */ 300 userpass[256], /* Username and password */ 301 hostname[256], /* Hostname */ 302 resource[1024], /* Resource path */ 303 filename[1024]; /* Temporary file */ 304 int port, /* Port number */ 305 fd; /* File descriptor */ 306 307 308 if (httpSeparateURI(HTTP_URI_CODING_ALL, reg_file, scheme, sizeof(scheme), 309 userpass, sizeof(userpass), hostname, sizeof(hostname), 310 &port, resource, sizeof(resource)) < HTTP_URI_OK) 311 { 312 fprintf(stderr, "xmltotest: Bad URI or filename \"%s\".\n", reg_file); 313 return (NULL); 314 } 315 316 if (!strcmp(scheme, "file")) 317 { 318 /* 319 * Local file... 320 */ 321 322 if ((fd = open(resource, O_RDONLY)) < 0) 323 { 324 fprintf(stderr, "xmltotest: Unable to open \"%s\": %s\n", resource, 325 strerror(errno)); 326 return (NULL); 327 } 328 329 filename[0] = '\0'; 330 } 331 else if (strcmp(scheme, "http") && strcmp(scheme, "https")) 332 { 333 fprintf(stderr, "xmltotest: Unsupported URI scheme \"%s\".\n", scheme); 334 return (NULL); 335 } 336 else 337 { 338 http_t *http; /* HTTP connection */ 339 http_encryption_t encryption; /* Encryption to use */ 340 http_status_t status; /* Status of HTTP GET */ 341 342 if (!strcmp(scheme, "https") || port == 443) 343 encryption = HTTP_ENCRYPT_ALWAYS; 344 else 345 encryption = HTTP_ENCRYPT_IF_REQUESTED; 346 347 if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL) 348 { 349 fprintf(stderr, "xmltotest: Unable to connect to \"%s\": %s\n", hostname, 350 cupsLastErrorString()); 351 return (NULL); 352 } 353 354 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0) 355 { 356 fprintf(stderr, "xmltotest: Unable to create temporary file: %s\n", 357 strerror(errno)); 358 httpClose(http); 359 return (NULL); 360 } 361 362 status = cupsGetFd(http, resource, fd); 363 httpClose(http); 364 365 if (status != HTTP_OK) 366 { 367 fprintf(stderr, "mxmltotest: Unable to get \"%s\": %d\n", reg_file, 368 status); 369 close(fd); 370 unlink(filename); 371 return (NULL); 372 } 373 374 lseek(fd, 0, SEEK_SET); 375 } 376 377 /* 378 * Load the XML file... 379 */ 380 381 xml = mxmlLoadFd(NULL, fd, MXML_OPAQUE_CALLBACK); 382 383 close(fd); 384 385 if (filename[0]) 386 unlink(filename); 387 388 return (xml); 389} 390 391 392/* 393 * 'match_xref()' - Compare the xref against the named standard. 394 */ 395 396static int /* O - 1 if match, 0 if not */ 397match_xref(mxml_node_t *xref, /* I - <xref> node */ 398 const char *standard) /* I - Name of standard */ 399{ 400 const char *data; /* "data" attribute */ 401 char s[256]; /* String to look for */ 402 403 404 if ((data = mxmlElementGetAttr(xref, "data")) == NULL) 405 return (1); 406 407 if (!strcmp(data, standard)) 408 return (1); 409 410 if (!strncmp(standard, "pwg", 3)) 411 { 412 snprintf(s, sizeof(s), "-%s.pdf", standard + 3); 413 return (strstr(data, s) != NULL); 414 } 415 else 416 return (0); 417} 418 419 420/* 421 * 'new_reg()' - Create a new registration record. 422 */ 423 424static _cups_reg_t * /* O - New record */ 425new_reg(mxml_node_t *name, /* I - Attribute name */ 426 mxml_node_t *member, /* I - Member attribute, if any */ 427 mxml_node_t *sub_member, /* I - Sub-member attribute, if any */ 428 mxml_node_t *syntax) /* I - Syntax */ 429{ 430 _cups_reg_t *reg; /* New record */ 431 432 433 if ((reg = calloc(1, sizeof(_cups_reg_t))) != NULL) 434 { 435 reg->name = name->child->value.opaque; 436 reg->syntax = syntax->child->value.opaque; 437 438 if (member) 439 reg->member = member->child->value.opaque; 440 441 if (sub_member) 442 reg->sub_member = sub_member->child->value.opaque; 443 } 444 445 return (reg); 446} 447 448 449/* 450 * 'usage()' - Show usage message. 451 */ 452 453static int /* O - Exit status */ 454usage(void) 455{ 456 puts("Usage ./xmltotest [--ref standard] {--job|--printer} [XML file/URL] " 457 ">file.test"); 458 return (1); 459} 460 461 462/* 463 * 'write_expect()' - Write an EXPECT test for an attribute. 464 */ 465 466static void 467write_expect(_cups_reg_t *reg, /* I - Registration information */ 468 ipp_tag_t group) /* I - Attribute group tag */ 469{ 470 const char *syntax; /* Pointer into syntax string */ 471 int single = 1, /* Single valued? */ 472 skip = 0; /* Skip characters? */ 473 474 475 printf("\tEXPECT ?%s OF-TYPE ", reg->name); 476 477 syntax = reg->syntax; 478 479 while (*syntax) 480 { 481 if (!strncmp(syntax, "1setOf", 6)) 482 { 483 single = 0; 484 syntax += 6; 485 486 while (isspace(*syntax & 255)) 487 syntax ++; 488 489 if (*syntax == '(') 490 syntax ++; 491 } 492 else if (!strncmp(syntax, "type1", 5) || !strncmp(syntax, "type2", 5) || 493 !strncmp(syntax, "type3", 5)) 494 syntax += 5; 495 else if (*syntax == '(') 496 { 497 skip = 1; 498 syntax ++; 499 } 500 else if (*syntax == ')') 501 { 502 skip = 0; 503 syntax ++; 504 } 505 else if (!skip && (*syntax == '|' || isalpha(*syntax & 255))) 506 putchar(*syntax++); 507 else 508 syntax ++; 509 } 510 511 if (single) 512 printf(" IN-GROUP %s COUNT 1\n", ippTagString(group)); 513 else 514 printf(" IN-GROUP %s\n", ippTagString(group)); 515} 516 517 518#else /* !HAVE_MXML */ 519int 520main(void) 521{ 522 return (1); 523} 524#endif /* HAVE_MXML */ 525 526 527/* 528 * End of "$Id: xmltotest.c 3650 2012-02-13 18:13:46Z msweet $". 529 */ 530