]> git.ipfire.org Git - thirdparty/cups.git/blob - test/xmltotest.c
Import CUPS v1.7.1
[thirdparty/cups.git] / test / xmltotest.c
1 /*
2 * "$Id: xmltotest.c 3643 2012-02-13 16:35:48Z 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
49 typedef 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
62 static int compare_reg(_cups_reg_t *a, _cups_reg_t *b);
63 static mxml_node_t *load_xml(const char *reg_file);
64 static int match_xref(mxml_node_t *xref, const char *standard);
65 static _cups_reg_t *new_reg(mxml_node_t *name, mxml_node_t *member,
66 mxml_node_t *sub_member, mxml_node_t *syntax);
67 static int usage(void);
68 static void write_expect(_cups_reg_t *reg, ipp_tag_t group);
69
70
71 /*
72 * 'main()' - Process command-line arguments.
73 */
74
75 int
76 main(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
260 static int /* O - Result of comparison */
261 compare_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
295 static mxml_node_t * /* O - XML file or NULL */
296 load_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
396 static int /* O - 1 if match, 0 if not */
397 match_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
424 static _cups_reg_t * /* O - New record */
425 new_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
453 static int /* O - Exit status */
454 usage(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
466 static void
467 write_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 */
519 int
520 main(void)
521 {
522 return (1);
523 }
524 #endif /* HAVE_MXML */
525
526
527 /*
528 * End of "$Id: xmltotest.c 3643 2012-02-13 16:35:48Z msweet $".
529 */