]>
Commit | Line | Data |
---|---|---|
9b66acc5 MS |
1 | /* |
2 | * "$Id$" | |
3 | * | |
4 | * IANA XML registration to test file generator for CUPS. | |
5 | * | |
12f89d24 | 6 | * Copyright 2011-2012 by Apple Inc. |
9b66acc5 MS |
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 | ||
12f89d24 MS |
37 | |
38 | #include <config.h> | |
9b66acc5 MS |
39 | #include <cups/cups.h> |
40 | #include <unistd.h> | |
41 | #include <fcntl.h> | |
42 | ||
12f89d24 MS |
43 | #ifdef HAVE_MXML_H |
44 | # include <mxml.h> | |
9b66acc5 MS |
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 | ||
12f89d24 MS |
518 | #else /* !HAVE_MXML */ |
519 | int | |
520 | main(void) | |
521 | { | |
522 | return (1); | |
523 | } | |
524 | #endif /* HAVE_MXML */ | |
525 | ||
526 | ||
9b66acc5 MS |
527 | /* |
528 | * End of "$Id$". | |
529 | */ |