2 * ipptool command for CUPS.
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers...
15 #include <cups/cups-private.h>
29 #endif /* !O_BINARY */
36 typedef enum _cups_transfer_e
/**** How to send request data ****/
38 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
39 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
40 _CUPS_TRANSFER_LENGTH
/* Length always */
43 typedef enum _cups_output_e
/**** Output mode ****/
45 _CUPS_OUTPUT_QUIET
, /* No output */
46 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
47 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
48 _CUPS_OUTPUT_IPPSERVER
, /* ippserver attribute file output */
49 _CUPS_OUTPUT_LIST
, /* Tabular list output */
50 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
53 typedef enum _cups_with_e
/**** WITH flags ****/
55 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
56 _CUPS_WITH_ALL
= 1, /* Must match all values */
57 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
58 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
59 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
60 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
63 typedef struct _cups_expect_s
/**** Expected attribute info ****/
65 int optional
, /* Optional attribute? */
66 not_expect
, /* Don't expect attribute? */
67 expect_all
; /* Expect all attributes to match/not match */
68 char *name
, /* Attribute name */
69 *of_type
, /* Type name */
70 *same_count_as
, /* Parallel attribute name */
71 *if_defined
, /* Only required if variable defined */
72 *if_not_defined
, /* Only required if variable is not defined */
73 *with_value
, /* Attribute must include this value */
74 *with_value_from
, /* Attribute must have one of the values in this attribute */
75 *define_match
, /* Variable to define on match */
76 *define_no_match
, /* Variable to define on no-match */
77 *define_value
; /* Variable to define with value */
78 int repeat_limit
, /* Maximum number of times to repeat */
79 repeat_match
, /* Repeat test on match */
80 repeat_no_match
, /* Repeat test on no match */
81 with_flags
, /* WITH flags */
82 count
; /* Expected count if > 0 */
83 ipp_tag_t in_group
; /* IN-GROUP value */
86 typedef struct _cups_status_s
/**** Status info ****/
88 ipp_status_t status
; /* Expected status code */
89 char *if_defined
, /* Only if variable is defined */
90 *if_not_defined
, /* Only if variable is not defined */
91 *define_match
, /* Variable to define on match */
92 *define_no_match
, /* Variable to define on no-match */
93 *define_value
; /* Variable to define with value */
94 int repeat_limit
, /* Maximum number of times to repeat */
95 repeat_match
, /* Repeat the test when it does not match */
96 repeat_no_match
; /* Repeat the test when it matches */
99 typedef struct _cups_testdata_s
/**** Test Data ****/
102 http_encryption_t encryption
; /* Encryption for connection */
103 int family
; /* Address family */
104 _cups_output_t output
; /* Output mode */
105 int stop_after_include_error
;
106 /* Stop after include errors? */
107 double timeout
; /* Timeout for connection */
108 int validate_headers
, /* Validate HTTP headers in response? */
109 verbosity
; /* Show all attributes? */
112 int def_ignore_errors
; /* Default IGNORE-ERRORS value */
113 _cups_transfer_t def_transfer
; /* Default TRANSFER value */
114 int def_version
; /* Default IPP version */
117 http_t
*http
; /* HTTP connection to printer/server */
118 cups_file_t
*outfile
; /* Output file */
119 int show_header
, /* Show the test header? */
120 xml_header
; /* 1 if XML plist header was written */
121 int pass
, /* Have we passed all tests? */
122 test_count
, /* Number of tests (total) */
123 pass_count
, /* Number of tests that passed */
124 fail_count
, /* Number of tests that failed */
125 skip_count
; /* Number of tests that were skipped */
128 cups_array_t
*errors
; /* Errors array */
129 int prev_pass
, /* Result of previous test */
130 skip_previous
; /* Skip on previous test failure? */
131 char compression
[16]; /* COMPRESSION value */
132 useconds_t delay
; /* Initial delay */
133 int num_displayed
; /* Number of displayed attributes */
134 char *displayed
[200]; /* Displayed attributes */
135 int num_expects
; /* Number of expected attributes */
136 _cups_expect_t expects
[200], /* Expected attributes */
137 *expect
, /* Current expected attribute */
138 *last_expect
; /* Last EXPECT (for predicates) */
139 char file
[1024], /* Data filename */
140 file_id
[1024]; /* File identifier */
141 int ignore_errors
; /* Ignore test failures? */
142 char name
[1024]; /* Test name */
143 useconds_t repeat_interval
; /* Repeat interval (delay) */
144 int request_id
; /* Current request ID */
145 char resource
[512]; /* Resource for request */
146 int skip_test
, /* Skip this test? */
147 num_statuses
; /* Number of valid status codes */
148 _cups_status_t statuses
[100], /* Valid status codes */
149 *last_status
; /* Last STATUS (for predicates) */
150 char test_id
[1024]; /* Test identifier */
151 _cups_transfer_t transfer
; /* To chunk or not to chunk */
152 int version
; /* IPP version number to use */
160 static int Cancel
= 0; /* Cancel test? */
167 static void add_stringf(cups_array_t
*a
, const char *s
, ...) _CUPS_FORMAT(2, 3);
168 static int compare_uris(const char *a
, const char *b
);
169 static void copy_hex_string(char *buffer
, unsigned char *data
, int datalen
, size_t bufsize
);
170 static int do_test(_ipp_file_t
*f
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
);
171 static int do_tests(const char *testfile
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
);
172 static int error_cb(_ipp_file_t
*f
, _cups_testdata_t
*data
, const char *error
);
173 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
174 static char *get_filename(const char *testfile
, char *dst
, const char *src
, size_t dstsize
);
175 static const char *get_string(ipp_attribute_t
*attr
, int element
, int flags
, char *buffer
, size_t bufsize
);
176 static void init_data(_cups_testdata_t
*data
);
177 static char *iso_date(const ipp_uchar_t
*date
);
178 static void pause_message(const char *message
);
179 static void print_attr(cups_file_t
*outfile
, _cups_output_t output
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
180 static void print_csv(_cups_testdata_t
*data
, ipp_t
*ipp
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
181 static void print_fatal_error(_cups_testdata_t
*data
, const char *s
, ...) _CUPS_FORMAT(2, 3);
182 static void print_ippserver_attr(_cups_testdata_t
*data
, ipp_attribute_t
*attr
, int indent
);
183 static void print_ippserver_string(_cups_testdata_t
*data
, const char *s
, size_t len
);
184 static void print_line(_cups_testdata_t
*data
, ipp_t
*ipp
, ipp_attribute_t
*attr
, int num_displayed
, char **displayed
, size_t *widths
);
185 static void print_xml_header(_cups_testdata_t
*data
);
186 static void print_xml_string(cups_file_t
*outfile
, const char *element
, const char *s
);
187 static void print_xml_trailer(_cups_testdata_t
*data
, int success
, const char *message
);
189 static void sigterm_handler(int sig
);
191 static int timeout_cb(http_t
*http
, void *user_data
);
192 static int token_cb(_ipp_file_t
*f
, _ipp_vars_t
*vars
, _cups_testdata_t
*data
, const char *token
);
193 static void usage(void) _CUPS_NORETURN
;
194 static const char *with_flags_string(int flags
);
195 static int with_value(_cups_testdata_t
*data
, cups_array_t
*errors
, char *value
, int flags
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
196 static int with_value_from(cups_array_t
*errors
, ipp_attribute_t
*fromattr
, ipp_attribute_t
*attr
, char *matchbuf
, size_t matchlen
);
200 * 'main()' - Parse options and do tests.
203 int /* O - Exit status */
204 main(int argc
, /* I - Number of command-line args */
205 char *argv
[]) /* I - Command-line arguments */
207 int i
; /* Looping var */
208 int status
; /* Status of tests... */
209 char *opt
, /* Current option */
210 name
[1024], /* Name/value buffer */
211 *value
, /* Pointer to value */
212 filename
[1024], /* Real filename */
213 testname
[1024]; /* Real test filename */
214 const char *ext
, /* Extension on filename */
215 *testfile
; /* Test file to use */
216 int interval
, /* Test interval in microseconds */
217 repeat
; /* Repeat count */
218 _cups_testdata_t data
; /* Test data */
219 _ipp_vars_t vars
; /* Variables */
220 _cups_globals_t
*cg
= _cupsGlobals();
226 * Catch SIGINT and SIGTERM...
229 signal(SIGINT
, sigterm_handler
);
230 signal(SIGTERM
, sigterm_handler
);
234 * Initialize the locale and variables...
237 _cupsSetLocale(argv
);
241 _ippVarsInit(&vars
, NULL
, (_ipp_ferror_cb_t
)error_cb
, (_ipp_ftoken_cb_t
)token_cb
);
243 _ippVarsSet(&vars
, "date-start", iso_date(ippTimeToDate(time(NULL
))));
248 * ipptool URI testfile
256 for (i
= 1; i
< argc
; i
++)
258 if (!strcmp(argv
[i
], "--help"))
262 else if (!strcmp(argv
[i
], "--ippserver"))
268 _cupsLangPuts(stderr
, _("ipptool: Missing filename for \"--ippserver\"."));
272 if (data
.outfile
!= cupsFileStdout())
275 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
277 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
281 data
.output
= _CUPS_OUTPUT_IPPSERVER
;
283 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
285 data
.stop_after_include_error
= 1;
287 else if (!strcmp(argv
[i
], "--version"))
292 else if (argv
[i
][0] == '-')
294 for (opt
= argv
[i
] + 1; *opt
; opt
++)
298 case '4' : /* Connect using IPv4 only */
299 data
.family
= AF_INET
;
303 case '6' : /* Connect using IPv6 only */
304 data
.family
= AF_INET6
;
306 #endif /* AF_INET6 */
308 case 'C' : /* Enable HTTP chunking */
309 data
.def_transfer
= _CUPS_TRANSFER_CHUNKED
;
312 case 'E' : /* Encrypt with TLS */
314 data
.encryption
= HTTP_ENCRYPT_REQUIRED
;
316 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
318 #endif /* HAVE_SSL */
321 case 'I' : /* Ignore errors */
322 data
.def_ignore_errors
= 1;
325 case 'L' : /* Disable HTTP chunking */
326 data
.def_transfer
= _CUPS_TRANSFER_LENGTH
;
329 case 'P' : /* Output to plist file */
334 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
338 if (data
.outfile
!= cupsFileStdout())
341 if ((data
.outfile
= cupsFileOpen(argv
[i
], "w")) == NULL
)
343 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
347 data
.output
= _CUPS_OUTPUT_PLIST
;
349 if (interval
|| repeat
)
351 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
356 case 'S' : /* Encrypt with SSL */
358 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
360 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
362 #endif /* HAVE_SSL */
365 case 'T' : /* Set timeout */
370 _cupsLangPrintf(stderr
,
371 _("%s: Missing timeout for \"-T\"."),
376 data
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
379 case 'V' : /* Set IPP version */
384 _cupsLangPrintf(stderr
,
385 _("%s: Missing version for \"-V\"."),
390 if (!strcmp(argv
[i
], "1.0"))
392 data
.def_version
= 10;
394 else if (!strcmp(argv
[i
], "1.1"))
396 data
.def_version
= 11;
398 else if (!strcmp(argv
[i
], "2.0"))
400 data
.def_version
= 20;
402 else if (!strcmp(argv
[i
], "2.1"))
404 data
.def_version
= 21;
406 else if (!strcmp(argv
[i
], "2.2"))
408 data
.def_version
= 22;
412 _cupsLangPrintf(stderr
, _("%s: Bad version %s for \"-V\"."), "ipptool", argv
[i
]);
417 case 'X' : /* Produce XML output */
418 data
.output
= _CUPS_OUTPUT_PLIST
;
420 if (interval
|| repeat
)
422 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
427 case 'c' : /* CSV output */
428 data
.output
= _CUPS_OUTPUT_CSV
;
431 case 'd' : /* Define a variable */
436 _cupsLangPuts(stderr
,
437 _("ipptool: Missing name=value for \"-d\"."));
441 strlcpy(name
, argv
[i
], sizeof(name
));
442 if ((value
= strchr(name
, '=')) != NULL
)
445 value
= name
+ strlen(name
);
447 _ippVarsSet(&vars
, name
, value
);
450 case 'f' : /* Set the default test filename */
455 _cupsLangPuts(stderr
,
456 _("ipptool: Missing filename for \"-f\"."));
460 if (access(argv
[i
], 0))
466 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
467 if (access(filename
, 0) && filename
[0] != '/'
469 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
473 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
474 if (access(filename
, 0))
476 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz", cg
->cups_datadir
, argv
[i
]);
477 if (access(filename
, 0))
478 strlcpy(filename
, argv
[i
], sizeof(filename
));
483 strlcpy(filename
, argv
[i
], sizeof(filename
));
485 _ippVarsSet(&vars
, "filename", filename
);
487 if ((ext
= strrchr(filename
, '.')) != NULL
)
490 * Guess the MIME media type based on the extension...
493 if (!_cups_strcasecmp(ext
, ".gif"))
494 _ippVarsSet(&vars
, "filetype", "image/gif");
495 else if (!_cups_strcasecmp(ext
, ".htm") ||
496 !_cups_strcasecmp(ext
, ".htm.gz") ||
497 !_cups_strcasecmp(ext
, ".html") ||
498 !_cups_strcasecmp(ext
, ".html.gz"))
499 _ippVarsSet(&vars
, "filetype", "text/html");
500 else if (!_cups_strcasecmp(ext
, ".jpg") ||
501 !_cups_strcasecmp(ext
, ".jpeg"))
502 _ippVarsSet(&vars
, "filetype", "image/jpeg");
503 else if (!_cups_strcasecmp(ext
, ".pcl") ||
504 !_cups_strcasecmp(ext
, ".pcl.gz"))
505 _ippVarsSet(&vars
, "filetype", "application/vnd.hp-PCL");
506 else if (!_cups_strcasecmp(ext
, ".pdf"))
507 _ippVarsSet(&vars
, "filetype", "application/pdf");
508 else if (!_cups_strcasecmp(ext
, ".png"))
509 _ippVarsSet(&vars
, "filetype", "image/png");
510 else if (!_cups_strcasecmp(ext
, ".ps") ||
511 !_cups_strcasecmp(ext
, ".ps.gz"))
512 _ippVarsSet(&vars
, "filetype", "application/postscript");
513 else if (!_cups_strcasecmp(ext
, ".pwg") ||
514 !_cups_strcasecmp(ext
, ".pwg.gz") ||
515 !_cups_strcasecmp(ext
, ".ras") ||
516 !_cups_strcasecmp(ext
, ".ras.gz"))
517 _ippVarsSet(&vars
, "filetype", "image/pwg-raster");
518 else if (!_cups_strcasecmp(ext
, ".tif") ||
519 !_cups_strcasecmp(ext
, ".tiff"))
520 _ippVarsSet(&vars
, "filetype", "image/tiff");
521 else if (!_cups_strcasecmp(ext
, ".txt") ||
522 !_cups_strcasecmp(ext
, ".txt.gz"))
523 _ippVarsSet(&vars
, "filetype", "text/plain");
524 else if (!_cups_strcasecmp(ext
, ".urf") ||
525 !_cups_strcasecmp(ext
, ".urf.gz"))
526 _ippVarsSet(&vars
, "filetype", "image/urf");
527 else if (!_cups_strcasecmp(ext
, ".xps"))
528 _ippVarsSet(&vars
, "filetype", "application/openxps");
530 _ippVarsSet(&vars
, "filetype", "application/octet-stream");
535 * Use the "auto-type" MIME media type...
538 _ippVarsSet(&vars
, "filetype", "application/octet-stream");
542 case 'h' : /* Validate response headers */
543 data
.validate_headers
= 1;
546 case 'i' : /* Test every N seconds */
551 _cupsLangPuts(stderr
, _("ipptool: Missing seconds for \"-i\"."));
556 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) * 1000000.0);
559 _cupsLangPuts(stderr
, _("ipptool: Invalid seconds for \"-i\"."));
564 if ((data
.output
== _CUPS_OUTPUT_PLIST
|| data
.output
== _CUPS_OUTPUT_IPPSERVER
) && interval
)
566 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
571 case 'l' : /* List as a table */
572 data
.output
= _CUPS_OUTPUT_LIST
;
575 case 'n' : /* Repeat count */
580 _cupsLangPuts(stderr
, _("ipptool: Missing count for \"-n\"."));
584 repeat
= atoi(argv
[i
]);
586 if ((data
.output
== _CUPS_OUTPUT_PLIST
|| data
.output
== _CUPS_OUTPUT_IPPSERVER
) && repeat
)
588 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"--ippserver\", \"-P\", and \"-X\"."));
593 case 'q' : /* Be quiet */
594 data
.output
= _CUPS_OUTPUT_QUIET
;
597 case 't' : /* CUPS test output */
598 data
.output
= _CUPS_OUTPUT_TEST
;
601 case 'v' : /* Be verbose */
606 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), "ipptool", *opt
);
611 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
613 || !strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8)
614 #endif /* HAVE_SSL */
623 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
628 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
629 data
.encryption
= HTTP_ENCRYPT_ALWAYS
;
630 #endif /* HAVE_SSL */
632 if (!_ippVarsSet(&vars
, "uri", argv
[i
]))
634 _cupsLangPrintf(stderr
, _("ipptool: Bad URI \"%s\"."), argv
[i
]);
638 if (vars
.username
[0] && vars
.password
)
639 cupsSetPasswordCB2(_ippVarsPasswordCB
, &vars
);
649 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
650 _cupsLangPuts(stderr
, argv
[i
]);
654 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
656 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
660 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
, argv
[i
]);
661 if (access(testname
, 0))
669 if (!do_tests(testfile
, &vars
, &data
))
674 if (!vars
.uri
|| !testfile
)
678 * Loop if the interval is set...
681 if (data
.output
== _CUPS_OUTPUT_PLIST
)
682 print_xml_trailer(&data
, !status
, NULL
);
683 else if (interval
> 0 && repeat
> 0)
687 usleep((useconds_t
)interval
);
688 do_tests(testfile
, &vars
, &data
);
692 else if (interval
> 0)
696 usleep((useconds_t
)interval
);
697 do_tests(testfile
, &vars
, &data
);
701 if ((data
.output
== _CUPS_OUTPUT_TEST
|| (data
.output
== _CUPS_OUTPUT_PLIST
&& data
.outfile
)) && data
.test_count
> 1)
704 * Show a summary report if there were multiple tests...
707 cupsFilePrintf(cupsFileStdout(), "\nSummary: %d tests, %d passed, %d failed, %d skipped\nScore: %d%%\n", data
.test_count
, data
.pass_count
, data
.fail_count
, data
.skip_count
, 100 * (data
.pass_count
+ data
.skip_count
) / data
.test_count
);
710 cupsFileClose(data
.outfile
);
721 * 'add_stringf()' - Add a formatted string to an array.
725 add_stringf(cups_array_t
*a
, /* I - Array */
726 const char *s
, /* I - Printf-style format string */
727 ...) /* I - Additional args as needed */
729 char buffer
[10240]; /* Format buffer */
730 va_list ap
; /* Argument pointer */
734 * Don't bother is the array is NULL...
741 * Format the message...
745 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
749 * Add it to the array...
752 cupsArrayAdd(a
, buffer
);
757 * 'compare_uris()' - Compare two URIs...
760 static int /* O - Result of comparison */
761 compare_uris(const char *a
, /* I - First URI */
762 const char *b
) /* I - Second URI */
764 char ascheme
[32], /* Components of first URI */
769 char bscheme
[32], /* Components of second URI */
774 char *ptr
; /* Pointer into string */
775 int result
; /* Result of comparison */
779 * Separate the URIs into their components...
782 if (httpSeparateURI(HTTP_URI_CODING_ALL
, a
, ascheme
, sizeof(ascheme
), auserpass
, sizeof(auserpass
), ahost
, sizeof(ahost
), &aport
, aresource
, sizeof(aresource
)) < HTTP_URI_STATUS_OK
)
785 if (httpSeparateURI(HTTP_URI_CODING_ALL
, b
, bscheme
, sizeof(bscheme
), buserpass
, sizeof(buserpass
), bhost
, sizeof(bhost
), &bport
, bresource
, sizeof(bresource
)) < HTTP_URI_STATUS_OK
)
789 * Strip trailing dots from the host components, if present...
792 if ((ptr
= ahost
+ strlen(ahost
) - 1) > ahost
&& *ptr
== '.')
795 if ((ptr
= bhost
+ strlen(bhost
) - 1) > bhost
&& *ptr
== '.')
799 * Compare each component...
802 if ((result
= _cups_strcasecmp(ascheme
, bscheme
)) != 0)
805 if ((result
= strcmp(auserpass
, buserpass
)) != 0)
808 if ((result
= _cups_strcasecmp(ahost
, bhost
)) != 0)
812 return (aport
- bport
);
814 if (!_cups_strcasecmp(ascheme
, "mailto") || !_cups_strcasecmp(ascheme
, "urn"))
815 return (_cups_strcasecmp(aresource
, bresource
));
817 return (strcmp(aresource
, bresource
));
822 * 'copy_hex_string()' - Copy an octetString to a C string and encode as hex if
827 copy_hex_string(char *buffer
, /* I - String buffer */
828 unsigned char *data
, /* I - octetString data */
829 int datalen
, /* I - octetString length */
830 size_t bufsize
) /* I - Size of string buffer */
832 char *bufptr
, /* Pointer into string buffer */
833 *bufend
= buffer
+ bufsize
- 2;
834 /* End of string buffer */
835 unsigned char *dataptr
, /* Pointer into octetString data */
836 *dataend
= data
+ datalen
;
837 /* End of octetString data */
838 static const char *hexdigits
= "0123456789ABCDEF";
843 * First see if there are any non-ASCII bytes in the octetString...
846 for (dataptr
= data
; dataptr
< dataend
; dataptr
++)
847 if (*dataptr
< 0x20 || *dataptr
>= 0x7f)
850 if (dataptr
< dataend
)
853 * Yes, encode as hex...
858 for (bufptr
= buffer
+ 1, dataptr
= data
; bufptr
< bufend
&& dataptr
< dataend
; dataptr
++)
860 *bufptr
++ = hexdigits
[*dataptr
>> 4];
861 *bufptr
++ = hexdigits
[*dataptr
& 15];
872 * No, copy as a string...
875 if ((size_t)datalen
> bufsize
)
876 datalen
= (int)bufsize
- 1;
878 memcpy(buffer
, data
, (size_t)datalen
);
879 buffer
[datalen
] = '\0';
885 * 'do_test()' - Do a single test from the test file.
888 static int /* O - 1 on success, 0 on failure */
889 do_test(_ipp_file_t
*f
, /* I - IPP data file */
890 _ipp_vars_t
*vars
, /* I - IPP variables */
891 _cups_testdata_t
*data
) /* I - Test data */
894 int i
, /* Looping var */
895 status_ok
, /* Did we get a matching status? */
896 repeat_count
= 0, /* Repeat count */
897 repeat_test
; /* Repeat the test? */
898 _cups_expect_t
*expect
; /* Current expected attribute */
899 ipp_t
*request
, /* IPP request */
900 *response
; /* IPP response */
901 size_t length
; /* Length of IPP request */
902 http_status_t status
; /* HTTP status */
903 cups_array_t
*a
; /* Duplicate attribute array */
904 ipp_tag_t group
; /* Current group */
905 ipp_attribute_t
*attrptr
, /* Attribute pointer */
906 *found
; /* Found attribute */
907 char temp
[1024]; /* Temporary string */
908 cups_file_t
*reqfile
; /* File to send */
909 ssize_t bytes
; /* Bytes read/written */
910 char buffer
[131072]; /* Copy buffer */
911 size_t widths
[200]; /* Width of columns */
912 const char *error
; /* Current error */
919 * Take over control of the attributes in the request...
926 * Submit the IPP request...
931 ippSetVersion(request
, data
->version
/ 10, data
->version
% 10);
932 ippSetRequestId(request
, data
->request_id
);
934 if (data
->output
== _CUPS_OUTPUT_PLIST
)
936 cupsFilePuts(data
->outfile
, "<dict>\n");
937 cupsFilePuts(data
->outfile
, "<key>Name</key>\n");
938 print_xml_string(data
->outfile
, "string", data
->name
);
939 if (data
->file_id
[0])
941 cupsFilePuts(data
->outfile
, "<key>FileId</key>\n");
942 print_xml_string(data
->outfile
, "string", data
->file_id
);
944 if (data
->test_id
[0])
946 cupsFilePuts(data
->outfile
, "<key>TestId</key>\n");
947 print_xml_string(data
->outfile
, "string", data
->test_id
);
949 cupsFilePuts(data
->outfile
, "<key>Version</key>\n");
950 cupsFilePrintf(data
->outfile
, "<string>%d.%d</string>\n", data
->version
/ 10, data
->version
% 10);
951 cupsFilePuts(data
->outfile
, "<key>Operation</key>\n");
952 print_xml_string(data
->outfile
, "string", ippOpString(ippGetOperation(request
)));
953 cupsFilePuts(data
->outfile
, "<key>RequestId</key>\n");
954 cupsFilePrintf(data
->outfile
, "<integer>%d</integer>\n", data
->request_id
);
955 cupsFilePuts(data
->outfile
, "<key>RequestAttributes</key>\n");
956 cupsFilePuts(data
->outfile
, "<array>\n");
957 if (ippFirstAttribute(request
))
959 cupsFilePuts(data
->outfile
, "<dict>\n");
960 for (attrptr
= ippFirstAttribute(request
), group
= ippGetGroupTag(attrptr
); attrptr
; attrptr
= ippNextAttribute(request
))
961 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
962 cupsFilePuts(data
->outfile
, "</dict>\n");
964 cupsFilePuts(data
->outfile
, "</array>\n");
967 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
971 cupsFilePrintf(cupsFileStdout(), " %s:\n", ippOpString(ippGetOperation(request
)));
973 for (attrptr
= ippFirstAttribute(request
); attrptr
; attrptr
= ippNextAttribute(request
))
974 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
977 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
980 if ((data
->skip_previous
&& !data
->prev_pass
) || data
->skip_test
)
988 if (data
->output
== _CUPS_OUTPUT_PLIST
)
990 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
991 cupsFilePuts(data
->outfile
, "<true />\n");
992 cupsFilePuts(data
->outfile
, "<key>Skipped</key>\n");
993 cupsFilePuts(data
->outfile
, "<true />\n");
994 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
995 print_xml_string(data
->outfile
, "string", "skip");
996 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
997 cupsFilePuts(data
->outfile
, "<dict />\n");
1000 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1001 cupsFilePuts(cupsFileStdout(), "SKIP]\n");
1006 vars
->password_tries
= 0;
1010 if (data
->delay
> 0)
1011 usleep(data
->delay
);
1013 data
->delay
= data
->repeat_interval
;
1016 status
= HTTP_STATUS_OK
;
1018 if (data
->transfer
== _CUPS_TRANSFER_CHUNKED
|| (data
->transfer
== _CUPS_TRANSFER_AUTO
&& data
->file
[0]))
1021 * Send request using chunking - a 0 length means "chunk".
1029 * Send request using content length...
1032 length
= ippLength(request
);
1034 if (data
->file
[0] && (reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1037 * Read the file to get the uncompressed file size...
1040 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1041 length
+= (size_t)bytes
;
1043 cupsFileClose(reqfile
);
1048 * Send the request...
1051 data
->prev_pass
= 1;
1055 if (status
!= HTTP_STATUS_ERROR
)
1057 while (!response
&& !Cancel
&& data
->prev_pass
)
1059 status
= cupsSendRequest(data
->http
, request
, data
->resource
, length
);
1062 if (data
->compression
[0])
1063 httpSetField(data
->http
, HTTP_FIELD_CONTENT_ENCODING
, data
->compression
);
1064 #endif /* HAVE_LIBZ */
1066 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&& ippGetState(request
) == IPP_DATA
&& data
->file
[0])
1068 if ((reqfile
= cupsFileOpen(data
->file
, "r")) != NULL
)
1070 while (!Cancel
&& (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
1072 if ((status
= cupsWriteRequestData(data
->http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
1076 cupsFileClose(reqfile
);
1080 snprintf(buffer
, sizeof(buffer
), "%s: %s", data
->file
, strerror(errno
));
1081 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
1083 status
= HTTP_STATUS_ERROR
;
1088 * Get the server's response...
1091 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
1093 response
= cupsGetResponse(data
->http
, data
->resource
);
1094 status
= httpGetStatus(data
->http
);
1097 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1099 httpError(data
->http
) != WSAETIMEDOUT
)
1101 httpError(data
->http
) != ETIMEDOUT
)
1104 if (httpReconnect2(data
->http
, 30000, NULL
))
1105 data
->prev_pass
= 0;
1107 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
1109 data
->prev_pass
= 0;
1112 else if (status
!= HTTP_STATUS_OK
)
1114 httpFlush(data
->http
);
1116 if (status
== HTTP_STATUS_UNAUTHORIZED
)
1124 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& httpError(data
->http
) != EINVAL
&&
1126 httpError(data
->http
) != WSAETIMEDOUT
)
1128 httpError(data
->http
) != ETIMEDOUT
)
1131 if (httpReconnect2(data
->http
, 30000, NULL
))
1132 data
->prev_pass
= 0;
1134 else if (status
== HTTP_STATUS_ERROR
)
1137 httpReconnect2(data
->http
, 30000, NULL
);
1139 data
->prev_pass
= 0;
1141 else if (status
!= HTTP_STATUS_OK
)
1143 httpFlush(data
->http
);
1144 data
->prev_pass
= 0;
1148 * Check results of request...
1151 cupsArrayClear(data
->errors
);
1153 if (httpGetVersion(data
->http
) != HTTP_1_1
)
1155 int version
= (int)httpGetVersion(data
->http
);
1157 add_stringf(data
->errors
, "Bad HTTP version (%d.%d)", version
/ 100, version
% 100);
1160 if (data
->validate_headers
)
1162 const char *header
; /* HTTP header value */
1164 if ((header
= httpGetField(data
->http
, HTTP_FIELD_CONTENT_TYPE
)) == NULL
|| _cups_strcasecmp(header
, "application/ipp"))
1165 add_stringf(data
->errors
, "Bad HTTP Content-Type in response (%s)", header
&& *header
? header
: "<missing>");
1167 if ((header
= httpGetField(data
->http
, HTTP_FIELD_DATE
)) != NULL
&& *header
&& httpGetDateTime(header
) == 0)
1168 add_stringf(data
->errors
, "Bad HTTP Date in response (%s)", header
);
1174 * No response, log error...
1177 add_stringf(data
->errors
, "IPP request failed with status %s (%s)", ippErrorString(cupsLastError()), cupsLastErrorString());
1182 * Collect common attribute values...
1185 if ((attrptr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
1187 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1188 _ippVarsSet(vars
, "job-id", temp
);
1191 if ((attrptr
= ippFindAttribute(response
, "job-uri", IPP_TAG_URI
)) != NULL
)
1192 _ippVarsSet(vars
, "job-uri", ippGetString(attrptr
, 0, NULL
));
1194 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id", IPP_TAG_INTEGER
)) != NULL
)
1196 snprintf(temp
, sizeof(temp
), "%d", ippGetInteger(attrptr
, 0));
1197 _ippVarsSet(vars
, "notify-subscription-id", temp
);
1201 * Check response, validating groups and attributes and logging errors
1205 if (ippGetState(response
) != IPP_DATA
)
1206 add_stringf(data
->errors
, "Missing end-of-attributes-tag in response (RFC 2910 section 3.5.1)");
1210 int major
, minor
; /* IPP version */
1212 major
= ippGetVersion(response
, &minor
);
1214 if (major
!= (data
->version
/ 10) || minor
!= (data
->version
% 10))
1215 add_stringf(data
->errors
, "Bad version %d.%d in response - expected %d.%d (RFC 2911 section 3.1.8).", major
, minor
, data
->version
/ 10, data
->version
% 10);
1218 if (ippGetRequestId(response
) != data
->request_id
)
1219 add_stringf(data
->errors
, "Bad request ID %d in response - expected %d (RFC 2911 section 3.1.1)", ippGetRequestId(response
), data
->request_id
);
1221 attrptr
= ippFirstAttribute(response
);
1224 add_stringf(data
->errors
, "Missing first attribute \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1228 if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_CHARSET
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 ||strcmp(ippGetName(attrptr
), "attributes-charset"))
1229 add_stringf(data
->errors
, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-charset (charset)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).", ippGetName(attrptr
) ? ippGetName(attrptr
) : "(null)", ippGetCount(attrptr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr
)), ippTagString(ippGetGroupTag(attrptr
)));
1231 attrptr
= ippNextAttribute(response
);
1233 add_stringf(data
->errors
, "Missing second attribute \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).");
1234 else if (!ippGetName(attrptr
) || ippGetValueTag(attrptr
) != IPP_TAG_LANGUAGE
|| ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
|| ippGetCount(attrptr
) != 1 || strcmp(ippGetName(attrptr
), "attributes-natural-language"))
1235 add_stringf(data
->errors
, "Bad first attribute \"%s (%s%s)\" in group %s, expected \"attributes-natural-language (naturalLanguage)\" in group operation-attributes-tag (RFC 2911 section 3.1.4).", ippGetName(attrptr
) ? ippGetName(attrptr
) : "(null)", ippGetCount(attrptr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attrptr
)), ippTagString(ippGetGroupTag(attrptr
)));
1238 if ((attrptr
= ippFindAttribute(response
, "status-message", IPP_TAG_ZERO
)) != NULL
)
1240 const char *status_message
= ippGetString(attrptr
, 0, NULL
);
1243 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1244 add_stringf(data
->errors
, "status-message (text(255)) has wrong value tag %s (RFC 2911 section 3.1.6.2).", ippTagString(ippGetValueTag(attrptr
)));
1245 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1246 add_stringf(data
->errors
, "status-message (text(255)) has wrong group tag %s (RFC 2911 section 3.1.6.2).", ippTagString(ippGetGroupTag(attrptr
)));
1247 if (ippGetCount(attrptr
) != 1)
1248 add_stringf(data
->errors
, "status-message (text(255)) has %d values (RFC 2911 section 3.1.6.2).", ippGetCount(attrptr
));
1249 if (status_message
&& strlen(status_message
) > 255)
1250 add_stringf(data
->errors
, "status-message (text(255)) has bad length %d (RFC 2911 section 3.1.6.2).", (int)strlen(status_message
));
1253 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
1254 IPP_TAG_ZERO
)) != NULL
)
1256 const char *detailed_status_message
= ippGetString(attrptr
, 0, NULL
);
1259 if (ippGetValueTag(attrptr
) != IPP_TAG_TEXT
)
1260 add_stringf(data
->errors
,
1261 "detailed-status-message (text(MAX)) has wrong "
1262 "value tag %s (RFC 2911 section 3.1.6.3).",
1263 ippTagString(ippGetValueTag(attrptr
)));
1264 if (ippGetGroupTag(attrptr
) != IPP_TAG_OPERATION
)
1265 add_stringf(data
->errors
,
1266 "detailed-status-message (text(MAX)) has wrong "
1267 "group tag %s (RFC 2911 section 3.1.6.3).",
1268 ippTagString(ippGetGroupTag(attrptr
)));
1269 if (ippGetCount(attrptr
) != 1)
1270 add_stringf(data
->errors
,
1271 "detailed-status-message (text(MAX)) has %d values"
1272 " (RFC 2911 section 3.1.6.3).",
1273 ippGetCount(attrptr
));
1274 if (detailed_status_message
&& strlen(detailed_status_message
) > 1023)
1275 add_stringf(data
->errors
,
1276 "detailed-status-message (text(MAX)) has bad "
1277 "length %d (RFC 2911 section 3.1.6.3).",
1278 (int)strlen(detailed_status_message
));
1281 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1283 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1285 attrptr
= ippNextAttribute(response
))
1287 if (ippGetGroupTag(attrptr
) != group
)
1289 int out_of_order
= 0; /* Are attribute groups out-of-order? */
1292 switch (ippGetGroupTag(attrptr
))
1297 case IPP_TAG_OPERATION
:
1301 case IPP_TAG_UNSUPPORTED_GROUP
:
1302 if (group
!= IPP_TAG_OPERATION
)
1307 case IPP_TAG_PRINTER
:
1308 if (group
!= IPP_TAG_OPERATION
&& group
!= IPP_TAG_UNSUPPORTED_GROUP
)
1312 case IPP_TAG_SUBSCRIPTION
:
1313 if (group
> ippGetGroupTag(attrptr
) && group
!= IPP_TAG_DOCUMENT
)
1318 if (group
> ippGetGroupTag(attrptr
))
1324 add_stringf(data
->errors
, "Attribute groups out of order (%s < %s)",
1325 ippTagString(ippGetGroupTag(attrptr
)),
1326 ippTagString(group
));
1328 if (ippGetGroupTag(attrptr
) != IPP_TAG_ZERO
)
1329 group
= ippGetGroupTag(attrptr
);
1332 if (!ippValidateAttribute(attrptr
))
1333 cupsArrayAdd(data
->errors
, (void *)cupsLastErrorString());
1335 if (ippGetName(attrptr
))
1337 if (cupsArrayFind(a
, (void *)ippGetName(attrptr
)) && data
->output
< _CUPS_OUTPUT_LIST
)
1338 add_stringf(data
->errors
, "Duplicate \"%s\" attribute in %s group",
1339 ippGetName(attrptr
), ippTagString(group
));
1341 cupsArrayAdd(a
, (void *)ippGetName(attrptr
));
1348 * Now check the test-defined expected status-code and attribute
1352 for (i
= 0, status_ok
= 0; i
< data
->num_statuses
; i
++)
1354 if (data
->statuses
[i
].if_defined
&&
1355 !_ippVarsGet(vars
, data
->statuses
[i
].if_defined
))
1358 if (data
->statuses
[i
].if_not_defined
&&
1359 _ippVarsGet(vars
, data
->statuses
[i
].if_not_defined
))
1362 if (ippGetStatusCode(response
) == data
->statuses
[i
].status
)
1366 if (data
->statuses
[i
].repeat_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1369 if (data
->statuses
[i
].define_match
)
1370 _ippVarsSet(vars
, data
->statuses
[i
].define_match
, "1");
1374 if (data
->statuses
[i
].repeat_no_match
&& repeat_count
< data
->statuses
[i
].repeat_limit
)
1377 if (data
->statuses
[i
].define_no_match
)
1379 _ippVarsSet(vars
, data
->statuses
[i
].define_no_match
, "1");
1385 if (!status_ok
&& data
->num_statuses
> 0)
1387 for (i
= 0; i
< data
->num_statuses
; i
++)
1389 if (data
->statuses
[i
].if_defined
&&
1390 !_ippVarsGet(vars
, data
->statuses
[i
].if_defined
))
1393 if (data
->statuses
[i
].if_not_defined
&&
1394 _ippVarsGet(vars
, data
->statuses
[i
].if_not_defined
))
1397 if (!data
->statuses
[i
].repeat_match
|| repeat_count
>= data
->statuses
[i
].repeat_limit
)
1398 add_stringf(data
->errors
, "EXPECTED: STATUS %s (got %s)",
1399 ippErrorString(data
->statuses
[i
].status
),
1400 ippErrorString(cupsLastError()));
1403 if ((attrptr
= ippFindAttribute(response
, "status-message",
1404 IPP_TAG_TEXT
)) != NULL
)
1405 add_stringf(data
->errors
, "status-message=\"%s\"", ippGetString(attrptr
, 0, NULL
));
1408 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
1410 ipp_attribute_t
*group_found
; /* Found parent attribute for group tests */
1412 if (expect
->if_defined
&& !_ippVarsGet(vars
, expect
->if_defined
))
1415 if (expect
->if_not_defined
&&
1416 _ippVarsGet(vars
, expect
->if_not_defined
))
1419 if ((found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
&& expect
->in_group
&& expect
->in_group
!= ippGetGroupTag(found
))
1421 while ((found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
)
1422 if (expect
->in_group
== ippGetGroupTag(found
))
1428 group_found
= found
;
1430 if (expect
->in_group
&& strchr(expect
->name
, '/'))
1432 char group_name
[256],/* Parent attribute name */
1433 *group_ptr
; /* Pointer into parent attribute name */
1435 strlcpy(group_name
, expect
->name
, sizeof(group_name
));
1436 if ((group_ptr
= strchr(group_name
, '/')) != NULL
)
1439 group_found
= ippFindAttribute(response
, group_name
, IPP_TAG_ZERO
);
1442 if ((found
&& expect
->not_expect
) ||
1443 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
1444 (found
&& !expect_matches(expect
, ippGetValueTag(found
))) ||
1445 (group_found
&& expect
->in_group
&& ippGetGroupTag(group_found
) != expect
->in_group
))
1447 if (expect
->define_no_match
)
1448 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1449 else if (!expect
->define_match
&& !expect
->define_value
)
1451 if (found
&& expect
->not_expect
)
1452 add_stringf(data
->errors
, "NOT EXPECTED: %s", expect
->name
);
1453 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
1454 add_stringf(data
->errors
, "EXPECTED: %s", expect
->name
);
1457 if (!expect_matches(expect
, ippGetValueTag(found
)))
1458 add_stringf(data
->errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
1459 expect
->name
, expect
->of_type
,
1460 ippTagString(ippGetValueTag(found
)));
1462 if (expect
->in_group
&& ippGetGroupTag(group_found
) != expect
->in_group
)
1463 add_stringf(data
->errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
1464 expect
->name
, ippTagString(expect
->in_group
),
1465 ippTagString(ippGetGroupTag(group_found
)));
1469 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1475 ippAttributeString(found
, buffer
, sizeof(buffer
));
1477 if (found
&& expect
->with_value_from
&& !with_value_from(NULL
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
)))
1479 if (expect
->define_no_match
)
1480 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1481 else if (!expect
->define_match
&& !expect
->define_value
&& ((!expect
->repeat_match
&& !expect
->repeat_no_match
) || repeat_count
>= expect
->repeat_limit
))
1483 add_stringf(data
->errors
, "EXPECTED: %s WITH-VALUES-FROM %s", expect
->name
, expect
->with_value_from
);
1485 with_value_from(data
->errors
, ippFindAttribute(response
, expect
->with_value_from
, IPP_TAG_ZERO
), found
, buffer
, sizeof(buffer
));
1488 if (expect
->repeat_no_match
&& repeat_count
< expect
->repeat_limit
)
1493 else if (found
&& !with_value(data
, NULL
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
)))
1495 if (expect
->define_no_match
)
1496 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1497 else if (!expect
->define_match
&& !expect
->define_value
&&
1498 !expect
->repeat_match
&& (!expect
->repeat_no_match
|| repeat_count
>= expect
->repeat_limit
))
1500 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
1501 add_stringf(data
->errors
, "EXPECTED: %s %s /%s/", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1503 add_stringf(data
->errors
, "EXPECTED: %s %s \"%s\"", expect
->name
, with_flags_string(expect
->with_flags
), expect
->with_value
);
1505 with_value(data
, data
->errors
, expect
->with_value
, expect
->with_flags
, found
, buffer
, sizeof(buffer
));
1508 if (expect
->repeat_no_match
&&
1509 repeat_count
< expect
->repeat_limit
)
1515 if (found
&& expect
->count
> 0 && ippGetCount(found
) != expect
->count
)
1517 if (expect
->define_no_match
)
1518 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1519 else if (!expect
->define_match
&& !expect
->define_value
)
1521 add_stringf(data
->errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
1522 expect
->count
, ippGetCount(found
));
1525 if (expect
->repeat_no_match
&&
1526 repeat_count
< expect
->repeat_limit
)
1532 if (found
&& expect
->same_count_as
)
1534 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
1537 if (!attrptr
|| ippGetCount(attrptr
) != ippGetCount(found
))
1539 if (expect
->define_no_match
)
1540 _ippVarsSet(vars
, expect
->define_no_match
, "1");
1541 else if (!expect
->define_match
&& !expect
->define_value
)
1544 add_stringf(data
->errors
,
1545 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1546 "(not returned)", expect
->name
,
1547 ippGetCount(found
), expect
->same_count_as
);
1548 else if (ippGetCount(attrptr
) != ippGetCount(found
))
1549 add_stringf(data
->errors
,
1550 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
1551 "(%d values)", expect
->name
, ippGetCount(found
),
1552 expect
->same_count_as
, ippGetCount(attrptr
));
1555 if (expect
->repeat_no_match
&&
1556 repeat_count
< expect
->repeat_limit
)
1563 if (found
&& expect
->define_match
)
1564 _ippVarsSet(vars
, expect
->define_match
, "1");
1566 if (found
&& expect
->define_value
)
1568 if (!expect
->with_value
)
1570 int last
= ippGetCount(found
) - 1;
1571 /* Last element in attribute */
1573 switch (ippGetValueTag(found
))
1576 case IPP_TAG_INTEGER
:
1577 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
1580 case IPP_TAG_BOOLEAN
:
1581 if (ippGetBoolean(found
, last
))
1582 strlcpy(buffer
, "true", sizeof(buffer
));
1584 strlcpy(buffer
, "false", sizeof(buffer
));
1587 case IPP_TAG_RESOLUTION
:
1589 int xres
, /* Horizontal resolution */
1590 yres
; /* Vertical resolution */
1591 ipp_res_t units
; /* Resolution units */
1593 xres
= ippGetResolution(found
, last
, &yres
, &units
);
1596 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1598 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
1602 case IPP_TAG_CHARSET
:
1603 case IPP_TAG_KEYWORD
:
1604 case IPP_TAG_LANGUAGE
:
1605 case IPP_TAG_MIMETYPE
:
1607 case IPP_TAG_NAMELANG
:
1609 case IPP_TAG_TEXTLANG
:
1611 case IPP_TAG_URISCHEME
:
1612 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
1616 ippAttributeString(found
, buffer
, sizeof(buffer
));
1621 _ippVarsSet(vars
, expect
->define_value
, buffer
);
1624 if (found
&& expect
->repeat_match
&&
1625 repeat_count
< expect
->repeat_limit
)
1628 while (expect
->expect_all
&& (found
= ippFindNextAttribute(response
, expect
->name
, IPP_TAG_ZERO
)) != NULL
);
1633 * If we are going to repeat this test, display intermediate results...
1638 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1640 cupsFilePrintf(cupsFileStdout(), "%04d]\n", repeat_count
);
1642 if (data
->num_displayed
> 0)
1644 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1646 const char *attrname
= ippGetName(attrptr
);
1649 for (i
= 0; i
< data
->num_displayed
; i
++)
1651 if (!strcmp(data
->displayed
[i
], attrname
))
1653 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
1662 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1664 cupsFilePrintf(cupsFileStdout(), " %-68.68s [", data
->name
);
1667 ippDelete(response
);
1671 while (repeat_test
);
1677 if (cupsArrayCount(data
->errors
) > 0)
1678 data
->prev_pass
= data
->pass
= 0;
1680 if (data
->prev_pass
)
1681 data
->pass_count
++;
1683 data
->fail_count
++;
1685 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1687 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
1688 cupsFilePuts(data
->outfile
, data
->prev_pass
? "<true />\n" : "<false />\n");
1689 cupsFilePuts(data
->outfile
, "<key>StatusCode</key>\n");
1690 print_xml_string(data
->outfile
, "string", ippErrorString(cupsLastError()));
1691 cupsFilePuts(data
->outfile
, "<key>ResponseAttributes</key>\n");
1692 cupsFilePuts(data
->outfile
, "<array>\n");
1693 cupsFilePuts(data
->outfile
, "<dict>\n");
1694 for (attrptr
= ippFirstAttribute(response
), group
= ippGetGroupTag(attrptr
);
1696 attrptr
= ippNextAttribute(response
))
1697 print_attr(data
->outfile
, data
->output
, attrptr
, &group
);
1698 cupsFilePuts(data
->outfile
, "</dict>\n");
1699 cupsFilePuts(data
->outfile
, "</array>\n");
1701 else if (data
->output
== _CUPS_OUTPUT_IPPSERVER
&& response
)
1703 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1705 if (!ippGetName(attrptr
) || ippGetGroupTag(attrptr
) != IPP_TAG_PRINTER
)
1708 print_ippserver_attr(data
, attrptr
, 0);
1712 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1714 cupsFilePuts(cupsFileStdout(), data
->prev_pass
? "PASS]\n" : "FAIL]\n");
1716 if (!data
->prev_pass
|| (data
->verbosity
&& response
))
1718 cupsFilePrintf(cupsFileStdout(), " RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response
));
1719 cupsFilePrintf(cupsFileStdout(), " status-code = %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString());
1721 if (data
->verbosity
&& response
)
1723 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1724 print_attr(cupsFileStdout(), _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
1728 else if (!data
->prev_pass
&& data
->output
!= _CUPS_OUTPUT_QUIET
)
1729 fprintf(stderr
, "%s\n", cupsLastErrorString());
1731 if (data
->prev_pass
&& data
->output
>= _CUPS_OUTPUT_LIST
&& !data
->verbosity
&& data
->num_displayed
> 0)
1733 size_t width
; /* Length of value */
1735 for (i
= 0; i
< data
->num_displayed
; i
++)
1737 widths
[i
] = strlen(data
->displayed
[i
]);
1739 for (attrptr
= ippFindAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
);
1741 attrptr
= ippFindNextAttribute(response
, data
->displayed
[i
], IPP_TAG_ZERO
))
1743 width
= ippAttributeString(attrptr
, NULL
, 0);
1744 if (width
> widths
[i
])
1749 if (data
->output
== _CUPS_OUTPUT_CSV
)
1750 print_csv(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
1752 print_line(data
, NULL
, NULL
, data
->num_displayed
, data
->displayed
, widths
);
1754 attrptr
= ippFirstAttribute(response
);
1758 while (attrptr
&& ippGetGroupTag(attrptr
) <= IPP_TAG_OPERATION
)
1759 attrptr
= ippNextAttribute(response
);
1763 if (data
->output
== _CUPS_OUTPUT_CSV
)
1764 print_csv(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
1766 print_line(data
, response
, attrptr
, data
->num_displayed
, data
->displayed
, widths
);
1768 while (attrptr
&& ippGetGroupTag(attrptr
) > IPP_TAG_OPERATION
)
1769 attrptr
= ippNextAttribute(response
);
1773 else if (!data
->prev_pass
)
1775 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1777 cupsFilePuts(data
->outfile
, "<key>Errors</key>\n");
1778 cupsFilePuts(data
->outfile
, "<array>\n");
1780 for (error
= (char *)cupsArrayFirst(data
->errors
);
1782 error
= (char *)cupsArrayNext(data
->errors
))
1783 print_xml_string(data
->outfile
, "string", error
);
1785 cupsFilePuts(data
->outfile
, "</array>\n");
1788 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
1790 for (error
= (char *)cupsArrayFirst(data
->errors
);
1792 error
= (char *)cupsArrayNext(data
->errors
))
1793 cupsFilePrintf(cupsFileStdout(), " %s\n", error
);
1797 if (data
->num_displayed
> 0 && !data
->verbosity
&& response
&& (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout())))
1799 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
1801 if (ippGetName(attrptr
))
1803 for (i
= 0; i
< data
->num_displayed
; i
++)
1805 if (!strcmp(data
->displayed
[i
], ippGetName(attrptr
)))
1807 print_attr(data
->outfile
, data
->output
, attrptr
, NULL
);
1817 if (data
->output
== _CUPS_OUTPUT_PLIST
)
1818 cupsFilePuts(data
->outfile
, "</dict>\n");
1820 ippDelete(response
);
1823 for (i
= 0; i
< data
->num_statuses
; i
++)
1825 if (data
->statuses
[i
].if_defined
)
1826 free(data
->statuses
[i
].if_defined
);
1827 if (data
->statuses
[i
].if_not_defined
)
1828 free(data
->statuses
[i
].if_not_defined
);
1829 if (data
->statuses
[i
].define_match
)
1830 free(data
->statuses
[i
].define_match
);
1831 if (data
->statuses
[i
].define_no_match
)
1832 free(data
->statuses
[i
].define_no_match
);
1834 data
->num_statuses
= 0;
1836 for (i
= data
->num_expects
, expect
= data
->expects
; i
> 0; i
--, expect
++)
1839 if (expect
->of_type
)
1840 free(expect
->of_type
);
1841 if (expect
->same_count_as
)
1842 free(expect
->same_count_as
);
1843 if (expect
->if_defined
)
1844 free(expect
->if_defined
);
1845 if (expect
->if_not_defined
)
1846 free(expect
->if_not_defined
);
1847 if (expect
->with_value
)
1848 free(expect
->with_value
);
1849 if (expect
->define_match
)
1850 free(expect
->define_match
);
1851 if (expect
->define_no_match
)
1852 free(expect
->define_no_match
);
1853 if (expect
->define_value
)
1854 free(expect
->define_value
);
1856 data
->num_expects
= 0;
1858 for (i
= 0; i
< data
->num_displayed
; i
++)
1859 free(data
->displayed
[i
]);
1860 data
->num_displayed
= 0;
1862 return (data
->ignore_errors
|| data
->prev_pass
);
1867 * 'do_tests()' - Do tests as specified in the test file.
1870 static int /* O - 1 on success, 0 on failure */
1871 do_tests(const char *testfile
, /* I - Test file to use */
1872 _ipp_vars_t
*vars
, /* I - Variables */
1873 _cups_testdata_t
*data
) /* I - Test data */
1875 http_encryption_t encryption
; /* Encryption mode */
1879 * Connect to the printer/server...
1882 if (!_cups_strcasecmp(vars
->scheme
, "https") || !_cups_strcasecmp(vars
->scheme
, "ipps"))
1883 encryption
= HTTP_ENCRYPTION_ALWAYS
;
1885 encryption
= data
->encryption
;
1887 if ((data
->http
= httpConnect2(vars
->host
, vars
->port
, NULL
, data
->family
, encryption
, 1, 30000, NULL
)) == NULL
)
1889 print_fatal_error(data
, "Unable to connect to \"%s\" on port %d - %s", vars
->host
, vars
->port
, cupsLastErrorString());
1894 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "deflate, gzip, identity");
1896 httpSetDefaultField(data
->http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
1897 #endif /* HAVE_LIBZ */
1899 if (data
->timeout
> 0.0)
1900 httpSetTimeout(data
->http
, data
->timeout
, timeout_cb
, NULL
);
1906 _ippFileParse(vars
, testfile
, (void *)data
);
1909 * Close connection and return...
1912 httpClose(data
->http
);
1915 return (data
->pass
);
1920 * 'error_cb()' - Print/add an error message.
1923 static int /* O - 1 to continue, 0 to stop */
1924 error_cb(_ipp_file_t
*f
, /* I - IPP file data */
1925 _cups_testdata_t
*data
, /* I - Test data */
1926 const char *error
) /* I - Error message */
1930 print_fatal_error(data
, "%s", error
);
1937 * 'expect_matches()' - Return true if the tag matches the specification.
1940 static int /* O - 1 if matches, 0 otherwise */
1942 _cups_expect_t
*expect
, /* I - Expected attribute */
1943 ipp_tag_t value_tag
) /* I - Value tag for attribute */
1945 int match
; /* Match? */
1946 char *of_type
, /* Type name to match */
1947 *next
, /* Next name to match */
1948 sep
; /* Separator character */
1952 * If we don't expect a particular type, return immediately...
1955 if (!expect
->of_type
)
1959 * Parse the "of_type" value since the string can contain multiple attribute
1960 * types separated by "," or "|"...
1963 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
1966 * Find the next separator, and set it (temporarily) to nul if present.
1969 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
1971 if ((sep
= *next
) != '\0')
1975 * Support some meta-types to make it easier to write the test file.
1978 if (!strcmp(of_type
, "text"))
1979 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
1980 else if (!strcmp(of_type
, "name"))
1981 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
1982 else if (!strcmp(of_type
, "collection"))
1983 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
1985 match
= value_tag
== ippTagValue(of_type
);
1988 * Restore the separator if we have one...
2000 * 'get_filename()' - Get a filename based on the current test file.
2003 static char * /* O - Filename */
2004 get_filename(const char *testfile
, /* I - Current test file */
2005 char *dst
, /* I - Destination filename */
2006 const char *src
, /* I - Source filename */
2007 size_t dstsize
) /* I - Size of destination buffer */
2009 char *dstptr
; /* Pointer into destination */
2010 _cups_globals_t
*cg
= _cupsGlobals();
2014 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
2017 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2020 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
2021 dstptr
= dst
+ strlen(dst
) - 1;
2025 else if (!access(src
, R_OK
) || *src
== '/'
2027 || (isalpha(*src
& 255) && src
[1] == ':')
2032 * Use the path as-is...
2035 strlcpy(dst
, src
, dstsize
);
2040 * Make path relative to testfile...
2043 strlcpy(dst
, testfile
, dstsize
);
2044 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
2047 dstptr
= dst
; /* Should never happen */
2049 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
2057 * 'get_string()' - Get a pointer to a string value or the portion of interest.
2060 static const char * /* O - Pointer to string */
2061 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
2062 int element
, /* I - Element to fetch */
2063 int flags
, /* I - Value ("with") flags */
2064 char *buffer
, /* I - Temporary buffer */
2065 size_t bufsize
) /* I - Size of temporary buffer */
2067 const char *value
; /* Value */
2068 char *ptr
, /* Pointer into value */
2069 scheme
[256], /* URI scheme */
2070 userpass
[256], /* Username/password */
2071 hostname
[256], /* Hostname */
2072 resource
[1024]; /* Resource */
2073 int port
; /* Port number */
2076 value
= ippGetString(attr
, element
, NULL
);
2078 if (flags
& _CUPS_WITH_HOSTNAME
)
2080 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2083 ptr
= buffer
+ strlen(buffer
) - 1;
2084 if (ptr
>= buffer
&& *ptr
== '.')
2085 *ptr
= '\0'; /* Drop trailing "." */
2089 else if (flags
& _CUPS_WITH_RESOURCE
)
2091 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
2096 else if (flags
& _CUPS_WITH_SCHEME
)
2098 if (httpSeparateURI(HTTP_URI_CODING_ALL
, value
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
2103 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& (!strncmp(value
, "ipp://", 6) || !strncmp(value
, "http://", 7) || !strncmp(value
, "ipps://", 7) || !strncmp(value
, "https://", 8)))
2105 http_uri_status_t status
= httpSeparateURI(HTTP_URI_CODING_ALL
, value
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
));
2107 if (status
< HTTP_URI_STATUS_OK
)
2118 * Normalize URI with no trailing dot...
2121 if ((ptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&& *ptr
== '.')
2124 httpAssembleURI(HTTP_URI_CODING_ALL
, buffer
, (int)bufsize
, scheme
, userpass
, hostname
, port
, resource
);
2135 * 'init_data()' - Initialize test data.
2139 init_data(_cups_testdata_t
*data
) /* I - Data */
2141 memset(data
, 0, sizeof(_cups_testdata_t
));
2143 data
->output
= _CUPS_OUTPUT_LIST
;
2144 data
->outfile
= cupsFileStdout();
2145 data
->family
= AF_UNSPEC
;
2146 data
->def_transfer
= _CUPS_TRANSFER_AUTO
;
2147 data
->def_version
= 11;
2148 data
->errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
, (cups_afree_func_t
)free
);
2150 data
->prev_pass
= 1;
2151 data
->request_id
= (CUPS_RAND() % 1000) * 137 + 1;
2152 data
->show_header
= 1;
2157 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
2161 static char * /* O - ISO 8601 date/time string */
2162 iso_date(const ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
2164 time_t utctime
; /* UTC time since 1970 */
2165 struct tm
*utcdate
; /* UTC date/time */
2166 static char buffer
[255]; /* String buffer */
2169 utctime
= ippDateToTime(date
);
2170 utcdate
= gmtime(&utctime
);
2172 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
2173 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
2174 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
2181 * 'pause_message()' - Display the message and pause until the user presses a key.
2185 pause_message(const char *message
) /* I - Message */
2188 HANDLE tty
; /* Console handle */
2189 DWORD mode
; /* Console mode */
2190 char key
; /* Key press */
2191 DWORD bytes
; /* Bytes read for key press */
2195 * Disable input echo and set raw input...
2198 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
2201 if (!GetConsoleMode(tty
, &mode
))
2204 if (!SetConsoleMode(tty
, 0))
2208 int tty
; /* /dev/tty - never read from stdin */
2209 struct termios original
, /* Original input mode */
2210 noecho
; /* No echo input mode */
2211 char key
; /* Current key press */
2215 * Disable input echo and set raw input...
2218 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
2221 if (tcgetattr(tty
, &original
))
2228 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
2230 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
2238 * Display the prompt...
2241 cupsFilePrintf(cupsFileStdout(), "%s\n---- PRESS ANY KEY ----", message
);
2248 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
2254 SetConsoleMode(tty
, mode
);
2267 tcsetattr(tty
, TCSAFLUSH
, &original
);
2272 * Erase the "press any key" prompt...
2275 cupsFilePuts(cupsFileStdout(), "\r \r");
2280 * 'print_attr()' - Print an attribute on the screen.
2284 print_attr(cups_file_t
*outfile
, /* I - Output file */
2285 _cups_output_t output
, /* I - Output format */
2286 ipp_attribute_t
*attr
, /* I - Attribute to print */
2287 ipp_tag_t
*group
) /* IO - Current group */
2289 int i
, /* Looping var */
2290 count
; /* Number of values */
2291 ipp_attribute_t
*colattr
; /* Collection attribute */
2294 if (output
== _CUPS_OUTPUT_PLIST
)
2296 if (!ippGetName(attr
) || (group
&& *group
!= ippGetGroupTag(attr
)))
2298 if (ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
2300 cupsFilePuts(outfile
, "</dict>\n");
2301 cupsFilePuts(outfile
, "<dict>\n");
2305 *group
= ippGetGroupTag(attr
);
2308 if (!ippGetName(attr
))
2311 print_xml_string(outfile
, "key", ippGetName(attr
));
2312 if ((count
= ippGetCount(attr
)) > 1)
2313 cupsFilePuts(outfile
, "<array>\n");
2315 switch (ippGetValueTag(attr
))
2317 case IPP_TAG_INTEGER
:
2319 for (i
= 0; i
< count
; i
++)
2320 cupsFilePrintf(outfile
, "<integer>%d</integer>\n", ippGetInteger(attr
, i
));
2323 case IPP_TAG_BOOLEAN
:
2324 for (i
= 0; i
< count
; i
++)
2325 cupsFilePuts(outfile
, ippGetBoolean(attr
, i
) ? "<true />\n" : "<false />\n");
2328 case IPP_TAG_RANGE
:
2329 for (i
= 0; i
< count
; i
++)
2331 int lower
, upper
; /* Lower and upper ranges */
2333 lower
= ippGetRange(attr
, i
, &upper
);
2334 cupsFilePrintf(outfile
, "<dict><key>lower</key><integer>%d</integer><key>upper</key><integer>%d</integer></dict>\n", lower
, upper
);
2338 case IPP_TAG_RESOLUTION
:
2339 for (i
= 0; i
< count
; i
++)
2341 int xres
, yres
; /* Resolution values */
2342 ipp_res_t units
; /* Resolution units */
2344 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
2345 cupsFilePrintf(outfile
, "<dict><key>xres</key><integer>%d</integer><key>yres</key><integer>%d</integer><key>units</key><string>%s</string></dict>\n", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
2350 for (i
= 0; i
< count
; i
++)
2351 cupsFilePrintf(outfile
, "<date>%s</date>\n", iso_date(ippGetDate(attr
, i
)));
2354 case IPP_TAG_STRING
:
2355 for (i
= 0; i
< count
; i
++)
2357 int datalen
; /* Length of data */
2358 void *data
= ippGetOctetString(attr
, i
, &datalen
);
2360 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
2361 /* Base64 output buffer */
2363 cupsFilePrintf(outfile
, "<data>%s</data>\n", httpEncode64_2(buffer
, sizeof(buffer
), data
, datalen
));
2369 case IPP_TAG_KEYWORD
:
2371 case IPP_TAG_URISCHEME
:
2372 case IPP_TAG_CHARSET
:
2373 case IPP_TAG_LANGUAGE
:
2374 case IPP_TAG_MIMETYPE
:
2375 for (i
= 0; i
< count
; i
++)
2376 print_xml_string(outfile
, "string", ippGetString(attr
, i
, NULL
));
2379 case IPP_TAG_TEXTLANG
:
2380 case IPP_TAG_NAMELANG
:
2381 for (i
= 0; i
< count
; i
++)
2383 const char *s
, /* String */
2384 *lang
; /* Language */
2386 s
= ippGetString(attr
, i
, &lang
);
2387 cupsFilePuts(outfile
, "<dict><key>language</key><string>");
2388 print_xml_string(outfile
, NULL
, lang
);
2389 cupsFilePuts(outfile
, "</string><key>string</key><string>");
2390 print_xml_string(outfile
, NULL
, s
);
2391 cupsFilePuts(outfile
, "</string></dict>\n");
2395 case IPP_TAG_BEGIN_COLLECTION
:
2396 for (i
= 0; i
< count
; i
++)
2398 ipp_t
*col
= ippGetCollection(attr
, i
);
2399 /* Collection value */
2401 cupsFilePuts(outfile
, "<dict>\n");
2402 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
2403 print_attr(outfile
, output
, colattr
, NULL
);
2404 cupsFilePuts(outfile
, "</dict>\n");
2409 cupsFilePrintf(outfile
, "<string><<%s>></string>\n", ippTagString(ippGetValueTag(attr
)));
2414 cupsFilePuts(outfile
, "</array>\n");
2418 char buffer
[131072]; /* Value buffer */
2420 if (output
== _CUPS_OUTPUT_TEST
)
2422 if (!ippGetName(attr
))
2424 cupsFilePuts(outfile
, " -- separator --\n");
2428 cupsFilePrintf(outfile
, " %s (%s%s) = ", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
2431 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2432 cupsFilePrintf(outfile
, "%s\n", buffer
);
2438 * 'print_csv()' - Print a line of CSV text.
2443 _cups_testdata_t
*data
, /* I - Test data */
2444 ipp_t
*ipp
, /* I - Response message */
2445 ipp_attribute_t
*attr
, /* I - First attribute for line */
2446 int num_displayed
, /* I - Number of attributes to display */
2447 char **displayed
, /* I - Attributes to display */
2448 size_t *widths
) /* I - Column widths */
2450 int i
; /* Looping var */
2451 size_t maxlength
; /* Max length of all columns */
2452 char *buffer
, /* String buffer */
2453 *bufptr
; /* Pointer into buffer */
2454 ipp_attribute_t
*current
; /* Current attribute */
2458 * Get the maximum string length we have to show and allocate...
2461 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
2462 if (widths
[i
] > maxlength
)
2463 maxlength
= widths
[i
];
2467 if ((buffer
= malloc(maxlength
)) == NULL
)
2471 * Loop through the attributes to display...
2476 for (i
= 0; i
< num_displayed
; i
++)
2479 cupsFilePutChar(data
->outfile
, ',');
2483 for (current
= attr
; current
; current
= ippNextAttribute(ipp
))
2485 if (!ippGetName(current
))
2487 else if (!strcmp(ippGetName(current
), displayed
[i
]))
2489 ippAttributeString(current
, buffer
, maxlength
);
2494 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
2495 strchr(buffer
, '\\') != NULL
)
2497 cupsFilePutChar(cupsFileStdout(), '\"');
2498 for (bufptr
= buffer
; *bufptr
; bufptr
++)
2500 if (*bufptr
== '\\' || *bufptr
== '\"')
2501 cupsFilePutChar(cupsFileStdout(), '\\');
2502 cupsFilePutChar(cupsFileStdout(), *bufptr
);
2504 cupsFilePutChar(cupsFileStdout(), '\"');
2507 cupsFilePuts(data
->outfile
, buffer
);
2509 cupsFilePutChar(cupsFileStdout(), '\n');
2513 for (i
= 0; i
< num_displayed
; i
++)
2516 cupsFilePutChar(cupsFileStdout(), ',');
2518 cupsFilePuts(data
->outfile
, displayed
[i
]);
2520 cupsFilePutChar(cupsFileStdout(), '\n');
2528 * 'print_fatal_error()' - Print a fatal error message.
2533 _cups_testdata_t
*data
, /* I - Test data */
2534 const char *s
, /* I - Printf-style format string */
2535 ...) /* I - Additional arguments as needed */
2537 char buffer
[10240]; /* Format buffer */
2538 va_list ap
; /* Pointer to arguments */
2542 * Format the error message...
2546 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
2553 if (data
->output
== _CUPS_OUTPUT_PLIST
)
2555 print_xml_header(data
);
2556 print_xml_trailer(data
, 0, buffer
);
2559 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
2564 * 'print_ippserver_attr()' - Print a attribute suitable for use by ippserver.
2568 print_ippserver_attr(
2569 _cups_testdata_t
*data
, /* I - Test data */
2570 ipp_attribute_t
*attr
, /* I - Attribute to print */
2571 int indent
) /* I - Indentation level */
2573 int i
, /* Looping var */
2574 count
= ippGetCount(attr
);
2575 /* Number of values */
2576 ipp_attribute_t
*colattr
; /* Collection attribute */
2580 cupsFilePrintf(data
->outfile
, "ATTR %s %s", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
2582 cupsFilePrintf(data
->outfile
, "%*sMEMBER %s %s", indent
, "", ippTagString(ippGetValueTag(attr
)), ippGetName(attr
));
2584 switch (ippGetValueTag(attr
))
2586 case IPP_TAG_INTEGER
:
2588 for (i
= 0; i
< count
; i
++)
2589 cupsFilePrintf(data
->outfile
, "%s%d", i
? "," : " ", ippGetInteger(attr
, i
));
2592 case IPP_TAG_BOOLEAN
:
2593 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 0) ? " true" : " false");
2595 for (i
= 1; i
< count
; i
++)
2596 cupsFilePuts(data
->outfile
, ippGetBoolean(attr
, 1) ? ",true" : ",false");
2599 case IPP_TAG_RANGE
:
2600 for (i
= 0; i
< count
; i
++)
2602 int upper
, lower
= ippGetRange(attr
, i
, &upper
);
2604 cupsFilePrintf(data
->outfile
, "%s%d-%d", i
? "," : " ", lower
, upper
);
2608 case IPP_TAG_RESOLUTION
:
2609 for (i
= 0; i
< count
; i
++)
2612 int yres
, xres
= ippGetResolution(attr
, i
, &yres
, &units
);
2614 cupsFilePrintf(data
->outfile
, "%s%dx%d%s", i
? "," : " ", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
2619 for (i
= 0; i
< count
; i
++)
2620 cupsFilePrintf(data
->outfile
, "%s%s", i
? "," : " ", iso_date(ippGetDate(attr
, i
)));
2623 case IPP_TAG_STRING
:
2624 for (i
= 0; i
< count
; i
++)
2627 const char *s
= (const char *)ippGetOctetString(attr
, i
, &len
);
2629 cupsFilePuts(data
->outfile
, i
? "," : " ");
2630 print_ippserver_string(data
, s
, (size_t)len
);
2635 case IPP_TAG_TEXTLANG
:
2637 case IPP_TAG_NAMELANG
:
2638 case IPP_TAG_KEYWORD
:
2640 case IPP_TAG_URISCHEME
:
2641 case IPP_TAG_CHARSET
:
2642 case IPP_TAG_LANGUAGE
:
2643 case IPP_TAG_MIMETYPE
:
2644 for (i
= 0; i
< count
; i
++)
2646 const char *s
= ippGetString(attr
, i
, NULL
);
2648 cupsFilePuts(data
->outfile
, i
? "," : " ");
2649 print_ippserver_string(data
, s
, strlen(s
));
2653 case IPP_TAG_BEGIN_COLLECTION
:
2654 for (i
= 0; i
< count
; i
++)
2656 ipp_t
*col
= ippGetCollection(attr
, i
);
2658 cupsFilePuts(data
->outfile
, i
? ",{\n" : " {\n");
2659 for (colattr
= ippFirstAttribute(col
); colattr
; colattr
= ippNextAttribute(col
))
2660 print_ippserver_attr(data
, colattr
, indent
+ 4);
2661 cupsFilePrintf(data
->outfile
, "%*s}", indent
, "");
2666 /* Out-of-band value */
2670 cupsFilePuts(data
->outfile
, "\n");
2675 * 'print_ippserver_string()' - Print a string suitable for use by ippserver.
2679 print_ippserver_string(
2680 _cups_testdata_t
*data
, /* I - Test data */
2681 const char *s
, /* I - String to print */
2682 size_t len
) /* I - Length of string */
2684 cupsFilePutChar(data
->outfile
, '\"');
2688 cupsFilePutChar(data
->outfile
, '\\');
2689 cupsFilePutChar(data
->outfile
, *s
);
2694 cupsFilePutChar(data
->outfile
, '\"');
2699 * 'print_line()' - Print a line of formatted or CSV text.
2704 _cups_testdata_t
*data
, /* I - Test data */
2705 ipp_t
*ipp
, /* I - Response message */
2706 ipp_attribute_t
*attr
, /* I - First attribute for line */
2707 int num_displayed
, /* I - Number of attributes to display */
2708 char **displayed
, /* I - Attributes to display */
2709 size_t *widths
) /* I - Column widths */
2711 int i
; /* Looping var */
2712 size_t maxlength
; /* Max length of all columns */
2713 char *buffer
; /* String buffer */
2714 ipp_attribute_t
*current
; /* Current attribute */
2718 * Get the maximum string length we have to show and allocate...
2721 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
2722 if (widths
[i
] > maxlength
)
2723 maxlength
= widths
[i
];
2727 if ((buffer
= malloc(maxlength
)) == NULL
)
2731 * Loop through the attributes to display...
2736 for (i
= 0; i
< num_displayed
; i
++)
2739 cupsFilePutChar(cupsFileStdout(), ' ');
2743 for (current
= attr
; current
; current
= ippNextAttribute(ipp
))
2745 if (!ippGetName(current
))
2747 else if (!strcmp(ippGetName(current
), displayed
[i
]))
2749 ippAttributeString(current
, buffer
, maxlength
);
2754 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], buffer
);
2756 cupsFilePutChar(cupsFileStdout(), '\n');
2760 for (i
= 0; i
< num_displayed
; i
++)
2763 cupsFilePutChar(cupsFileStdout(), ' ');
2765 cupsFilePrintf(data
->outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
2767 cupsFilePutChar(cupsFileStdout(), '\n');
2769 for (i
= 0; i
< num_displayed
; i
++)
2772 cupsFilePutChar(cupsFileStdout(), ' ');
2774 memset(buffer
, '-', widths
[i
]);
2775 buffer
[widths
[i
]] = '\0';
2776 cupsFilePuts(data
->outfile
, buffer
);
2778 cupsFilePutChar(cupsFileStdout(), '\n');
2786 * 'print_xml_header()' - Print a standard XML plist header.
2790 print_xml_header(_cups_testdata_t
*data
)/* I - Test data */
2792 if (!data
->xml_header
)
2794 cupsFilePuts(data
->outfile
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2795 cupsFilePuts(data
->outfile
, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
2796 cupsFilePuts(data
->outfile
, "<plist version=\"1.0\">\n");
2797 cupsFilePuts(data
->outfile
, "<dict>\n");
2798 cupsFilePuts(data
->outfile
, "<key>ipptoolVersion</key>\n");
2799 cupsFilePuts(data
->outfile
, "<string>" CUPS_SVERSION
"</string>\n");
2800 cupsFilePuts(data
->outfile
, "<key>Transfer</key>\n");
2801 cupsFilePrintf(data
->outfile
, "<string>%s</string>\n", data
->transfer
== _CUPS_TRANSFER_AUTO
? "auto" : data
->transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
2802 cupsFilePuts(data
->outfile
, "<key>Tests</key>\n");
2803 cupsFilePuts(data
->outfile
, "<array>\n");
2805 data
->xml_header
= 1;
2811 * 'print_xml_string()' - Print an XML string with escaping.
2815 print_xml_string(cups_file_t
*outfile
, /* I - Test data */
2816 const char *element
, /* I - Element name or NULL */
2817 const char *s
) /* I - String to print */
2820 cupsFilePrintf(outfile
, "<%s>", element
);
2825 cupsFilePuts(outfile
, "&");
2827 cupsFilePuts(outfile
, "<");
2829 cupsFilePuts(outfile
, ">");
2830 else if ((*s
& 0xe0) == 0xc0)
2833 * Validate UTF-8 two-byte sequence...
2836 if ((s
[1] & 0xc0) != 0x80)
2838 cupsFilePutChar(outfile
, '?');
2843 cupsFilePutChar(outfile
, *s
++);
2844 cupsFilePutChar(outfile
, *s
);
2847 else if ((*s
& 0xf0) == 0xe0)
2850 * Validate UTF-8 three-byte sequence...
2853 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
2855 cupsFilePutChar(outfile
, '?');
2860 cupsFilePutChar(outfile
, *s
++);
2861 cupsFilePutChar(outfile
, *s
++);
2862 cupsFilePutChar(outfile
, *s
);
2865 else if ((*s
& 0xf8) == 0xf0)
2868 * Validate UTF-8 four-byte sequence...
2871 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
2872 (s
[3] & 0xc0) != 0x80)
2874 cupsFilePutChar(outfile
, '?');
2879 cupsFilePutChar(outfile
, *s
++);
2880 cupsFilePutChar(outfile
, *s
++);
2881 cupsFilePutChar(outfile
, *s
++);
2882 cupsFilePutChar(outfile
, *s
);
2885 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
2888 * Invalid control character...
2891 cupsFilePutChar(outfile
, '?');
2894 cupsFilePutChar(outfile
, *s
);
2900 cupsFilePrintf(outfile
, "</%s>\n", element
);
2905 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
2910 _cups_testdata_t
*data
, /* I - Test data */
2911 int success
, /* I - 1 on success, 0 on failure */
2912 const char *message
) /* I - Error message or NULL */
2914 if (data
->xml_header
)
2916 cupsFilePuts(data
->outfile
, "</array>\n");
2917 cupsFilePuts(data
->outfile
, "<key>Successful</key>\n");
2918 cupsFilePuts(data
->outfile
, success
? "<true />\n" : "<false />\n");
2921 cupsFilePuts(data
->outfile
, "<key>ErrorMessage</key>\n");
2922 print_xml_string(data
->outfile
, "string", message
);
2924 cupsFilePuts(data
->outfile
, "</dict>\n");
2925 cupsFilePuts(data
->outfile
, "</plist>\n");
2927 data
->xml_header
= 0;
2934 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
2938 sigterm_handler(int sig
) /* I - Signal number (unused) */
2944 signal(SIGINT
, SIG_DFL
);
2945 signal(SIGTERM
, SIG_DFL
);
2947 #endif /* !_WIN32 */
2951 * 'timeout_cb()' - Handle HTTP timeouts.
2954 static int /* O - 1 to continue, 0 to cancel */
2955 timeout_cb(http_t
*http
, /* I - Connection to server */
2956 void *user_data
) /* I - User data (unused) */
2958 int buffered
= 0; /* Bytes buffered but not yet sent */
2964 * If the socket still have data waiting to be sent to the printer (as can
2965 * happen if the printer runs out of paper), continue to wait until the output
2966 * buffer is empty...
2969 #ifdef SO_NWRITE /* macOS and some versions of Linux */
2970 socklen_t len
= sizeof(buffered
); /* Size of return value */
2972 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
2975 #elif defined(SIOCOUTQ) /* Others except Windows */
2976 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
2979 #else /* Windows (not possible) */
2981 #endif /* SO_NWRITE */
2983 return (buffered
> 0);
2988 * 'token_cb()' - Parse test file-specific tokens and run tests.
2991 static int /* O - 1 to continue, 0 to stop */
2992 token_cb(_ipp_file_t
*f
, /* I - IPP file data */
2993 _ipp_vars_t
*vars
, /* I - IPP variables */
2994 _cups_testdata_t
*data
, /* I - Test data */
2995 const char *token
) /* I - Current token */
2997 char name
[1024], /* Name string */
2998 temp
[1024], /* Temporary string */
2999 value
[1024], /* Value string */
3000 *ptr
; /* Pointer into value */
3006 * Initialize state as needed (nothing for now...)
3014 * Parse until we see a close brace...
3017 if (_cups_strcasecmp(token
, "COUNT") &&
3018 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
3019 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
3020 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
3021 _cups_strcasecmp(token
, "IF-DEFINED") &&
3022 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
3023 _cups_strcasecmp(token
, "IN-GROUP") &&
3024 _cups_strcasecmp(token
, "OF-TYPE") &&
3025 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
3026 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
3027 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
3028 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
3029 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
3030 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
3031 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
3032 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
3033 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
3034 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
3035 _cups_strcasecmp(token
, "WITH-SCHEME") &&
3036 _cups_strcasecmp(token
, "WITH-VALUE") &&
3037 _cups_strcasecmp(token
, "WITH-VALUE-FROM"))
3038 data
->last_expect
= NULL
;
3040 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
3041 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
3042 _cups_strcasecmp(token
, "IF-DEFINED") &&
3043 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
3044 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
3045 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
3046 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
3047 data
->last_status
= NULL
;
3049 if (!strcmp(token
, "}"))
3051 return (do_test(f
, vars
, data
));
3053 else if (!strcmp(token
, "COMPRESSION"))
3057 * COMPRESSION deflate
3061 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3063 _ippVarsExpand(vars
, data
->compression
, temp
, sizeof(data
->compression
));
3065 if (strcmp(data
->compression
, "none") && strcmp(data
->compression
, "deflate") &&
3066 strcmp(data
->compression
, "gzip"))
3068 if (strcmp(data
->compression
, "none"))
3069 #endif /* HAVE_LIBZ */
3071 print_fatal_error(data
, "Unsupported COMPRESSION value \"%s\" on line %d of \"%s\".", data
->compression
, f
->linenum
, f
->filename
);
3075 if (!strcmp(data
->compression
, "none"))
3076 data
->compression
[0] = '\0';
3080 print_fatal_error(data
, "Missing COMPRESSION value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3084 else if (!strcmp(token
, "DEFINE"))
3090 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
3092 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3093 _ippVarsSet(vars
, name
, value
);
3097 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3101 else if (!strcmp(token
, "IGNORE-ERRORS"))
3108 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3110 data
->ignore_errors
= !_cups_strcasecmp(temp
, "yes");
3114 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3118 else if (!_cups_strcasecmp(token
, "NAME"))
3124 _ippFileReadToken(f
, temp
, sizeof(temp
));
3125 _ippVarsExpand(vars
, data
->name
, temp
, sizeof(data
->name
));
3127 else if (!_cups_strcasecmp(token
, "PAUSE"))
3130 * Pause with a message...
3133 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3135 pause_message(temp
);
3139 print_fatal_error(data
, "Missing PAUSE message on line %d of \"%s\".", f
->linenum
, f
->filename
);
3143 else if (!strcmp(token
, "REQUEST-ID"))
3150 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3152 if (isdigit(temp
[0] & 255))
3154 data
->request_id
= atoi(temp
);
3156 else if (!_cups_strcasecmp(temp
, "random"))
3158 data
->request_id
= (CUPS_RAND() % 1000) * 137 + 1;
3162 print_fatal_error(data
, "Bad REQUEST-ID value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3168 print_fatal_error(data
, "Missing REQUEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3172 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
3175 * SKIP-IF-DEFINED variable
3178 if (_ippFileReadToken(f
, name
, sizeof(name
)))
3180 if (_ippVarsGet(vars
, name
))
3181 data
->skip_test
= 1;
3185 print_fatal_error(data
, "Missing SKIP-IF-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3189 else if (!strcmp(token
, "SKIP-IF-MISSING"))
3192 * SKIP-IF-MISSING filename
3195 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3197 char filename
[1024]; /* Filename */
3199 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3200 get_filename(f
->filename
, filename
, temp
, sizeof(filename
));
3202 if (access(filename
, R_OK
))
3203 data
->skip_test
= 1;
3207 print_fatal_error(data
, "Missing SKIP-IF-MISSING filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
3211 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
3214 * SKIP-IF-NOT-DEFINED variable
3217 if (_ippFileReadToken(f
, name
, sizeof(name
)))
3219 if (!_ippVarsGet(vars
, name
))
3220 data
->skip_test
= 1;
3224 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3228 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
3231 * SKIP-PREVIOUS-ERROR yes
3232 * SKIP-PREVIOUS-ERROR no
3235 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
3237 data
->skip_previous
= !_cups_strcasecmp(temp
, "yes");
3241 print_fatal_error(data
, "Missing SKIP-PREVIOUS-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3245 else if (!strcmp(token
, "TEST-ID"))
3251 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3253 _ippVarsExpand(vars
, data
->test_id
, temp
, sizeof(data
->test_id
));
3257 print_fatal_error(data
, "Missing TEST-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3261 else if (!strcmp(token
, "TRANSFER"))
3269 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3271 if (!strcmp(temp
, "auto"))
3273 data
->transfer
= _CUPS_TRANSFER_AUTO
;
3275 else if (!strcmp(temp
, "chunked"))
3277 data
->transfer
= _CUPS_TRANSFER_CHUNKED
;
3279 else if (!strcmp(temp
, "length"))
3281 data
->transfer
= _CUPS_TRANSFER_LENGTH
;
3285 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3291 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3295 else if (!_cups_strcasecmp(token
, "VERSION"))
3297 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
3299 if (!strcmp(temp
, "0.0"))
3303 else if (!strcmp(temp
, "1.0"))
3307 else if (!strcmp(temp
, "1.1"))
3311 else if (!strcmp(temp
, "2.0"))
3315 else if (!strcmp(temp
, "2.1"))
3319 else if (!strcmp(temp
, "2.2"))
3325 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3331 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
3335 else if (!_cups_strcasecmp(token
, "RESOURCE"))
3341 if (!_ippFileReadToken(f
, data
->resource
, sizeof(data
->resource
)))
3343 print_fatal_error(data
, "Missing RESOURCE path on line %d of \"%s\".", f
->linenum
, f
->filename
);
3347 else if (!_cups_strcasecmp(token
, "OPERATION"))
3353 ipp_op_t op
; /* Operation code */
3355 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3357 print_fatal_error(data
, "Missing OPERATION code on line %d of \"%s\".", f
->linenum
, f
->filename
);
3361 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3363 if ((op
= ippOpValue(value
)) == (ipp_op_t
)-1 && (op
= (ipp_op_t
)strtol(value
, NULL
, 0)) == 0)
3365 print_fatal_error(data
, "Bad OPERATION code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3369 ippSetOperation(f
->attrs
, op
);
3371 else if (!_cups_strcasecmp(token
, "GROUP"))
3374 * Attribute group...
3377 ipp_tag_t group_tag
; /* Group tag */
3379 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3381 print_fatal_error(data
, "Missing GROUP tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
3385 if ((group_tag
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| group_tag
>= IPP_TAG_UNSUPPORTED_VALUE
)
3387 print_fatal_error(data
, "Bad GROUP tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3391 if (group_tag
== f
->group_tag
)
3392 ippAddSeparator(f
->attrs
);
3394 f
->group_tag
= group_tag
;
3396 else if (!_cups_strcasecmp(token
, "DELAY"))
3399 * Delay before operation...
3402 double dval
; /* Delay value */
3404 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3406 print_fatal_error(data
, "Missing DELAY value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3410 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3412 if ((dval
= _cupsStrScand(value
, &ptr
, localeconv())) < 0.0 || (*ptr
&& *ptr
!= ','))
3414 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
3418 data
->delay
= (useconds_t
)(1000000.0 * dval
);
3422 if ((dval
= _cupsStrScand(ptr
+ 1, &ptr
, localeconv())) <= 0.0 || *ptr
)
3424 print_fatal_error(data
, "Bad DELAY value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
3428 data
->repeat_interval
= (useconds_t
)(1000000.0 * dval
);
3431 data
->repeat_interval
= data
->delay
;
3433 else if (!_cups_strcasecmp(token
, "FILE"))
3439 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3441 print_fatal_error(data
, "Missing FILE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
3445 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3446 get_filename(f
->filename
, data
->file
, value
, sizeof(data
->file
));
3448 if (access(data
->file
, R_OK
))
3450 print_fatal_error(data
, "Filename \"%s\" (mapped to \"%s\") on line %d of \"%s\" cannot be read.", value
, data
->file
, f
->linenum
, f
->filename
);
3454 else if (!_cups_strcasecmp(token
, "STATUS"))
3460 if (data
->num_statuses
>= (int)(sizeof(data
->statuses
) / sizeof(data
->statuses
[0])))
3462 print_fatal_error(data
, "Too many STATUS's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3466 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3468 print_fatal_error(data
, "Missing STATUS code on line %d of \"%s\".", f
->linenum
, f
->filename
);
3472 if ((data
->statuses
[data
->num_statuses
].status
= ippErrorValue(temp
)) == (ipp_status_t
)-1 && (data
->statuses
[data
->num_statuses
].status
= (ipp_status_t
)strtol(temp
, NULL
, 0)) == 0)
3474 print_fatal_error(data
, "Bad STATUS code \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3478 data
->last_status
= data
->statuses
+ data
->num_statuses
;
3479 data
->num_statuses
++;
3481 data
->last_status
->define_match
= NULL
;
3482 data
->last_status
->define_no_match
= NULL
;
3483 data
->last_status
->if_defined
= NULL
;
3484 data
->last_status
->if_not_defined
= NULL
;
3485 data
->last_status
->repeat_limit
= 1000;
3486 data
->last_status
->repeat_match
= 0;
3487 data
->last_status
->repeat_no_match
= 0;
3489 else if (!_cups_strcasecmp(token
, "EXPECT") || !_cups_strcasecmp(token
, "EXPECT-ALL"))
3492 * Expected attributes...
3495 int expect_all
= !_cups_strcasecmp(token
, "EXPECT-ALL");
3497 if (data
->num_expects
>= (int)(sizeof(data
->expects
) / sizeof(data
->expects
[0])))
3499 print_fatal_error(data
, "Too many EXPECT's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3503 if (!_ippFileReadToken(f
, name
, sizeof(name
)))
3505 print_fatal_error(data
, "Missing EXPECT name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3509 data
->last_expect
= data
->expects
+ data
->num_expects
;
3510 data
->num_expects
++;
3512 memset(data
->last_expect
, 0, sizeof(_cups_expect_t
));
3513 data
->last_expect
->repeat_limit
= 1000;
3514 data
->last_expect
->expect_all
= expect_all
;
3518 data
->last_expect
->not_expect
= 1;
3519 data
->last_expect
->name
= strdup(name
+ 1);
3521 else if (name
[0] == '?')
3523 data
->last_expect
->optional
= 1;
3524 data
->last_expect
->name
= strdup(name
+ 1);
3527 data
->last_expect
->name
= strdup(name
);
3529 else if (!_cups_strcasecmp(token
, "COUNT"))
3531 int count
; /* Count value */
3533 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3535 print_fatal_error(data
, "Missing COUNT number on line %d of \"%s\".", f
->linenum
, f
->filename
);
3539 if ((count
= atoi(temp
)) <= 0)
3541 print_fatal_error(data
, "Bad COUNT \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3545 if (data
->last_expect
)
3547 data
->last_expect
->count
= count
;
3551 print_fatal_error(data
, "COUNT without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3555 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
3557 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3559 print_fatal_error(data
, "Missing DEFINE-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3563 if (data
->last_expect
)
3565 data
->last_expect
->define_match
= strdup(temp
);
3567 else if (data
->last_status
)
3569 data
->last_status
->define_match
= strdup(temp
);
3573 print_fatal_error(data
, "DEFINE-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3577 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
3579 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3581 print_fatal_error(data
, "Missing DEFINE-NO-MATCH variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3585 if (data
->last_expect
)
3587 data
->last_expect
->define_no_match
= strdup(temp
);
3589 else if (data
->last_status
)
3591 data
->last_status
->define_no_match
= strdup(temp
);
3595 print_fatal_error(data
, "DEFINE-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3599 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
3601 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3603 print_fatal_error(data
, "Missing DEFINE-VALUE variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
3607 if (data
->last_expect
)
3609 data
->last_expect
->define_value
= strdup(temp
);
3613 print_fatal_error(data
, "DEFINE-VALUE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3617 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
3619 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3621 print_fatal_error(data
, "Missing OF-TYPE value tag(s) on line %d of \"%s\".", f
->linenum
, f
->filename
);
3625 if (data
->last_expect
)
3627 data
->last_expect
->of_type
= strdup(temp
);
3631 print_fatal_error(data
, "OF-TYPE without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3635 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
3637 ipp_tag_t in_group
; /* IN-GROUP value */
3639 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3641 print_fatal_error(data
, "Missing IN-GROUP group tag on line %d of \"%s\".", f
->linenum
, f
->filename
);
3645 if ((in_group
= ippTagValue(temp
)) == IPP_TAG_ZERO
|| in_group
>= IPP_TAG_UNSUPPORTED_VALUE
)
3647 print_fatal_error(data
, "Bad IN-GROUP group tag \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
3650 else if (data
->last_expect
)
3652 data
->last_expect
->in_group
= in_group
;
3656 print_fatal_error(data
, "IN-GROUP without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3660 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
3662 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3664 print_fatal_error(data
, "Missing REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3667 else if (atoi(temp
) <= 0)
3669 print_fatal_error(data
, "Bad REPEAT-LIMIT value on line %d of \"%s\".", f
->linenum
, f
->filename
);
3673 if (data
->last_status
)
3675 data
->last_status
->repeat_limit
= atoi(temp
);
3677 else if (data
->last_expect
)
3679 data
->last_expect
->repeat_limit
= atoi(temp
);
3683 print_fatal_error(data
, "REPEAT-LIMIT without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3687 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
3689 if (data
->last_status
)
3691 data
->last_status
->repeat_match
= 1;
3693 else if (data
->last_expect
)
3695 data
->last_expect
->repeat_match
= 1;
3699 print_fatal_error(data
, "REPEAT-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3703 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
3705 if (data
->last_status
)
3707 data
->last_status
->repeat_no_match
= 1;
3709 else if (data
->last_expect
)
3711 data
->last_expect
->repeat_no_match
= 1;
3715 print_fatal_error(data
, "REPEAT-NO-MATCH without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3719 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
3721 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3723 print_fatal_error(data
, "Missing SAME-COUNT-AS name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3727 if (data
->last_expect
)
3729 data
->last_expect
->same_count_as
= strdup(temp
);
3733 print_fatal_error(data
, "SAME-COUNT-AS without a preceding EXPECT on line %d of \"%s\".", f
->linenum
, f
->filename
);
3737 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
3739 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3741 print_fatal_error(data
, "Missing IF-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3745 if (data
->last_expect
)
3747 data
->last_expect
->if_defined
= strdup(temp
);
3749 else if (data
->last_status
)
3751 data
->last_status
->if_defined
= strdup(temp
);
3755 print_fatal_error(data
, "IF-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3759 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
3761 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3763 print_fatal_error(data
, "Missing IF-NOT-DEFINED name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3767 if (data
->last_expect
)
3769 data
->last_expect
->if_not_defined
= strdup(temp
);
3771 else if (data
->last_status
)
3773 data
->last_status
->if_not_defined
= strdup(temp
);
3777 print_fatal_error(data
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS on line %d of \"%s\".", f
->linenum
, f
->filename
);
3781 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
3782 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
3783 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
3784 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
3785 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
3786 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
3787 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
3788 !_cups_strcasecmp(token
, "WITH-VALUE"))
3790 off_t lastpos
; /* Last file position */
3791 int lastline
; /* Last line number */
3793 if (data
->last_expect
)
3795 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") || !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
3796 data
->last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
3797 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") || !_cups_strcasecmp(token
, "WITH-RESOURCE"))
3798 data
->last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
3799 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") || !_cups_strcasecmp(token
, "WITH-SCHEME"))
3800 data
->last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
3802 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
3803 data
->last_expect
->with_flags
|= _CUPS_WITH_ALL
;
3806 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3808 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3813 * Read additional comma-delimited values - needed since legacy test files
3814 * will have unquoted WITH-VALUE values with commas...
3817 ptr
= temp
+ strlen(temp
);
3821 lastpos
= cupsFileTell(f
->fp
);
3822 lastline
= f
->linenum
;
3825 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3828 if (!strcmp(ptr
, ","))
3836 if (!_ippFileReadToken(f
, ptr
, (sizeof(temp
) - (size_t)(ptr
- temp
))))
3842 * Not another value, stop here...
3845 cupsFileSeek(f
->fp
, lastpos
);
3846 f
->linenum
= lastline
;
3852 if (data
->last_expect
)
3855 * Expand any variables in the value and then save it.
3858 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3860 ptr
= value
+ strlen(value
) - 1;
3862 if (value
[0] == '/' && ptr
> value
&& *ptr
== '/')
3865 * WITH-VALUE is a POSIX extended regular expression.
3868 data
->last_expect
->with_value
= calloc(1, (size_t)(ptr
- value
));
3869 data
->last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
3871 if (data
->last_expect
->with_value
)
3872 memcpy(data
->last_expect
->with_value
, value
+ 1, (size_t)(ptr
- value
- 1));
3877 * WITH-VALUE is a literal value...
3880 for (ptr
= value
; *ptr
; ptr
++)
3882 if (*ptr
== '\\' && ptr
[1])
3885 * Remove \ from \foo...
3888 _cups_strcpy(ptr
, ptr
+ 1);
3892 data
->last_expect
->with_value
= strdup(value
);
3893 data
->last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
3898 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3902 else if (!_cups_strcasecmp(token
, "WITH-VALUE-FROM"))
3904 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3906 print_fatal_error(data
, "Missing %s value on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3910 if (data
->last_expect
)
3913 * Expand any variables in the value and then save it.
3916 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
3918 data
->last_expect
->with_value_from
= strdup(value
);
3919 data
->last_expect
->with_flags
= _CUPS_WITH_LITERAL
;
3923 print_fatal_error(data
, "%s without a preceding EXPECT on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3927 else if (!_cups_strcasecmp(token
, "DISPLAY"))
3930 * Display attributes...
3933 if (data
->num_displayed
>= (int)(sizeof(data
->displayed
) / sizeof(data
->displayed
[0])))
3935 print_fatal_error(data
, "Too many DISPLAY's on line %d of \"%s\".", f
->linenum
, f
->filename
);
3939 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
3941 print_fatal_error(data
, "Missing DISPLAY name on line %d of \"%s\".", f
->linenum
, f
->filename
);
3945 data
->displayed
[data
->num_displayed
] = strdup(temp
);
3946 data
->num_displayed
++;
3950 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
3957 * Scan for the start of a test (open brace)...
3960 if (!strcmp(token
, "{"))
3966 if (data
->show_header
)
3968 if (data
->output
== _CUPS_OUTPUT_PLIST
)
3969 print_xml_header(data
);
3971 if (data
->output
== _CUPS_OUTPUT_TEST
|| (data
->output
== _CUPS_OUTPUT_PLIST
&& data
->outfile
!= cupsFileStdout()))
3972 cupsFilePrintf(cupsFileStdout(), "\"%s\":\n", f
->filename
);
3974 data
->show_header
= 0;
3977 data
->compression
[0] = '\0';
3979 data
->num_expects
= 0;
3980 data
->last_expect
= NULL
;
3981 data
->file
[0] = '\0';
3982 data
->ignore_errors
= data
->def_ignore_errors
;
3983 strlcpy(data
->name
, f
->filename
, sizeof(data
->name
));
3984 if ((ptr
= strrchr(data
->name
, '.')) != NULL
)
3986 data
->repeat_interval
= 5000000;
3987 data
->request_id
++;
3988 strlcpy(data
->resource
, vars
->resource
, sizeof(data
->resource
));
3989 data
->skip_previous
= 0;
3990 data
->skip_test
= 0;
3991 data
->num_statuses
= 0;
3992 data
->last_status
= NULL
;
3993 data
->test_id
[0] = '\0';
3994 data
->transfer
= data
->def_transfer
;
3995 data
->version
= data
->def_version
;
3997 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
3999 f
->attrs
= ippNew();
4000 f
->group_tag
= IPP_TAG_ZERO
;
4002 else if (!strcmp(token
, "DEFINE"))
4008 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4010 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
4011 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4012 _ippVarsSet(vars
, name
, value
);
4016 print_fatal_error(data
, "Missing DEFINE name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4020 else if (!strcmp(token
, "DEFINE-DEFAULT"))
4023 * DEFINE-DEFAULT name value
4026 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4028 if (!_ippVarsGet(vars
, name
))
4030 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
4031 _ippVarsExpand(vars
, value
, temp
, sizeof(value
));
4032 _ippVarsSet(vars
, name
, value
);
4037 print_fatal_error(data
, "Missing DEFINE-DEFAULT name and/or value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4041 else if (!strcmp(token
, "FILE-ID"))
4047 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4049 _ippVarsSet(vars
, "date-current", iso_date(ippTimeToDate(time(NULL
))));
4050 _ippVarsExpand(vars
, data
->file_id
, temp
, sizeof(data
->file_id
));
4054 print_fatal_error(data
, "Missing FILE-ID value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4058 else if (!strcmp(token
, "IGNORE-ERRORS"))
4065 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4067 data
->def_ignore_errors
= !_cups_strcasecmp(temp
, "yes");
4071 print_fatal_error(data
, "Missing IGNORE-ERRORS value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4075 else if (!strcmp(token
, "INCLUDE"))
4078 * INCLUDE "filename"
4079 * INCLUDE <filename>
4082 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4085 * Map the filename to and then run the tests...
4088 _cups_testdata_t inc_data
; /* Data for included file */
4089 char filename
[1024]; /* Mapped filename */
4091 memcpy(&inc_data
, data
, sizeof(inc_data
));
4092 inc_data
.http
= NULL
;
4094 inc_data
.prev_pass
= 1;
4095 inc_data
.show_header
= 1;
4097 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4099 data
->pass
= data
->prev_pass
= 0;
4105 print_fatal_error(data
, "Missing INCLUDE filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4109 data
->show_header
= 1;
4111 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
4114 * INCLUDE-IF-DEFINED name "filename"
4115 * INCLUDE-IF-DEFINED name <filename>
4118 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4121 * Map the filename to and then run the tests...
4124 _cups_testdata_t inc_data
; /* Data for included file */
4125 char filename
[1024]; /* Mapped filename */
4127 memcpy(&inc_data
, data
, sizeof(inc_data
));
4128 inc_data
.http
= NULL
;
4130 inc_data
.prev_pass
= 1;
4131 inc_data
.show_header
= 1;
4133 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4135 data
->pass
= data
->prev_pass
= 0;
4141 print_fatal_error(data
, "Missing INCLUDE-IF-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4145 data
->show_header
= 1;
4147 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
4150 * INCLUDE-IF-NOT-DEFINED name "filename"
4151 * INCLUDE-IF-NOT-DEFINED name <filename>
4154 if (_ippFileReadToken(f
, name
, sizeof(name
)) && _ippFileReadToken(f
, temp
, sizeof(temp
)))
4157 * Map the filename to and then run the tests...
4160 _cups_testdata_t inc_data
; /* Data for included file */
4161 char filename
[1024]; /* Mapped filename */
4163 memcpy(&inc_data
, data
, sizeof(inc_data
));
4164 inc_data
.http
= NULL
;
4166 inc_data
.prev_pass
= 1;
4167 inc_data
.show_header
= 1;
4169 if (!do_tests(get_filename(f
->filename
, filename
, temp
, sizeof(filename
)), vars
, &inc_data
) && data
->stop_after_include_error
)
4171 data
->pass
= data
->prev_pass
= 0;
4177 print_fatal_error(data
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on line %d of \"%s\".", f
->linenum
, f
->filename
);
4181 data
->show_header
= 1;
4183 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
4186 * SKIP-IF-DEFINED variable
4189 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4191 if (_ippVarsGet(vars
, name
))
4192 data
->skip_test
= 1;
4196 print_fatal_error(data
, "Missing SKIP-IF-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4200 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
4203 * SKIP-IF-NOT-DEFINED variable
4206 if (_ippFileReadToken(f
, name
, sizeof(name
)))
4208 if (!_ippVarsGet(vars
, name
))
4209 data
->skip_test
= 1;
4213 print_fatal_error(data
, "Missing SKIP-IF-NOT-DEFINED variable on line %d of \"%s\".", f
->linenum
, f
->filename
);
4217 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
4220 * STOP-AFTER-INCLUDE-ERROR yes
4221 * STOP-AFTER-INCLUDE-ERROR no
4224 if (_ippFileReadToken(f
, temp
, sizeof(temp
)) && (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
4226 data
->stop_after_include_error
= !_cups_strcasecmp(temp
, "yes");
4230 print_fatal_error(data
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4234 else if (!strcmp(token
, "TRANSFER"))
4242 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4244 if (!strcmp(temp
, "auto"))
4245 data
->def_transfer
= _CUPS_TRANSFER_AUTO
;
4246 else if (!strcmp(temp
, "chunked"))
4247 data
->def_transfer
= _CUPS_TRANSFER_CHUNKED
;
4248 else if (!strcmp(temp
, "length"))
4249 data
->def_transfer
= _CUPS_TRANSFER_LENGTH
;
4252 print_fatal_error(data
, "Bad TRANSFER value \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4258 print_fatal_error(data
, "Missing TRANSFER value on line %d of \"%s\".", f
->linenum
, f
->filename
);
4262 else if (!strcmp(token
, "VERSION"))
4264 if (_ippFileReadToken(f
, temp
, sizeof(temp
)))
4266 if (!strcmp(temp
, "1.0"))
4267 data
->def_version
= 10;
4268 else if (!strcmp(temp
, "1.1"))
4269 data
->def_version
= 11;
4270 else if (!strcmp(temp
, "2.0"))
4271 data
->def_version
= 20;
4272 else if (!strcmp(temp
, "2.1"))
4273 data
->def_version
= 21;
4274 else if (!strcmp(temp
, "2.2"))
4275 data
->def_version
= 22;
4278 print_fatal_error(data
, "Bad VERSION \"%s\" on line %d of \"%s\".", temp
, f
->linenum
, f
->filename
);
4284 print_fatal_error(data
, "Missing VERSION number on line %d of \"%s\".", f
->linenum
, f
->filename
);
4290 print_fatal_error(data
, "Unexpected token %s seen on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
4300 * 'usage()' - Show program usage.
4306 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... filenameN ]"));
4307 _cupsLangPuts(stderr
, _("Options:"));
4308 _cupsLangPuts(stderr
, _("--ippserver filename Produce ippserver attribute file"));
4309 _cupsLangPuts(stderr
, _("--stop-after-include-error\n"
4310 " Stop tests after a failed INCLUDE"));
4311 _cupsLangPuts(stderr
, _("--version Show version"));
4312 _cupsLangPuts(stderr
, _("-4 Connect using IPv4"));
4313 _cupsLangPuts(stderr
, _("-6 Connect using IPv6"));
4314 _cupsLangPuts(stderr
, _("-C Send requests using chunking (default)"));
4315 _cupsLangPuts(stderr
, _("-E Test with encryption using HTTP Upgrade to TLS"));
4316 _cupsLangPuts(stderr
, _("-I Ignore errors"));
4317 _cupsLangPuts(stderr
, _("-L Send requests using content-length"));
4318 _cupsLangPuts(stderr
, _("-P filename.plist Produce XML plist to a file and test report to standard output"));
4319 _cupsLangPuts(stderr
, _("-S Test with encryption using HTTPS"));
4320 _cupsLangPuts(stderr
, _("-T seconds Set the receive/send timeout in seconds"));
4321 _cupsLangPuts(stderr
, _("-V version Set default IPP version"));
4322 _cupsLangPuts(stderr
, _("-X Produce XML plist instead of plain text"));
4323 _cupsLangPuts(stderr
, _("-c Produce CSV output"));
4324 _cupsLangPuts(stderr
, _("-d name=value Set named variable to value"));
4325 _cupsLangPuts(stderr
, _("-f filename Set default request filename"));
4326 _cupsLangPuts(stderr
, _("-h Validate HTTP response headers"));
4327 _cupsLangPuts(stderr
, _("-i seconds Repeat the last file with the given time interval"));
4328 _cupsLangPuts(stderr
, _("-l Produce plain text output"));
4329 _cupsLangPuts(stderr
, _("-n count Repeat the last file the given number of times"));
4330 _cupsLangPuts(stderr
, _("-q Run silently"));
4331 _cupsLangPuts(stderr
, _("-t Produce a test report"));
4332 _cupsLangPuts(stderr
, _("-v Be verbose"));
4339 * 'with_flags_string()' - Return the "WITH-xxx" predicate that corresponds to
4343 static const char * /* O - WITH-xxx string */
4344 with_flags_string(int flags
) /* I - WITH flags */
4346 if (flags
& _CUPS_WITH_ALL
)
4348 if (flags
& _CUPS_WITH_HOSTNAME
)
4349 return ("WITH-ALL-HOSTNAMES");
4350 else if (flags
& _CUPS_WITH_RESOURCE
)
4351 return ("WITH-ALL-RESOURCES");
4352 else if (flags
& _CUPS_WITH_SCHEME
)
4353 return ("WITH-ALL-SCHEMES");
4355 return ("WITH-ALL-VALUES");
4357 else if (flags
& _CUPS_WITH_HOSTNAME
)
4358 return ("WITH-HOSTNAME");
4359 else if (flags
& _CUPS_WITH_RESOURCE
)
4360 return ("WITH-RESOURCE");
4361 else if (flags
& _CUPS_WITH_SCHEME
)
4362 return ("WITH-SCHEME");
4364 return ("WITH-VALUE");
4369 * 'with_value()' - Test a WITH-VALUE predicate.
4372 static int /* O - 1 on match, 0 on non-match */
4373 with_value(_cups_testdata_t
*data
, /* I - Test data */
4374 cups_array_t
*errors
, /* I - Errors array */
4375 char *value
, /* I - Value string */
4376 int flags
, /* I - Flags for match */
4377 ipp_attribute_t
*attr
, /* I - Attribute to compare */
4378 char *matchbuf
, /* I - Buffer to hold matching value */
4379 size_t matchlen
) /* I - Length of match buffer */
4381 int i
, /* Looping var */
4382 count
, /* Number of values */
4384 char temp
[1024], /* Temporary value string */
4385 *valptr
; /* Pointer into value */
4386 const char *name
; /* Attribute name */
4390 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
4393 * NULL matches everything.
4396 if (!value
|| !*value
)
4400 * Compare the value string to the attribute value.
4403 name
= ippGetName(attr
);
4404 count
= ippGetCount(attr
);
4406 switch (ippGetValueTag(attr
))
4408 case IPP_TAG_INTEGER
:
4410 for (i
= 0; i
< count
; i
++)
4412 char op
, /* Comparison operator */
4413 *nextptr
; /* Next pointer */
4414 int intvalue
, /* Integer value */
4415 attrvalue
= ippGetInteger(attr
, i
),
4416 /* Attribute value */
4417 valmatch
= 0; /* Does the current value match? */
4421 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4422 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4423 *valptr
== '=' || *valptr
== '>')
4426 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4428 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4436 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
4437 if (nextptr
== valptr
)
4441 if ((op
== '=' && attrvalue
== intvalue
) ||
4442 (op
== '<' && attrvalue
< intvalue
) ||
4443 (op
== '>' && attrvalue
> intvalue
))
4446 snprintf(matchbuf
, matchlen
, "%d", attrvalue
);
4453 if (flags
& _CUPS_WITH_ALL
)
4468 if (!match
&& errors
)
4470 for (i
= 0; i
< count
; i
++)
4471 add_stringf(data
->errors
, "GOT: %s=%d", name
, ippGetInteger(attr
, i
));
4475 case IPP_TAG_RANGE
:
4476 for (i
= 0; i
< count
; i
++)
4478 char op
, /* Comparison operator */
4479 *nextptr
; /* Next pointer */
4480 int intvalue
, /* Integer value */
4481 lower
, /* Lower range */
4482 upper
, /* Upper range */
4483 valmatch
= 0; /* Does the current value match? */
4485 lower
= ippGetRange(attr
, i
, &upper
);
4488 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
4489 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
4490 *valptr
== '=' || *valptr
== '>')
4493 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
4495 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
4503 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
4504 if (nextptr
== valptr
)
4508 if ((op
== '=' && (lower
== intvalue
|| upper
== intvalue
)) ||
4509 (op
== '<' && upper
< intvalue
) ||
4510 (op
== '>' && upper
> intvalue
))
4513 snprintf(matchbuf
, matchlen
, "%d-%d", lower
, upper
);
4520 if (flags
& _CUPS_WITH_ALL
)
4535 if (!match
&& errors
)
4537 for (i
= 0; i
< count
; i
++)
4539 int lower
, upper
; /* Range values */
4541 lower
= ippGetRange(attr
, i
, &upper
);
4542 add_stringf(data
->errors
, "GOT: %s=%d-%d", name
, lower
, upper
);
4547 case IPP_TAG_BOOLEAN
:
4548 for (i
= 0; i
< count
; i
++)
4550 if ((!strcmp(value
, "true") || !strcmp(value
, "1")) == ippGetBoolean(attr
, i
))
4553 strlcpy(matchbuf
, value
, matchlen
);
4555 if (!(flags
& _CUPS_WITH_ALL
))
4561 else if (flags
& _CUPS_WITH_ALL
)
4568 if (!match
&& errors
)
4570 for (i
= 0; i
< count
; i
++)
4571 add_stringf(data
->errors
, "GOT: %s=%s", name
, ippGetBoolean(attr
, i
) ? "true" : "false");
4575 case IPP_TAG_RESOLUTION
:
4576 for (i
= 0; i
< count
; i
++)
4578 int xres
, yres
; /* Resolution values */
4579 ipp_res_t units
; /* Resolution units */
4581 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
4583 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
4585 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
4587 if (!strcmp(value
, temp
))
4590 strlcpy(matchbuf
, value
, matchlen
);
4592 if (!(flags
& _CUPS_WITH_ALL
))
4598 else if (flags
& _CUPS_WITH_ALL
)
4605 if (!match
&& errors
)
4607 for (i
= 0; i
< count
; i
++)
4609 int xres
, yres
; /* Resolution values */
4610 ipp_res_t units
; /* Resolution units */
4612 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
4614 snprintf(temp
, sizeof(temp
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
4616 snprintf(temp
, sizeof(temp
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
4618 if (strcmp(value
, temp
))
4619 add_stringf(data
->errors
, "GOT: %s=%s", name
, temp
);
4624 case IPP_TAG_NOVALUE
:
4625 case IPP_TAG_UNKNOWN
:
4628 case IPP_TAG_CHARSET
:
4629 case IPP_TAG_KEYWORD
:
4630 case IPP_TAG_LANGUAGE
:
4631 case IPP_TAG_MIMETYPE
:
4633 case IPP_TAG_NAMELANG
:
4635 case IPP_TAG_TEXTLANG
:
4637 case IPP_TAG_URISCHEME
:
4638 if (flags
& _CUPS_WITH_REGEX
)
4641 * Value is an extended, case-sensitive POSIX regular expression...
4644 regex_t re
; /* Regular expression */
4646 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
4648 regerror(i
, &re
, temp
, sizeof(temp
));
4650 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value
, temp
);
4655 * See if ALL of the values match the given regular expression.
4658 for (i
= 0; i
< count
; i
++)
4660 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
4664 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
4666 if (!(flags
& _CUPS_WITH_ALL
))
4672 else if (flags
& _CUPS_WITH_ALL
)
4681 else if (ippGetValueTag(attr
) == IPP_TAG_URI
&& !(flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
| _CUPS_WITH_RESOURCE
)))
4684 * Value is a literal URI string, see if the value(s) match...
4687 for (i
= 0; i
< count
; i
++)
4689 if (!compare_uris(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
4692 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
4694 if (!(flags
& _CUPS_WITH_ALL
))
4700 else if (flags
& _CUPS_WITH_ALL
)
4710 * Value is a literal string, see if the value(s) match...
4713 for (i
= 0; i
< count
; i
++)
4717 switch (ippGetValueTag(attr
))
4721 * Some URI components are case-sensitive, some not...
4724 if (flags
& (_CUPS_WITH_SCHEME
| _CUPS_WITH_HOSTNAME
))
4725 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
4727 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
4730 case IPP_TAG_MIMETYPE
:
4732 case IPP_TAG_NAMELANG
:
4734 case IPP_TAG_TEXTLANG
:
4736 * mimeMediaType, nameWithoutLanguage, nameWithLanguage,
4737 * textWithoutLanguage, and textWithLanguage are defined to
4738 * be case-insensitive strings...
4741 result
= _cups_strcasecmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
4746 * Other string syntaxes are defined as lowercased so we use
4747 * case-sensitive comparisons to catch problems...
4750 result
= strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)));
4757 strlcpy(matchbuf
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)), matchlen
);
4759 if (!(flags
& _CUPS_WITH_ALL
))
4765 else if (flags
& _CUPS_WITH_ALL
)
4773 if (!match
&& errors
)
4775 for (i
= 0; i
< count
; i
++)
4776 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, ippGetString(attr
, i
, NULL
));
4780 case IPP_TAG_STRING
:
4781 if (flags
& _CUPS_WITH_REGEX
)
4784 * Value is an extended, case-sensitive POSIX regular expression...
4787 void *adata
; /* Pointer to octetString data */
4788 int adatalen
; /* Length of octetString */
4789 regex_t re
; /* Regular expression */
4791 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
4793 regerror(i
, &re
, temp
, sizeof(temp
));
4795 print_fatal_error(data
, "Unable to compile WITH-VALUE regular expression \"%s\" - %s", value
, temp
);
4800 * See if ALL of the values match the given regular expression.
4803 for (i
= 0; i
< count
; i
++)
4805 if ((adata
= ippGetOctetString(attr
, i
, &adatalen
)) == NULL
|| adatalen
>= (int)sizeof(temp
))
4810 memcpy(temp
, adata
, (size_t)adatalen
);
4811 temp
[adatalen
] = '\0';
4813 if (!regexec(&re
, temp
, 0, NULL
, 0))
4816 strlcpy(matchbuf
, temp
, matchlen
);
4818 if (!(flags
& _CUPS_WITH_ALL
))
4824 else if (flags
& _CUPS_WITH_ALL
)
4833 if (!match
&& errors
)
4835 for (i
= 0; i
< count
; i
++)
4837 adata
= ippGetOctetString(attr
, i
, &adatalen
);
4838 copy_hex_string(temp
, adata
, adatalen
, sizeof(temp
));
4839 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, temp
);
4846 * Value is a literal or hex-encoded string...
4849 unsigned char withdata
[1023], /* WITH-VALUE data */
4850 *adata
; /* Pointer to octetString data */
4851 int withlen
, /* Length of WITH-VALUE data */
4852 adatalen
; /* Length of octetString */
4857 * Grab hex-encoded value...
4860 if ((withlen
= (int)strlen(value
)) & 1 || withlen
> (int)(2 * (sizeof(withdata
) + 1)))
4862 print_fatal_error(data
, "Bad WITH-VALUE hex value.");
4866 withlen
= withlen
/ 2 - 1;
4868 for (valptr
= value
+ 1, adata
= withdata
; *valptr
; valptr
+= 2)
4870 int ch
; /* Current character/byte */
4872 if (isdigit(valptr
[0]))
4873 ch
= (valptr
[0] - '0') << 4;
4874 else if (isalpha(valptr
[0]))
4875 ch
= (tolower(valptr
[0]) - 'a' + 10) << 4;
4879 if (isdigit(valptr
[1]))
4880 ch
|= valptr
[1] - '0';
4881 else if (isalpha(valptr
[1]))
4882 ch
|= tolower(valptr
[1]) - 'a' + 10;
4886 *adata
++ = (unsigned char)ch
;
4891 print_fatal_error(data
, "Bad WITH-VALUE hex value.");
4898 * Copy literal string value...
4901 withlen
= (int)strlen(value
);
4903 memcpy(withdata
, value
, (size_t)withlen
);
4906 for (i
= 0; i
< count
; i
++)
4908 adata
= ippGetOctetString(attr
, i
, &adatalen
);
4910 if (withlen
== adatalen
&& !memcmp(withdata
, adata
, (size_t)withlen
))
4913 copy_hex_string(matchbuf
, adata
, adatalen
, matchlen
);
4915 if (!(flags
& _CUPS_WITH_ALL
))
4921 else if (flags
& _CUPS_WITH_ALL
)
4928 if (!match
&& errors
)
4930 for (i
= 0; i
< count
; i
++)
4932 adata
= ippGetOctetString(attr
, i
, &adatalen
);
4933 copy_hex_string(temp
, adata
, adatalen
, sizeof(temp
));
4934 add_stringf(data
->errors
, "GOT: %s=\"%s\"", name
, temp
);
4949 * 'with_value_from()' - Test a WITH-VALUE-FROM predicate.
4952 static int /* O - 1 on match, 0 on non-match */
4954 cups_array_t
*errors
, /* I - Errors array */
4955 ipp_attribute_t
*fromattr
, /* I - "From" attribute */
4956 ipp_attribute_t
*attr
, /* I - Attribute to compare */
4957 char *matchbuf
, /* I - Buffer to hold matching value */
4958 size_t matchlen
) /* I - Length of match buffer */
4960 int i
, j
, /* Looping vars */
4961 count
= ippGetCount(attr
), /* Number of attribute values */
4962 match
= 1; /* Match? */
4968 * Compare the from value(s) to the attribute value(s)...
4971 switch (ippGetValueTag(attr
))
4973 case IPP_TAG_INTEGER
:
4974 if (ippGetValueTag(fromattr
) != IPP_TAG_INTEGER
&& ippGetValueTag(fromattr
) != IPP_TAG_RANGE
)
4975 goto wrong_value_tag
;
4977 for (i
= 0; i
< count
; i
++)
4979 int value
= ippGetInteger(attr
, i
);
4980 /* Current integer value */
4982 if (ippContainsInteger(fromattr
, value
))
4985 snprintf(matchbuf
, matchlen
, "%d", value
);
4989 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
4996 if (ippGetValueTag(fromattr
) != IPP_TAG_ENUM
)
4997 goto wrong_value_tag
;
4999 for (i
= 0; i
< count
; i
++)
5001 int value
= ippGetInteger(attr
, i
);
5002 /* Current integer value */
5004 if (ippContainsInteger(fromattr
, value
))
5007 snprintf(matchbuf
, matchlen
, "%d", value
);
5011 add_stringf(errors
, "GOT: %s=%d", ippGetName(attr
), value
);
5017 case IPP_TAG_RESOLUTION
:
5018 if (ippGetValueTag(fromattr
) != IPP_TAG_RESOLUTION
)
5019 goto wrong_value_tag
;
5021 for (i
= 0; i
< count
; i
++)
5025 int fromcount
= ippGetCount(fromattr
);
5026 int fromxres
, fromyres
;
5027 ipp_res_t fromunits
;
5029 xres
= ippGetResolution(attr
, i
, &yres
, &units
);
5031 for (j
= 0; j
< fromcount
; j
++)
5033 fromxres
= ippGetResolution(fromattr
, j
, &fromyres
, &fromunits
);
5034 if (fromxres
== xres
&& fromyres
== yres
&& fromunits
== units
)
5043 snprintf(matchbuf
, matchlen
, "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5045 snprintf(matchbuf
, matchlen
, "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5051 add_stringf(errors
, "GOT: %s=%d%s", ippGetName(attr
), xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5053 add_stringf(errors
, "GOT: %s=%dx%d%s", ippGetName(attr
), xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
5060 case IPP_TAG_NOVALUE
:
5061 case IPP_TAG_UNKNOWN
:
5064 case IPP_TAG_CHARSET
:
5065 case IPP_TAG_KEYWORD
:
5066 case IPP_TAG_LANGUAGE
:
5067 case IPP_TAG_MIMETYPE
:
5069 case IPP_TAG_NAMELANG
:
5071 case IPP_TAG_TEXTLANG
:
5072 case IPP_TAG_URISCHEME
:
5073 for (i
= 0; i
< count
; i
++)
5075 const char *value
= ippGetString(attr
, i
, NULL
);
5076 /* Current string value */
5078 if (ippContainsString(fromattr
, value
))
5081 strlcpy(matchbuf
, value
, matchlen
);
5085 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
5092 for (i
= 0; i
< count
; i
++)
5094 const char *value
= ippGetString(attr
, i
, NULL
);
5095 /* Current string value */
5096 int fromcount
= ippGetCount(fromattr
);
5098 for (j
= 0; j
< fromcount
; j
++)
5100 if (!compare_uris(value
, ippGetString(fromattr
, j
, NULL
)))
5103 strlcpy(matchbuf
, value
, matchlen
);
5110 add_stringf(errors
, "GOT: %s='%s'", ippGetName(attr
), value
);
5123 /* value tag mismatch between fromattr and attr */
5126 add_stringf(errors
, "GOT: %s OF-TYPE %s", ippGetName(attr
), ippTagString(ippGetValueTag(attr
)));