4 * ipptool command for CUPS.
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include <cups/cups-private.h>
23 #include <cups/file-private.h>
35 #endif /* !O_BINARY */
42 typedef enum _cups_transfer_e
/**** How to send request data ****/
44 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
45 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
46 _CUPS_TRANSFER_LENGTH
/* Length always */
49 typedef enum _cups_output_e
/**** Output mode ****/
51 _CUPS_OUTPUT_QUIET
, /* No output */
52 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
53 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
54 _CUPS_OUTPUT_LIST
, /* Tabular list output */
55 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
58 typedef enum _cups_with_e
/**** WITH flags ****/
60 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
61 _CUPS_WITH_ALL
= 1, /* Must match all values */
62 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
63 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
64 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
65 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
68 typedef struct _cups_expect_s
/**** Expected attribute info ****/
70 int optional
, /* Optional attribute? */
71 not_expect
; /* Don't expect attribute? */
72 char *name
, /* Attribute name */
73 *of_type
, /* Type name */
74 *same_count_as
, /* Parallel attribute name */
75 *if_defined
, /* Only required if variable defined */
76 *if_not_defined
, /* Only required if variable is not defined */
77 *with_value
, /* Attribute must include this value */
78 *define_match
, /* Variable to define on match */
79 *define_no_match
, /* Variable to define on no-match */
80 *define_value
; /* Variable to define with value */
81 int repeat_limit
, /* Maximum number of times to repeat */
82 repeat_match
, /* Repeat test on match */
83 repeat_no_match
, /* Repeat test on no match */
84 with_flags
, /* WITH flags */
85 count
; /* Expected count if > 0 */
86 ipp_tag_t in_group
; /* IN-GROUP value */
89 typedef struct _cups_status_s
/**** Status info ****/
91 ipp_status_t status
; /* Expected status code */
92 char *if_defined
, /* Only if variable is defined */
93 *if_not_defined
, /* Only if variable is not defined */
94 *define_match
, /* Variable to define on match */
95 *define_no_match
, /* Variable to define on no-match */
96 *define_value
; /* Variable to define with value */
97 int repeat_limit
, /* Maximum number of times to repeat */
98 repeat_match
, /* Repeat the test when it does not match */
99 repeat_no_match
; /* Repeat the test when it matches */
102 typedef struct _cups_var_s
/**** Variable ****/
104 char *name
, /* Name of variable */
105 *value
; /* Value of variable */
108 typedef struct _cups_vars_s
/**** Set of variables ****/
110 char *uri
, /* URI for printer */
111 *filename
, /* Filename */
112 scheme
[64], /* Scheme from URI */
113 userpass
[256], /* Username/password from URI */
114 hostname
[256], /* Hostname from URI */
115 resource
[1024]; /* Resource path from URI */
116 int port
; /* Port number from URI */
117 http_encryption_t encryption
; /* Encryption for connection? */
118 double timeout
; /* Timeout for connection */
119 int family
; /* Address family */
120 cups_array_t
*vars
; /* Array of variables */
128 static _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
129 /* How to transfer requests */
130 static _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
132 static int Cancel
= 0, /* Cancel test? */
133 IgnoreErrors
= 0, /* Ignore errors? */
134 StopAfterIncludeError
= 0,
135 /* Stop after include errors? */
136 Verbosity
= 0, /* Show all attributes? */
137 Version
= 11, /* Default IPP version */
138 XMLHeader
= 0, /* 1 if header is written */
139 TestCount
= 0, /* Number of tests run */
140 PassCount
= 0, /* Number of passing tests */
141 FailCount
= 0, /* Number of failing tests */
142 SkipCount
= 0; /* Number of skipped tests */
143 static char *Username
= NULL
, /* Username from URI */
144 *Password
= NULL
; /* Password from URI */
145 static int PasswordTries
= 0; /* Number of tries with password */
152 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
153 __attribute__ ((__format__ (__printf__
, 2, 3)));
154 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
155 static int do_tests(FILE *outfile
, _cups_vars_t
*vars
, const char *testfile
);
156 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
157 size_t dstsize
) __attribute__((nonnull(1,2,3)));
158 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
159 static ipp_t
*get_collection(FILE *outfile
, _cups_vars_t
*vars
, FILE *fp
, int *linenum
);
160 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
162 static char *get_string(ipp_attribute_t
*attr
, int element
, int flags
,
163 char *buffer
, size_t bufsize
);
164 static char *get_token(FILE *fp
, char *buf
, int buflen
,
166 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
167 static char *iso_date(ipp_uchar_t
*date
);
168 static const char *password_cb(const char *prompt
);
169 static void pause_message(const char *message
);
170 static void print_attr(FILE *outfile
, int format
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
171 static void print_csv(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
,
172 char **displayed
, size_t *widths
);
173 static void print_fatal_error(FILE *outfile
, const char *s
, ...)
174 __attribute__ ((__format__ (__printf__
, 2, 3)));
175 static void print_line(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
,
176 char **displayed
, size_t *widths
);
177 static void print_xml_header(FILE *outfile
);
178 static void print_xml_string(FILE *outfile
, const char *element
, const char *s
);
179 static void print_xml_trailer(FILE *outfile
, int success
, const char *message
);
180 static void set_variable(FILE *outfile
, _cups_vars_t
*vars
, const char *name
, const char *value
);
182 static void sigterm_handler(int sig
);
184 static int timeout_cb(http_t
*http
, void *user_data
);
185 static void usage(void) __attribute__((noreturn
));
186 static int validate_attr(FILE *outfile
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
187 static int with_value(FILE *outfile
, cups_array_t
*errors
, char *value
, int flags
,
188 ipp_attribute_t
*attr
, char *matchbuf
,
193 * 'main()' - Parse options and do tests.
196 int /* O - Exit status */
197 main(int argc
, /* I - Number of command-line args */
198 char *argv
[]) /* I - Command-line arguments */
200 int i
; /* Looping var */
201 int status
; /* Status of tests... */
202 FILE *outfile
= stdout
;
204 char *opt
, /* Current option */
205 name
[1024], /* Name/value buffer */
206 *value
, /* Pointer to value */
207 filename
[1024], /* Real filename */
208 testname
[1024], /* Real test filename */
209 uri
[1024]; /* Copy of printer URI */
210 const char *ext
, /* Extension on filename */
211 *testfile
; /* Test file to use */
212 int interval
, /* Test interval in microseconds */
213 repeat
; /* Repeat count */
214 _cups_vars_t vars
; /* Variables */
215 http_uri_status_t uri_status
; /* URI separation status */
216 _cups_globals_t
*cg
= _cupsGlobals();
222 * Catch SIGINT and SIGTERM...
225 signal(SIGINT
, sigterm_handler
);
226 signal(SIGTERM
, sigterm_handler
);
230 * Initialize the locale and variables...
233 _cupsSetLocale(argv
);
235 memset(&vars
, 0, sizeof(vars
));
236 vars
.family
= AF_UNSPEC
;
237 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
242 * ipptool URI testfile
250 for (i
= 1; i
< argc
; i
++)
252 if (!strcmp(argv
[i
], "--help"))
256 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
258 StopAfterIncludeError
= 1;
260 else if (!strcmp(argv
[i
], "--version"))
265 else if (argv
[i
][0] == '-')
267 for (opt
= argv
[i
] + 1; *opt
; opt
++)
271 case '4' : /* Connect using IPv4 only */
272 vars
.family
= AF_INET
;
276 case '6' : /* Connect using IPv6 only */
277 vars
.family
= AF_INET6
;
279 #endif /* AF_INET6 */
281 case 'C' : /* Enable HTTP chunking */
282 Transfer
= _CUPS_TRANSFER_CHUNKED
;
285 case 'E' : /* Encrypt with TLS */
287 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
289 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
291 #endif /* HAVE_SSL */
294 case 'I' : /* Ignore errors */
298 case 'L' : /* Disable HTTP chunking */
299 Transfer
= _CUPS_TRANSFER_LENGTH
;
302 case 'P' : /* Output to plist file */
307 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
311 if (outfile
!= stdout
)
314 if ((outfile
= fopen(argv
[i
], "w")) == NULL
)
316 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
320 Output
= _CUPS_OUTPUT_PLIST
;
322 if (interval
|| repeat
)
324 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
329 case 'S' : /* Encrypt with SSL */
331 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
333 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
335 #endif /* HAVE_SSL */
338 case 'T' : /* Set timeout */
343 _cupsLangPrintf(stderr
,
344 _("%s: Missing timeout for \"-T\"."),
349 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
352 case 'V' : /* Set IPP version */
357 _cupsLangPrintf(stderr
,
358 _("%s: Missing version for \"-V\"."),
363 if (!strcmp(argv
[i
], "1.0"))
365 else if (!strcmp(argv
[i
], "1.1"))
367 else if (!strcmp(argv
[i
], "2.0"))
369 else if (!strcmp(argv
[i
], "2.1"))
371 else if (!strcmp(argv
[i
], "2.2"))
375 _cupsLangPrintf(stderr
,
376 _("%s: Bad version %s for \"-V\"."),
382 case 'X' : /* Produce XML output */
383 Output
= _CUPS_OUTPUT_PLIST
;
385 if (interval
|| repeat
)
387 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
392 case 'c' : /* CSV output */
393 Output
= _CUPS_OUTPUT_CSV
;
396 case 'd' : /* Define a variable */
401 _cupsLangPuts(stderr
,
402 _("ipptool: Missing name=value for \"-d\"."));
406 strlcpy(name
, argv
[i
], sizeof(name
));
407 if ((value
= strchr(name
, '=')) != NULL
)
410 value
= name
+ strlen(name
);
412 set_variable(outfile
, &vars
, name
, value
);
415 case 'f' : /* Set the default test filename */
420 _cupsLangPuts(stderr
,
421 _("ipptool: Missing filename for \"-f\"."));
428 vars
.filename
= NULL
;
431 if (access(argv
[i
], 0))
437 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
438 if (access(filename
, 0) && filename
[0] != '/'
440 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
444 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
445 cg
->cups_datadir
, argv
[i
]);
446 if (access(filename
, 0))
448 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
449 cg
->cups_datadir
, argv
[i
]);
450 if (access(filename
, 0))
451 vars
.filename
= strdup(argv
[i
]);
453 vars
.filename
= strdup(filename
);
456 vars
.filename
= strdup(filename
);
459 vars
.filename
= strdup(filename
);
462 vars
.filename
= strdup(argv
[i
]);
464 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
467 * Guess the MIME media type based on the extension...
470 if (!_cups_strcasecmp(ext
, ".gif"))
471 set_variable(outfile
, &vars
, "filetype", "image/gif");
472 else if (!_cups_strcasecmp(ext
, ".htm") ||
473 !_cups_strcasecmp(ext
, ".htm.gz") ||
474 !_cups_strcasecmp(ext
, ".html") ||
475 !_cups_strcasecmp(ext
, ".html.gz"))
476 set_variable(outfile
, &vars
, "filetype", "text/html");
477 else if (!_cups_strcasecmp(ext
, ".jpg") ||
478 !_cups_strcasecmp(ext
, ".jpeg"))
479 set_variable(outfile
, &vars
, "filetype", "image/jpeg");
480 else if (!_cups_strcasecmp(ext
, ".pcl") ||
481 !_cups_strcasecmp(ext
, ".pcl.gz"))
482 set_variable(outfile
, &vars
, "filetype", "application/vnd.hp-PCL");
483 else if (!_cups_strcasecmp(ext
, ".pdf"))
484 set_variable(outfile
, &vars
, "filetype", "application/pdf");
485 else if (!_cups_strcasecmp(ext
, ".png"))
486 set_variable(outfile
, &vars
, "filetype", "image/png");
487 else if (!_cups_strcasecmp(ext
, ".ps") ||
488 !_cups_strcasecmp(ext
, ".ps.gz"))
489 set_variable(outfile
, &vars
, "filetype", "application/postscript");
490 else if (!_cups_strcasecmp(ext
, ".pwg") ||
491 !_cups_strcasecmp(ext
, ".pwg.gz") ||
492 !_cups_strcasecmp(ext
, ".ras") ||
493 !_cups_strcasecmp(ext
, ".ras.gz"))
494 set_variable(outfile
, &vars
, "filetype", "image/pwg-raster");
495 else if (!_cups_strcasecmp(ext
, ".tif") ||
496 !_cups_strcasecmp(ext
, ".tiff"))
497 set_variable(outfile
, &vars
, "filetype", "image/tiff");
498 else if (!_cups_strcasecmp(ext
, ".txt") ||
499 !_cups_strcasecmp(ext
, ".txt.gz"))
500 set_variable(outfile
, &vars
, "filetype", "text/plain");
501 else if (!_cups_strcasecmp(ext
, ".urf") ||
502 !_cups_strcasecmp(ext
, ".urf.gz"))
503 set_variable(outfile
, &vars
, "filetype", "image/urf");
504 else if (!_cups_strcasecmp(ext
, ".xps"))
505 set_variable(outfile
, &vars
, "filetype", "application/openxps");
507 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
512 * Use the "auto-type" MIME media type...
515 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
519 case 'i' : /* Test every N seconds */
524 _cupsLangPuts(stderr
,
525 _("ipptool: Missing seconds for \"-i\"."));
530 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
534 _cupsLangPuts(stderr
,
535 _("ipptool: Invalid seconds for \"-i\"."));
540 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
542 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
547 case 'l' : /* List as a table */
548 Output
= _CUPS_OUTPUT_LIST
;
551 case 'n' : /* Repeat count */
556 _cupsLangPuts(stderr
,
557 _("ipptool: Missing count for \"-n\"."));
561 repeat
= atoi(argv
[i
]);
563 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
565 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
570 case 'q' : /* Be quiet */
571 Output
= _CUPS_OUTPUT_QUIET
;
574 case 't' : /* CUPS test output */
575 Output
= _CUPS_OUTPUT_TEST
;
578 case 'v' : /* Be verbose */
583 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
589 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
591 || !strncmp(argv
[i
], "ipps://", 7)
592 || !strncmp(argv
[i
], "https://", 8)
593 #endif /* HAVE_SSL */
602 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
607 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
608 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
609 #endif /* HAVE_SSL */
612 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
613 vars
.scheme
, sizeof(vars
.scheme
),
614 vars
.userpass
, sizeof(vars
.userpass
),
615 vars
.hostname
, sizeof(vars
.hostname
),
617 vars
.resource
, sizeof(vars
.resource
));
619 if (uri_status
!= HTTP_URI_OK
)
621 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
625 if (vars
.userpass
[0])
627 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
630 Username
= vars
.userpass
;
631 cupsSetPasswordCB(password_cb
);
632 set_variable(outfile
, &vars
, "uriuser", vars
.userpass
);
635 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
636 vars
.hostname
, vars
.port
, vars
.resource
);
647 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
651 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
653 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
657 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
659 if (access(testname
, 0))
667 if (!do_tests(outfile
, &vars
, testfile
))
672 if (!vars
.uri
|| !testfile
)
676 * Loop if the interval is set...
679 if (Output
== _CUPS_OUTPUT_PLIST
)
680 print_xml_trailer(outfile
, !status
, NULL
);
681 else if (interval
> 0 && repeat
> 0)
685 usleep((useconds_t
)interval
);
686 do_tests(outfile
, &vars
, testfile
);
690 else if (interval
> 0)
694 usleep((useconds_t
)interval
);
695 do_tests(outfile
, &vars
, testfile
);
699 if ((Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
)) && TestCount
> 1)
702 * Show a summary report if there were multiple tests...
705 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
706 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
707 100 * (PassCount
+ SkipCount
) / TestCount
);
719 * 'add_stringf()' - Add a formatted string to an array.
723 add_stringf(cups_array_t
*a
, /* I - Array */
724 const char *s
, /* I - Printf-style format string */
725 ...) /* I - Additional args as needed */
727 char buffer
[10240]; /* Format buffer */
728 va_list ap
; /* Argument pointer */
732 * Don't bother is the array is NULL...
739 * Format the message...
743 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
747 * Add it to the array...
750 cupsArrayAdd(a
, buffer
);
755 * 'compare_vars()' - Compare two variables.
758 static int /* O - Result of comparison */
759 compare_vars(_cups_var_t
*a
, /* I - First variable */
760 _cups_var_t
*b
) /* I - Second variable */
762 return (_cups_strcasecmp(a
->name
, b
->name
));
767 * 'do_tests()' - Do tests as specified in the test file.
770 static int /* 1 = success, 0 = failure */
771 do_tests(FILE *outfile
, /* I - Output file */
772 _cups_vars_t
*vars
, /* I - Variables */
773 const char *testfile
) /* I - Test file to use */
775 int i
, /* Looping var */
776 linenum
, /* Current line number */
777 pass
, /* Did we pass the test? */
778 prev_pass
= 1, /* Did we pass the previous test? */
779 request_id
, /* Current request ID */
780 show_header
= 1, /* Show the test header? */
781 ignore_errors
, /* Ignore test failures? */
782 skip_previous
= 0, /* Skip on previous test failure? */
783 repeat_count
, /* Repeat count */
784 repeat_interval
, /* Repeat interval */
785 repeat_prev
, /* Previous repeat interval */
786 repeat_test
; /* Repeat a test? */
787 http_t
*http
= NULL
; /* HTTP connection to server */
788 FILE *fp
= NULL
; /* Test file */
789 char resource
[512], /* Resource for request */
790 token
[1024], /* Token from file */
791 *tokenptr
, /* Pointer into token */
792 temp
[1024], /* Temporary string */
793 buffer
[8192], /* Copy buffer */
794 compression
[16]; /* COMPRESSION value */
795 ipp_t
*request
= NULL
, /* IPP request */
796 *response
= NULL
; /* IPP response */
797 size_t length
; /* Length of IPP request */
798 http_status_t status
; /* HTTP status */
799 cups_file_t
*reqfile
; /* File to send */
800 ssize_t bytes
; /* Bytes read/written */
801 char attr
[128]; /* Attribute name */
802 ipp_op_t op
; /* Operation */
803 ipp_tag_t group
; /* Current group */
804 ipp_tag_t value
; /* Current value type */
805 ipp_attribute_t
*attrptr
, /* Attribute pointer */
806 *found
, /* Found attribute */
807 *lastcol
= NULL
; /* Last collection attribute */
808 char name
[1024], /* Name of test */
809 file_id
[1024], /* File identifier */
810 test_id
[1024]; /* Test identifier */
811 char filename
[1024]; /* Filename */
812 _cups_transfer_t transfer
; /* To chunk or not to chunk */
813 int version
, /* IPP version number to use */
814 skip_test
; /* Skip this test? */
815 int num_statuses
= 0; /* Number of valid status codes */
816 _cups_status_t statuses
[100], /* Valid status codes */
817 *last_status
; /* Last STATUS (for predicates) */
818 int num_expects
= 0; /* Number of expected attributes */
819 _cups_expect_t expects
[200], /* Expected attributes */
820 *expect
, /* Current expected attribute */
821 *last_expect
; /* Last EXPECT (for predicates) */
822 int num_displayed
= 0; /* Number of displayed attributes */
823 char *displayed
[200]; /* Displayed attributes */
824 size_t widths
[200]; /* Width of columns */
825 cups_array_t
*a
, /* Duplicate attribute array */
826 *errors
= NULL
; /* Errors array */
827 const char *error
; /* Current error */
831 * Open the test file...
834 if ((fp
= fopen(testfile
, "r")) == NULL
)
836 print_fatal_error(outfile
, "Unable to open test file %s - %s", testfile
,
843 * Connect to the server...
846 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
847 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
849 print_fatal_error(outfile
, "Unable to connect to %s on port %d - %s", vars
->hostname
,
850 vars
->port
, cupsLastErrorString());
856 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
857 "deflate, gzip, identity");
859 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
860 #endif /* HAVE_LIBZ */
862 if (vars
->timeout
> 0.0)
863 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
869 CUPS_SRAND(time(NULL
));
871 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
872 (cups_afree_func_t
)free
);
876 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
878 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
881 * Expect an open brace...
884 if (!strcmp(token
, "DEFINE"))
890 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
891 get_token(fp
, temp
, sizeof(temp
), &linenum
))
893 expand_variables(vars
, token
, temp
, sizeof(token
));
894 set_variable(outfile
, vars
, attr
, token
);
898 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
906 else if (!strcmp(token
, "DEFINE-DEFAULT"))
909 * DEFINE-DEFAULT name value
912 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
913 get_token(fp
, temp
, sizeof(temp
), &linenum
))
915 expand_variables(vars
, token
, temp
, sizeof(token
));
916 if (!get_variable(vars
, attr
))
917 set_variable(outfile
, vars
, attr
, token
);
921 print_fatal_error(outfile
, "Missing DEFINE-DEFAULT name and/or value on line "
929 else if (!strcmp(token
, "FILE-ID"))
935 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
937 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
941 print_fatal_error(outfile
, "Missing FILE-ID value on line %d.", linenum
);
948 else if (!strcmp(token
, "IGNORE-ERRORS"))
955 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
956 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
958 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
962 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
969 else if (!strcmp(token
, "INCLUDE"))
976 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
979 * Map the filename to and then run the tests...
982 if (!do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
986 if (StopAfterIncludeError
)
992 print_fatal_error(outfile
, "Missing INCLUDE filename on line %d.", linenum
);
1000 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
1003 * INCLUDE-IF-DEFINED name "filename"
1004 * INCLUDE-IF-DEFINED name <filename>
1007 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1008 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1011 * Map the filename to and then run the tests...
1014 if (get_variable(vars
, attr
) &&
1015 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1019 if (StopAfterIncludeError
)
1025 print_fatal_error(outfile
, "Missing INCLUDE-IF-DEFINED name or filename on line "
1034 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1037 * INCLUDE-IF-NOT-DEFINED name "filename"
1038 * INCLUDE-IF-NOT-DEFINED name <filename>
1041 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1042 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1045 * Map the filename to and then run the tests...
1048 if (!get_variable(vars
, attr
) &&
1049 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1053 if (StopAfterIncludeError
)
1059 print_fatal_error(outfile
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1060 "line %d.", linenum
);
1068 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1071 * SKIP-IF-DEFINED variable
1074 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1076 if (get_variable(vars
, temp
))
1081 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED variable on line %d.",
1087 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1090 * SKIP-IF-NOT-DEFINED variable
1093 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1095 if (!get_variable(vars
, temp
))
1100 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1106 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1109 * STOP-AFTER-INCLUDE-ERROR yes
1110 * STOP-AFTER-INCLUDE-ERROR no
1113 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1114 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1116 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1120 print_fatal_error(outfile
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1128 else if (!strcmp(token
, "TRANSFER"))
1136 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1138 if (!strcmp(temp
, "auto"))
1139 Transfer
= _CUPS_TRANSFER_AUTO
;
1140 else if (!strcmp(temp
, "chunked"))
1141 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1142 else if (!strcmp(temp
, "length"))
1143 Transfer
= _CUPS_TRANSFER_LENGTH
;
1146 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1154 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1161 else if (!strcmp(token
, "VERSION"))
1163 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1165 if (!strcmp(temp
, "1.0"))
1167 else if (!strcmp(temp
, "1.1"))
1169 else if (!strcmp(temp
, "2.0"))
1171 else if (!strcmp(temp
, "2.1"))
1173 else if (!strcmp(temp
, "2.2"))
1177 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1184 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1191 else if (strcmp(token
, "{"))
1193 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
1199 * Initialize things...
1204 if (Output
== _CUPS_OUTPUT_PLIST
)
1205 print_xml_header(outfile
);
1206 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
1207 printf("\"%s\":\n", testfile
);
1212 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1217 group
= IPP_TAG_ZERO
;
1218 ignore_errors
= IgnoreErrors
;
1226 transfer
= Transfer
;
1227 compression
[0] = '\0';
1229 strlcpy(name
, testfile
, sizeof(name
));
1230 if (strrchr(name
, '.') != NULL
)
1231 *strrchr(name
, '.') = '\0';
1234 * Parse until we see a close brace...
1237 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1239 if (_cups_strcasecmp(token
, "COUNT") &&
1240 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1241 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1242 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1243 _cups_strcasecmp(token
, "IF-DEFINED") &&
1244 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1245 _cups_strcasecmp(token
, "IN-GROUP") &&
1246 _cups_strcasecmp(token
, "OF-TYPE") &&
1247 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1248 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1249 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1250 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1251 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1252 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1253 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1254 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1255 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1256 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1257 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1258 _cups_strcasecmp(token
, "WITH-VALUE"))
1261 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1262 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1263 _cups_strcasecmp(token
, "IF-DEFINED") &&
1264 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1265 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1266 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1267 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1270 if (!strcmp(token
, "}"))
1272 else if (!strcmp(token
, "{") && lastcol
)
1275 * Another collection value
1278 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1279 /* Collection value */
1283 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1291 else if (!strcmp(token
, "COMPRESSION"))
1295 * COMPRESSION deflate
1299 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1301 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1303 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1304 strcmp(compression
, "gzip"))
1306 if (strcmp(compression
, "none"))
1307 #endif /* HAVE_LIBZ */
1309 print_fatal_error(outfile
, "Unsupported COMPRESSION value '%s' on line %d.",
1310 compression
, linenum
);
1315 if (!strcmp(compression
, "none"))
1316 compression
[0] = '\0';
1320 print_fatal_error(outfile
, "Missing COMPRESSION value on line %d.", linenum
);
1325 else if (!strcmp(token
, "DEFINE"))
1331 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1332 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1334 expand_variables(vars
, token
, temp
, sizeof(token
));
1335 set_variable(outfile
, vars
, attr
, token
);
1339 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
1345 else if (!strcmp(token
, "IGNORE-ERRORS"))
1352 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1353 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1355 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1359 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1366 else if (!_cups_strcasecmp(token
, "NAME"))
1372 get_token(fp
, name
, sizeof(name
), &linenum
);
1374 else if (!_cups_strcasecmp(token
, "PAUSE"))
1377 * Pause with a message...
1380 get_token(fp
, token
, sizeof(token
), &linenum
);
1381 pause_message(token
);
1383 else if (!strcmp(token
, "REQUEST-ID"))
1390 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1392 if (isdigit(temp
[0] & 255))
1393 request_id
= atoi(temp
);
1394 else if (!_cups_strcasecmp(temp
, "random"))
1395 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1398 print_fatal_error(outfile
, "Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1406 print_fatal_error(outfile
, "Missing REQUEST-ID value on line %d.", linenum
);
1411 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1414 * SKIP-IF-DEFINED variable
1417 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1419 if (get_variable(vars
, temp
))
1424 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED value on line %d.",
1430 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1433 * SKIP-IF-MISSING filename
1436 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1438 expand_variables(vars
, token
, temp
, sizeof(token
));
1439 get_filename(testfile
, filename
, token
, sizeof(filename
));
1441 if (access(filename
, R_OK
))
1446 print_fatal_error(outfile
, "Missing SKIP-IF-MISSING filename on line %d.",
1452 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1455 * SKIP-IF-NOT-DEFINED variable
1458 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1460 if (!get_variable(vars
, temp
))
1465 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
1471 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1474 * SKIP-PREVIOUS-ERROR yes
1475 * SKIP-PREVIOUS-ERROR no
1478 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1479 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1481 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1485 print_fatal_error(outfile
, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1492 else if (!strcmp(token
, "TEST-ID"))
1498 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1500 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1504 print_fatal_error(outfile
, "Missing TEST-ID value on line %d.", linenum
);
1511 else if (!strcmp(token
, "TRANSFER"))
1519 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1521 if (!strcmp(temp
, "auto"))
1522 transfer
= _CUPS_TRANSFER_AUTO
;
1523 else if (!strcmp(temp
, "chunked"))
1524 transfer
= _CUPS_TRANSFER_CHUNKED
;
1525 else if (!strcmp(temp
, "length"))
1526 transfer
= _CUPS_TRANSFER_LENGTH
;
1529 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1537 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1542 else if (!_cups_strcasecmp(token
, "VERSION"))
1544 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1546 if (!strcmp(temp
, "0.0"))
1548 else if (!strcmp(temp
, "1.0"))
1550 else if (!strcmp(temp
, "1.1"))
1552 else if (!strcmp(temp
, "2.0"))
1554 else if (!strcmp(temp
, "2.1"))
1556 else if (!strcmp(temp
, "2.2"))
1560 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1567 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1572 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1578 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1580 print_fatal_error(outfile
, "Missing RESOURCE path on line %d.", linenum
);
1585 else if (!_cups_strcasecmp(token
, "OPERATION"))
1591 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1593 print_fatal_error(outfile
, "Missing OPERATION code on line %d.", linenum
);
1598 expand_variables(vars
, token
, temp
, sizeof(token
));
1600 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1601 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1603 print_fatal_error(outfile
, "Bad OPERATION code \"%s\" on line %d.", token
,
1609 else if (!_cups_strcasecmp(token
, "GROUP"))
1612 * Attribute group...
1615 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1617 print_fatal_error(outfile
, "Missing GROUP tag on line %d.", linenum
);
1622 if ((value
= ippTagValue(token
)) < 0)
1624 print_fatal_error(outfile
, "Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1630 ippAddSeparator(request
);
1634 else if (!_cups_strcasecmp(token
, "DELAY"))
1637 * Delay before operation...
1642 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1644 print_fatal_error(outfile
, "Missing DELAY value on line %d.", linenum
);
1649 expand_variables(vars
, token
, temp
, sizeof(token
));
1651 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1653 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1660 if (Output
== _CUPS_OUTPUT_TEST
)
1661 printf(" [%g second delay]\n", delay
);
1663 usleep((useconds_t
)(1000000.0 * delay
));
1666 else if (!_cups_strcasecmp(token
, "ATTR"))
1672 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1674 print_fatal_error(outfile
, "Missing ATTR value tag on line %d.", linenum
);
1679 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1681 print_fatal_error(outfile
, "Bad ATTR value tag \"%s\" on line %d.", token
,
1687 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1689 print_fatal_error(outfile
, "Missing ATTR name on line %d.", linenum
);
1694 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1696 print_fatal_error(outfile
, "Missing ATTR value on line %d.", linenum
);
1701 expand_variables(vars
, token
, temp
, sizeof(token
));
1706 case IPP_TAG_BOOLEAN
:
1707 if (!_cups_strcasecmp(token
, "true"))
1708 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1710 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1713 case IPP_TAG_INTEGER
:
1715 if (!strchr(token
, ','))
1716 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1719 int values
[100], /* Values */
1720 num_values
= 1; /* Number of values */
1722 values
[0] = (int)strtol(token
, &tokenptr
, 10);
1723 while (tokenptr
&& *tokenptr
&&
1724 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1726 if (*tokenptr
== ',')
1728 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1731 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
1735 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1738 if (!tokenptr
|| *tokenptr
)
1740 print_fatal_error(outfile
, "Bad %s value \"%s\" on line %d.",
1741 ippTagString(value
), token
, linenum
);
1747 case IPP_TAG_RESOLUTION
:
1749 int xres
, /* X resolution */
1750 yres
; /* Y resolution */
1751 char *ptr
; /* Pointer into value */
1753 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1754 if (ptr
> token
&& xres
> 0)
1757 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1760 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1761 (_cups_strcasecmp(ptr
, "dpi") &&
1762 _cups_strcasecmp(ptr
, "dpc") &&
1763 _cups_strcasecmp(ptr
, "dpcm") &&
1764 _cups_strcasecmp(ptr
, "other")))
1766 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
1772 if (!_cups_strcasecmp(ptr
, "dpi"))
1773 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1774 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1775 !_cups_strcasecmp(ptr
, "dpcm"))
1776 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1778 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1782 case IPP_TAG_RANGE
:
1784 int lowers
[4], /* Lower value */
1785 uppers
[4], /* Upper values */
1786 num_vals
; /* Number of values */
1789 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1790 lowers
+ 0, uppers
+ 0,
1791 lowers
+ 1, uppers
+ 1,
1792 lowers
+ 2, uppers
+ 2,
1793 lowers
+ 3, uppers
+ 3);
1795 if ((num_vals
& 1) || num_vals
== 0)
1797 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line "
1798 "%d.", token
, linenum
);
1803 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1808 case IPP_TAG_BEGIN_COLLECTION
:
1809 if (!strcmp(token
, "{"))
1811 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1812 /* Collection value */
1816 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1827 print_fatal_error(outfile
, "Bad ATTR collection value on line %d.",
1835 ipp_t
*col
; /* Collection value */
1836 long pos
= ftell(fp
); /* Save position of file */
1838 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1841 if (strcmp(token
, ","))
1843 fseek(fp
, pos
, SEEK_SET
);
1847 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
1849 print_fatal_error(outfile
, "Unexpected \"%s\" on line %d.", token
, linenum
);
1855 if ((col
= get_collection(outfile
, vars
, fp
, &linenum
)) == NULL
)
1858 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
1861 while (!strcmp(token
, "{"));
1864 case IPP_TAG_STRING
:
1865 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
1869 print_fatal_error(outfile
, "Unsupported ATTR value tag %s on line %d.",
1870 ippTagString(value
), linenum
);
1874 case IPP_TAG_TEXTLANG
:
1875 case IPP_TAG_NAMELANG
:
1878 case IPP_TAG_KEYWORD
:
1880 case IPP_TAG_URISCHEME
:
1881 case IPP_TAG_CHARSET
:
1882 case IPP_TAG_LANGUAGE
:
1883 case IPP_TAG_MIMETYPE
:
1884 if (!strchr(token
, ','))
1885 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1889 * Multiple string values...
1892 int num_values
; /* Number of values */
1893 char *values
[100], /* Values */
1894 *ptr
; /* Pointer to next value */
1900 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1902 if (ptr
> token
&& ptr
[-1] == '\\')
1903 _cups_strcpy(ptr
- 1, ptr
);
1907 values
[num_values
] = ptr
;
1912 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1913 NULL
, (const char **)values
);
1920 print_fatal_error(outfile
, "Unable to add attribute on line %d: %s", linenum
,
1921 cupsLastErrorString());
1926 else if (!_cups_strcasecmp(token
, "FILE"))
1932 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1934 print_fatal_error(outfile
, "Missing FILE filename on line %d.", linenum
);
1939 expand_variables(vars
, token
, temp
, sizeof(token
));
1940 get_filename(testfile
, filename
, token
, sizeof(filename
));
1942 if (access(filename
, R_OK
))
1944 print_fatal_error(outfile
, "Filename \"%s\" on line %d cannot be read.",
1946 print_fatal_error(outfile
, "Filename mapped to \"%s\".", filename
);
1951 else if (!_cups_strcasecmp(token
, "STATUS"))
1957 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1959 print_fatal_error(outfile
, "Too many STATUS's on line %d.", linenum
);
1964 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1966 print_fatal_error(outfile
, "Missing STATUS code on line %d.", linenum
);
1971 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1972 == (ipp_status_t
)-1 &&
1973 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
1975 print_fatal_error(outfile
, "Bad STATUS code \"%s\" on line %d.", token
,
1981 last_status
= statuses
+ num_statuses
;
1984 last_status
->define_match
= NULL
;
1985 last_status
->define_no_match
= NULL
;
1986 last_status
->if_defined
= NULL
;
1987 last_status
->if_not_defined
= NULL
;
1988 last_status
->repeat_limit
= 1000;
1989 last_status
->repeat_match
= 0;
1990 last_status
->repeat_no_match
= 0;
1992 else if (!_cups_strcasecmp(token
, "EXPECT"))
1995 * Expected attributes...
1998 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
2000 print_fatal_error(outfile
, "Too many EXPECT's on line %d.", linenum
);
2005 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2007 print_fatal_error(outfile
, "Missing EXPECT name on line %d.", linenum
);
2012 last_expect
= expects
+ num_expects
;
2015 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2016 last_expect
->repeat_limit
= 1000;
2018 if (token
[0] == '!')
2020 last_expect
->not_expect
= 1;
2021 last_expect
->name
= strdup(token
+ 1);
2023 else if (token
[0] == '?')
2025 last_expect
->optional
= 1;
2026 last_expect
->name
= strdup(token
+ 1);
2029 last_expect
->name
= strdup(token
);
2031 else if (!_cups_strcasecmp(token
, "COUNT"))
2033 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2035 print_fatal_error(outfile
, "Missing COUNT number on line %d.", linenum
);
2040 if ((i
= atoi(token
)) <= 0)
2042 print_fatal_error(outfile
, "Bad COUNT \"%s\" on line %d.", token
, linenum
);
2048 last_expect
->count
= i
;
2051 print_fatal_error(outfile
, "COUNT without a preceding EXPECT on line %d.",
2057 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2059 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2061 print_fatal_error(outfile
, "Missing DEFINE-MATCH variable on line %d.",
2068 last_expect
->define_match
= strdup(token
);
2069 else if (last_status
)
2070 last_status
->define_match
= strdup(token
);
2073 print_fatal_error(outfile
, "DEFINE-MATCH without a preceding EXPECT or STATUS "
2074 "on line %d.", linenum
);
2079 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2081 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2083 print_fatal_error(outfile
, "Missing DEFINE-NO-MATCH variable on line %d.",
2090 last_expect
->define_no_match
= strdup(token
);
2091 else if (last_status
)
2092 last_status
->define_no_match
= strdup(token
);
2095 print_fatal_error(outfile
, "DEFINE-NO-MATCH without a preceding EXPECT or "
2096 "STATUS on line %d.", linenum
);
2101 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2103 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2105 print_fatal_error(outfile
, "Missing DEFINE-VALUE variable on line %d.",
2112 last_expect
->define_value
= strdup(token
);
2115 print_fatal_error(outfile
, "DEFINE-VALUE without a preceding EXPECT on "
2116 "line %d.", linenum
);
2121 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2123 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2125 print_fatal_error(outfile
, "Missing OF-TYPE value tag(s) on line %d.",
2132 last_expect
->of_type
= strdup(token
);
2135 print_fatal_error(outfile
, "OF-TYPE without a preceding EXPECT on line %d.",
2141 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2143 ipp_tag_t in_group
; /* IN-GROUP value */
2146 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2148 print_fatal_error(outfile
, "Missing IN-GROUP group tag on line %d.", linenum
);
2153 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2156 else if (last_expect
)
2157 last_expect
->in_group
= in_group
;
2160 print_fatal_error(outfile
, "IN-GROUP without a preceding EXPECT on line %d.",
2166 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2168 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2170 print_fatal_error(outfile
, "Missing REPEAT-LIMIT value on line %d.", linenum
);
2174 else if (atoi(token
) <= 0)
2176 print_fatal_error(outfile
, "Bad REPEAT-LIMIT value on line %d.", linenum
);
2182 last_status
->repeat_limit
= atoi(token
);
2183 else if (last_expect
)
2184 last_expect
->repeat_limit
= atoi(token
);
2187 print_fatal_error(outfile
, "REPEAT-LIMIT without a preceding EXPECT or STATUS "
2188 "on line %d.", linenum
);
2193 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2196 last_status
->repeat_match
= 1;
2197 else if (last_expect
)
2198 last_expect
->repeat_match
= 1;
2201 print_fatal_error(outfile
, "REPEAT-MATCH without a preceding EXPECT or STATUS "
2202 "on line %d.", linenum
);
2207 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2210 last_status
->repeat_no_match
= 1;
2211 else if (last_expect
)
2212 last_expect
->repeat_no_match
= 1;
2215 print_fatal_error(outfile
, "REPEAT-NO-MATCH without a preceding EXPECT or "
2216 "STATUS on ine %d.", linenum
);
2221 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2223 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2225 print_fatal_error(outfile
, "Missing SAME-COUNT-AS name on line %d.", linenum
);
2231 last_expect
->same_count_as
= strdup(token
);
2234 print_fatal_error(outfile
, "SAME-COUNT-AS without a preceding EXPECT on line "
2240 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2242 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2244 print_fatal_error(outfile
, "Missing IF-DEFINED name on line %d.", linenum
);
2250 last_expect
->if_defined
= strdup(token
);
2251 else if (last_status
)
2252 last_status
->if_defined
= strdup(token
);
2255 print_fatal_error(outfile
, "IF-DEFINED without a preceding EXPECT or STATUS "
2256 "on line %d.", linenum
);
2261 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2263 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2265 print_fatal_error(outfile
, "Missing IF-NOT-DEFINED name on line %d.", linenum
);
2271 last_expect
->if_not_defined
= strdup(token
);
2272 else if (last_status
)
2273 last_status
->if_not_defined
= strdup(token
);
2276 print_fatal_error(outfile
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2277 "on line %d.", linenum
);
2282 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2283 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2284 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2285 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2286 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2287 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2288 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2289 !_cups_strcasecmp(token
, "WITH-VALUE"))
2293 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2294 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2295 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2296 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2297 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2298 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2299 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2300 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2301 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2303 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2304 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2307 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2309 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2317 * Expand any variables in the value and then save it.
2320 expand_variables(vars
, token
, temp
, sizeof(token
));
2322 tokenptr
= token
+ strlen(token
) - 1;
2324 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2327 * WITH-VALUE is a POSIX extended regular expression.
2330 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2331 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2333 if (last_expect
->with_value
)
2334 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2339 * WITH-VALUE is a literal value...
2342 char *ptr
; /* Pointer into value */
2344 for (ptr
= token
; *ptr
; ptr
++)
2346 if (*ptr
== '\\' && ptr
[1])
2349 * Remove \ from \foo...
2352 _cups_strcpy(ptr
, ptr
+ 1);
2356 last_expect
->with_value
= strdup(token
);
2357 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2362 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2368 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2371 * Display attributes...
2374 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2376 print_fatal_error(outfile
, "Too many DISPLAY's on line %d", linenum
);
2381 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2383 print_fatal_error(outfile
, "Missing DISPLAY name on line %d.", linenum
);
2388 displayed
[num_displayed
] = strdup(token
);
2393 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
,
2401 * Submit the IPP request...
2406 ippSetVersion(request
, version
/ 10, version
% 10);
2407 ippSetOperation(request
, op
);
2408 ippSetRequestId(request
, request_id
);
2410 if (Output
== _CUPS_OUTPUT_PLIST
)
2412 fputs("<dict>\n", outfile
);
2413 fputs("<key>Name</key>\n", outfile
);
2414 print_xml_string(outfile
, "string", name
);
2417 fputs("<key>FileId</key>\n", outfile
);
2418 print_xml_string(outfile
, "string", file_id
);
2422 fputs("<key>TestId</key>\n", outfile
);
2423 print_xml_string(outfile
, "string", test_id
);
2425 fputs("<key>Version</key>\n", outfile
);
2426 fprintf(outfile
, "<string>%d.%d</string>\n", version
/ 10, version
% 10);
2427 fputs("<key>Operation</key>\n", outfile
);
2428 print_xml_string(outfile
, "string", ippOpString(op
));
2429 fputs("<key>RequestId</key>\n", outfile
);
2430 fprintf(outfile
, "<integer>%d</integer>\n", request_id
);
2431 fputs("<key>RequestAttributes</key>\n", outfile
);
2432 fputs("<array>\n", outfile
);
2435 fputs("<dict>\n", outfile
);
2436 for (attrptr
= request
->attrs
,
2437 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2439 attrptr
= attrptr
->next
)
2440 print_attr(outfile
, Output
, attrptr
, &group
);
2441 fputs("</dict>\n", outfile
);
2443 fputs("</array>\n", outfile
);
2446 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2450 printf(" %s:\n", ippOpString(op
));
2452 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2453 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
2456 printf(" %-68.68s [", name
);
2460 if ((skip_previous
&& !prev_pass
) || skip_test
)
2467 if (Output
== _CUPS_OUTPUT_PLIST
)
2469 fputs("<key>Successful</key>\n", outfile
);
2470 fputs("<true />\n", outfile
);
2471 fputs("<key>StatusCode</key>\n", outfile
);
2472 print_xml_string(outfile
, "string", "skip");
2473 fputs("<key>ResponseAttributes</key>\n", outfile
);
2474 fputs("<dict />\n", outfile
);
2477 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2485 repeat_interval
= 1;
2492 status
= HTTP_STATUS_OK
;
2494 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2495 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2498 * Send request using chunking - a 0 length means "chunk".
2506 * Send request using content length...
2509 length
= ippLength(request
);
2511 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2514 * Read the file to get the uncompressed file size...
2517 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2518 length
+= (size_t)bytes
;
2520 cupsFileClose(reqfile
);
2525 * Send the request...
2532 if (status
!= HTTP_STATUS_ERROR
)
2534 while (!response
&& !Cancel
&& prev_pass
)
2536 status
= cupsSendRequest(http
, request
, resource
, length
);
2540 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2541 #endif /* HAVE_LIBZ */
2543 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2544 request
->state
== IPP_DATA
&& filename
[0])
2546 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2549 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2550 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2553 cupsFileClose(reqfile
);
2557 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2559 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2561 status
= HTTP_STATUS_ERROR
;
2566 * Get the server's response...
2569 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2571 response
= cupsGetResponse(http
, resource
);
2572 status
= httpGetStatus(http
);
2575 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2577 http
->error
!= WSAETIMEDOUT
)
2579 http
->error
!= ETIMEDOUT
)
2582 if (httpReconnect(http
))
2585 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2590 else if (status
!= HTTP_STATUS_OK
)
2594 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2602 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2604 http
->error
!= WSAETIMEDOUT
)
2606 http
->error
!= ETIMEDOUT
)
2609 if (httpReconnect(http
))
2612 else if (status
== HTTP_STATUS_ERROR
)
2615 httpReconnect(http
);
2619 else if (status
!= HTTP_STATUS_OK
)
2626 * Check results of request...
2629 cupsArrayClear(errors
);
2631 if (http
->version
!= HTTP_1_1
)
2632 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2633 http
->version
% 100);
2638 * No response, log error...
2641 add_stringf(errors
, "IPP request failed with status %s (%s)",
2642 ippErrorString(cupsLastError()),
2643 cupsLastErrorString());
2648 * Collect common attribute values...
2651 if ((attrptr
= ippFindAttribute(response
, "job-id",
2652 IPP_TAG_INTEGER
)) != NULL
)
2654 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2655 set_variable(outfile
, vars
, "job-id", temp
);
2658 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2659 IPP_TAG_URI
)) != NULL
)
2660 set_variable(outfile
, vars
, "job-uri", attrptr
->values
[0].string
.text
);
2662 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2663 IPP_TAG_INTEGER
)) != NULL
)
2665 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2666 set_variable(outfile
, vars
, "notify-subscription-id", temp
);
2670 * Check response, validating groups and attributes and logging errors
2674 if (response
->state
!= IPP_DATA
)
2676 "Missing end-of-attributes-tag in response "
2677 "(RFC 2910 section 3.5.1)");
2680 (response
->request
.status
.version
[0] != (version
/ 10) ||
2681 response
->request
.status
.version
[1] != (version
% 10)))
2683 "Bad version %d.%d in response - expected %d.%d "
2684 "(RFC 2911 section 3.1.8).",
2685 response
->request
.status
.version
[0],
2686 response
->request
.status
.version
[1],
2687 version
/ 10, version
% 10);
2689 if (response
->request
.status
.request_id
!= request_id
)
2691 "Bad request ID %d in response - expected %d "
2692 "(RFC 2911 section 3.1.1)",
2693 response
->request
.status
.request_id
, request_id
);
2695 attrptr
= response
->attrs
;
2698 "Missing first attribute \"attributes-charset "
2699 "(charset)\" in group operation-attributes-tag "
2700 "(RFC 2911 section 3.1.4).");
2703 if (!attrptr
->name
||
2704 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2705 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2706 attrptr
->num_values
!= 1 ||
2707 strcmp(attrptr
->name
, "attributes-charset"))
2709 "Bad first attribute \"%s (%s%s)\" in group %s, "
2710 "expected \"attributes-charset (charset)\" in "
2711 "group operation-attributes-tag (RFC 2911 section "
2713 attrptr
->name
? attrptr
->name
: "(null)",
2714 attrptr
->num_values
> 1 ? "1setOf " : "",
2715 ippTagString(attrptr
->value_tag
),
2716 ippTagString(attrptr
->group_tag
));
2718 attrptr
= attrptr
->next
;
2721 "Missing second attribute \"attributes-natural-"
2722 "language (naturalLanguage)\" in group "
2723 "operation-attributes-tag (RFC 2911 section "
2725 else if (!attrptr
->name
||
2726 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2727 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2728 attrptr
->num_values
!= 1 ||
2729 strcmp(attrptr
->name
, "attributes-natural-language"))
2731 "Bad first attribute \"%s (%s%s)\" in group %s, "
2732 "expected \"attributes-natural-language "
2733 "(naturalLanguage)\" in group "
2734 "operation-attributes-tag (RFC 2911 section "
2736 attrptr
->name
? attrptr
->name
: "(null)",
2737 attrptr
->num_values
> 1 ? "1setOf " : "",
2738 ippTagString(attrptr
->value_tag
),
2739 ippTagString(attrptr
->group_tag
));
2742 if ((attrptr
= ippFindAttribute(response
, "status-message",
2743 IPP_TAG_ZERO
)) != NULL
)
2745 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2747 "status-message (text(255)) has wrong value tag "
2748 "%s (RFC 2911 section 3.1.6.2).",
2749 ippTagString(attrptr
->value_tag
));
2750 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2752 "status-message (text(255)) has wrong group tag "
2753 "%s (RFC 2911 section 3.1.6.2).",
2754 ippTagString(attrptr
->group_tag
));
2755 if (attrptr
->num_values
!= 1)
2757 "status-message (text(255)) has %d values "
2758 "(RFC 2911 section 3.1.6.2).",
2759 attrptr
->num_values
);
2760 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2761 strlen(attrptr
->values
[0].string
.text
) > 255)
2763 "status-message (text(255)) has bad length %d"
2764 " (RFC 2911 section 3.1.6.2).",
2765 (int)strlen(attrptr
->values
[0].string
.text
));
2768 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2769 IPP_TAG_ZERO
)) != NULL
)
2771 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2773 "detailed-status-message (text(MAX)) has wrong "
2774 "value tag %s (RFC 2911 section 3.1.6.3).",
2775 ippTagString(attrptr
->value_tag
));
2776 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2778 "detailed-status-message (text(MAX)) has wrong "
2779 "group tag %s (RFC 2911 section 3.1.6.3).",
2780 ippTagString(attrptr
->group_tag
));
2781 if (attrptr
->num_values
!= 1)
2783 "detailed-status-message (text(MAX)) has %d values"
2784 " (RFC 2911 section 3.1.6.3).",
2785 attrptr
->num_values
);
2786 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2787 strlen(attrptr
->values
[0].string
.text
) > 1023)
2789 "detailed-status-message (text(MAX)) has bad "
2790 "length %d (RFC 2911 section 3.1.6.3).",
2791 (int)strlen(attrptr
->values
[0].string
.text
));
2794 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2796 for (attrptr
= response
->attrs
,
2797 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2799 attrptr
= attrptr
->next
)
2801 if (attrptr
->group_tag
!= group
)
2803 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2806 switch (attrptr
->group_tag
)
2811 case IPP_TAG_OPERATION
:
2815 case IPP_TAG_UNSUPPORTED_GROUP
:
2816 if (group
!= IPP_TAG_OPERATION
)
2821 case IPP_TAG_PRINTER
:
2822 if (group
!= IPP_TAG_OPERATION
&&
2823 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2827 case IPP_TAG_SUBSCRIPTION
:
2828 if (group
> attrptr
->group_tag
&&
2829 group
!= IPP_TAG_DOCUMENT
)
2834 if (group
> attrptr
->group_tag
)
2840 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2841 ippTagString(attrptr
->group_tag
),
2842 ippTagString(group
));
2844 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2845 group
= attrptr
->group_tag
;
2848 validate_attr(outfile
, errors
, attrptr
);
2852 if (cupsArrayFind(a
, attrptr
->name
))
2853 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2854 attrptr
->name
, ippTagString(group
));
2856 cupsArrayAdd(a
, attrptr
->name
);
2863 * Now check the test-defined expected status-code and attribute
2867 for (i
= 0; i
< num_statuses
; i
++)
2869 if (statuses
[i
].if_defined
&&
2870 !get_variable(vars
, statuses
[i
].if_defined
))
2873 if (statuses
[i
].if_not_defined
&&
2874 get_variable(vars
, statuses
[i
].if_not_defined
))
2877 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2879 if (statuses
[i
].repeat_match
&&
2880 repeat_count
< statuses
[i
].repeat_limit
)
2883 if (statuses
[i
].define_match
)
2884 set_variable(outfile
, vars
, statuses
[i
].define_match
, "1");
2890 if (statuses
[i
].repeat_no_match
&&
2891 repeat_count
< statuses
[i
].repeat_limit
)
2894 if (statuses
[i
].define_no_match
)
2896 set_variable(outfile
, vars
, statuses
[i
].define_no_match
, "1");
2902 if (i
== num_statuses
&& num_statuses
> 0)
2904 for (i
= 0; i
< num_statuses
; i
++)
2906 if (statuses
[i
].if_defined
&&
2907 !get_variable(vars
, statuses
[i
].if_defined
))
2910 if (statuses
[i
].if_not_defined
&&
2911 get_variable(vars
, statuses
[i
].if_not_defined
))
2914 if (!statuses
[i
].repeat_match
)
2915 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2916 ippErrorString(statuses
[i
].status
),
2917 ippErrorString(cupsLastError()));
2920 if ((attrptr
= ippFindAttribute(response
, "status-message",
2921 IPP_TAG_TEXT
)) != NULL
)
2922 add_stringf(errors
, "status-message=\"%s\"",
2923 attrptr
->values
[0].string
.text
);
2926 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2928 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2931 if (expect
->if_not_defined
&&
2932 get_variable(vars
, expect
->if_not_defined
))
2935 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2937 if ((found
&& expect
->not_expect
) ||
2938 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2939 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2940 (found
&& expect
->in_group
&&
2941 found
->group_tag
!= expect
->in_group
))
2943 if (expect
->define_no_match
)
2944 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
2945 else if (!expect
->define_match
&& !expect
->define_value
)
2947 if (found
&& expect
->not_expect
)
2948 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2949 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2950 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2953 if (!expect_matches(expect
, found
->value_tag
))
2954 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2955 expect
->name
, expect
->of_type
,
2956 ippTagString(found
->value_tag
));
2958 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2959 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2960 expect
->name
, ippTagString(expect
->in_group
),
2961 ippTagString(found
->group_tag
));
2965 if (expect
->repeat_no_match
&&
2966 repeat_count
< expect
->repeat_limit
)
2973 ippAttributeString(found
, buffer
, sizeof(buffer
));
2976 !with_value(outfile
, NULL
, expect
->with_value
, expect
->with_flags
, found
,
2977 buffer
, sizeof(buffer
)))
2979 if (expect
->define_no_match
)
2980 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
2981 else if (!expect
->define_match
&& !expect
->define_value
&&
2982 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2984 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
2985 add_stringf(errors
, "EXPECTED: %s %s /%s/",
2987 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2988 "WITH-ALL-VALUES" : "WITH-VALUE",
2989 expect
->with_value
);
2991 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2993 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
2994 "WITH-ALL-VALUES" : "WITH-VALUE",
2995 expect
->with_value
);
2997 with_value(outfile
, errors
, expect
->with_value
, expect
->with_flags
, found
,
2998 buffer
, sizeof(buffer
));
3001 if (expect
->repeat_no_match
&&
3002 repeat_count
< expect
->repeat_limit
)
3008 if (found
&& expect
->count
> 0 &&
3009 found
->num_values
!= expect
->count
)
3011 if (expect
->define_no_match
)
3012 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3013 else if (!expect
->define_match
&& !expect
->define_value
)
3015 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
3016 expect
->count
, found
->num_values
);
3019 if (expect
->repeat_no_match
&&
3020 repeat_count
< expect
->repeat_limit
)
3026 if (found
&& expect
->same_count_as
)
3028 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3031 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3033 if (expect
->define_no_match
)
3034 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3035 else if (!expect
->define_match
&& !expect
->define_value
)
3039 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3040 "(not returned)", expect
->name
,
3041 found
->num_values
, expect
->same_count_as
);
3042 else if (attrptr
->num_values
!= found
->num_values
)
3044 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3045 "(%d values)", expect
->name
, found
->num_values
,
3046 expect
->same_count_as
, attrptr
->num_values
);
3049 if (expect
->repeat_no_match
&&
3050 repeat_count
< expect
->repeat_limit
)
3057 if (found
&& expect
->define_match
)
3058 set_variable(outfile
, vars
, expect
->define_match
, "1");
3060 if (found
&& expect
->define_value
)
3061 set_variable(outfile
, vars
, expect
->define_value
, buffer
);
3063 if (found
&& expect
->repeat_match
&&
3064 repeat_count
< expect
->repeat_limit
)
3070 * If we are going to repeat this test, sleep 1 second so we don't flood
3071 * the printer with requests...
3076 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3078 printf("%04d]\n", repeat_count
);
3082 sleep((unsigned)repeat_interval
);
3083 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3085 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3087 printf(" %-68.68s [", name
);
3092 while (repeat_test
);
3098 if (cupsArrayCount(errors
) > 0)
3099 prev_pass
= pass
= 0;
3106 if (Output
== _CUPS_OUTPUT_PLIST
)
3108 fputs("<key>Successful</key>\n", outfile
);
3109 fputs(prev_pass
? "<true />\n" : "<false />\n", outfile
);
3110 fputs("<key>StatusCode</key>\n", outfile
);
3111 print_xml_string(outfile
, "string", ippErrorString(cupsLastError()));
3112 fputs("<key>ResponseAttributes</key>\n", outfile
);
3113 fputs("<array>\n", outfile
);
3114 fputs("<dict>\n", outfile
);
3115 for (attrptr
= response
? response
->attrs
: NULL
,
3116 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3118 attrptr
= attrptr
->next
)
3119 print_attr(outfile
, Output
, attrptr
, &group
);
3120 fputs("</dict>\n", outfile
);
3121 fputs("</array>\n", outfile
);
3124 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3126 puts(prev_pass
? "PASS]" : "FAIL]");
3128 if (!prev_pass
|| (Verbosity
&& response
))
3130 printf(" RECEIVED: %lu bytes in response\n",
3131 (unsigned long)ippLength(response
));
3132 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3133 cupsLastErrorString());
3137 for (attrptr
= response
->attrs
;
3139 attrptr
= attrptr
->next
)
3140 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3144 else if (!prev_pass
)
3145 fprintf(stderr
, "%s\n", cupsLastErrorString());
3147 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3150 size_t width
; /* Length of value */
3152 for (i
= 0; i
< num_displayed
; i
++)
3154 widths
[i
] = strlen(displayed
[i
]);
3156 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3158 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3161 width
= ippAttributeString(attrptr
, NULL
, 0);
3162 if (width
> widths
[i
])
3167 if (Output
== _CUPS_OUTPUT_CSV
)
3168 print_csv(outfile
, NULL
, num_displayed
, displayed
, widths
);
3170 print_line(outfile
, NULL
, num_displayed
, displayed
, widths
);
3172 attrptr
= response
->attrs
;
3176 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3177 attrptr
= attrptr
->next
;
3181 if (Output
== _CUPS_OUTPUT_CSV
)
3182 print_csv(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3184 print_line(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3186 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3187 attrptr
= attrptr
->next
;
3191 else if (!prev_pass
)
3193 if (Output
== _CUPS_OUTPUT_PLIST
)
3195 fputs("<key>Errors</key>\n", outfile
);
3196 fputs("<array>\n", outfile
);
3198 for (error
= (char *)cupsArrayFirst(errors
);
3200 error
= (char *)cupsArrayNext(errors
))
3201 print_xml_string(outfile
, "string", error
);
3203 fputs("</array>\n", outfile
);
3206 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3208 for (error
= (char *)cupsArrayFirst(errors
);
3210 error
= (char *)cupsArrayNext(errors
))
3211 printf(" %s\n", error
);
3215 if (num_displayed
> 0 && !Verbosity
&& response
&& Output
== _CUPS_OUTPUT_TEST
)
3217 for (attrptr
= response
->attrs
;
3219 attrptr
= attrptr
->next
)
3223 for (i
= 0; i
< num_displayed
; i
++)
3225 if (!strcmp(displayed
[i
], attrptr
->name
))
3227 print_attr(outfile
, Output
, attrptr
, NULL
);
3237 if (Output
== _CUPS_OUTPUT_PLIST
)
3238 fputs("</dict>\n", outfile
);
3242 ippDelete(response
);
3245 for (i
= 0; i
< num_statuses
; i
++)
3247 if (statuses
[i
].if_defined
)
3248 free(statuses
[i
].if_defined
);
3249 if (statuses
[i
].if_not_defined
)
3250 free(statuses
[i
].if_not_defined
);
3251 if (statuses
[i
].define_match
)
3252 free(statuses
[i
].define_match
);
3253 if (statuses
[i
].define_no_match
)
3254 free(statuses
[i
].define_no_match
);
3258 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3261 if (expect
->of_type
)
3262 free(expect
->of_type
);
3263 if (expect
->same_count_as
)
3264 free(expect
->same_count_as
);
3265 if (expect
->if_defined
)
3266 free(expect
->if_defined
);
3267 if (expect
->if_not_defined
)
3268 free(expect
->if_not_defined
);
3269 if (expect
->with_value
)
3270 free(expect
->with_value
);
3271 if (expect
->define_match
)
3272 free(expect
->define_match
);
3273 if (expect
->define_no_match
)
3274 free(expect
->define_no_match
);
3275 if (expect
->define_value
)
3276 free(expect
->define_value
);
3280 for (i
= 0; i
< num_displayed
; i
++)
3284 if (!ignore_errors
&& !prev_pass
)
3290 cupsArrayDelete(errors
);
3297 ippDelete(response
);
3299 for (i
= 0; i
< num_statuses
; i
++)
3301 if (statuses
[i
].if_defined
)
3302 free(statuses
[i
].if_defined
);
3303 if (statuses
[i
].if_not_defined
)
3304 free(statuses
[i
].if_not_defined
);
3305 if (statuses
[i
].define_match
)
3306 free(statuses
[i
].define_match
);
3307 if (statuses
[i
].define_no_match
)
3308 free(statuses
[i
].define_no_match
);
3311 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3314 if (expect
->of_type
)
3315 free(expect
->of_type
);
3316 if (expect
->same_count_as
)
3317 free(expect
->same_count_as
);
3318 if (expect
->if_defined
)
3319 free(expect
->if_defined
);
3320 if (expect
->if_not_defined
)
3321 free(expect
->if_not_defined
);
3322 if (expect
->with_value
)
3323 free(expect
->with_value
);
3324 if (expect
->define_match
)
3325 free(expect
->define_match
);
3326 if (expect
->define_no_match
)
3327 free(expect
->define_no_match
);
3328 if (expect
->define_value
)
3329 free(expect
->define_value
);
3332 for (i
= 0; i
< num_displayed
; i
++)
3340 * 'expand_variables()' - Expand variables in a string.
3344 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3345 char *dst
, /* I - Destination string buffer */
3346 const char *src
, /* I - Source string */
3347 size_t dstsize
) /* I - Size of destination buffer */
3349 char *dstptr
, /* Pointer into destination */
3350 *dstend
, /* End of destination */
3351 temp
[256], /* Temporary string */
3352 *tempptr
; /* Pointer into temporary string */
3353 const char *value
; /* Value to substitute */
3357 dstend
= dst
+ dstsize
- 1;
3359 while (*src
&& dstptr
< dstend
)
3364 * Substitute a string/number...
3367 if (!strncmp(src
, "$$", 2))
3372 else if (!strncmp(src
, "$ENV[", 5))
3374 strlcpy(temp
, src
+ 5, sizeof(temp
));
3376 for (tempptr
= temp
; *tempptr
; tempptr
++)
3377 if (*tempptr
== ']')
3383 value
= getenv(temp
);
3384 src
+= tempptr
- temp
+ 5;
3388 strlcpy(temp
, src
+ 1, sizeof(temp
));
3390 for (tempptr
= temp
; *tempptr
; tempptr
++)
3391 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3397 if (!strcmp(temp
, "uri"))
3399 else if (!strcmp(temp
, "filename"))
3400 value
= vars
->filename
;
3401 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3402 value
= vars
->scheme
;
3403 else if (!strcmp(temp
, "username"))
3404 value
= vars
->userpass
;
3405 else if (!strcmp(temp
, "hostname"))
3406 value
= vars
->hostname
;
3407 else if (!strcmp(temp
, "port"))
3409 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3412 else if (!strcmp(temp
, "resource"))
3413 value
= vars
->resource
;
3414 else if (!strcmp(temp
, "user"))
3417 value
= get_variable(vars
, temp
);
3419 src
+= tempptr
- temp
+ 1;
3429 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3430 dstptr
+= strlen(dstptr
);
3442 * 'expect_matches()' - Return true if the tag matches the specification.
3445 static int /* O - 1 if matches, 0 otherwise */
3447 _cups_expect_t
*expect
, /* I - Expected attribute */
3448 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3450 int match
; /* Match? */
3451 char *of_type
, /* Type name to match */
3452 *next
, /* Next name to match */
3453 sep
; /* Separator character */
3457 * If we don't expect a particular type, return immediately...
3460 if (!expect
->of_type
)
3464 * Parse the "of_type" value since the string can contain multiple attribute
3465 * types separated by "," or "|"...
3468 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3471 * Find the next separator, and set it (temporarily) to nul if present.
3474 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3476 if ((sep
= *next
) != '\0')
3480 * Support some meta-types to make it easier to write the test file.
3483 if (!strcmp(of_type
, "text"))
3484 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3485 else if (!strcmp(of_type
, "name"))
3486 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3487 else if (!strcmp(of_type
, "collection"))
3488 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3490 match
= value_tag
== ippTagValue(of_type
);
3493 * Restore the separator if we have one...
3505 * 'get_collection()' - Get a collection value from the current test file.
3508 static ipp_t
* /* O - Collection value */
3509 get_collection(FILE *outfile
, /* I - Output file */
3510 _cups_vars_t
*vars
, /* I - Variables */
3511 FILE *fp
, /* I - File to read from */
3512 int *linenum
) /* IO - Line number */
3514 char token
[1024], /* Token from file */
3515 temp
[1024], /* Temporary string */
3516 attr
[128]; /* Attribute name */
3517 ipp_tag_t value
; /* Current value type */
3518 ipp_t
*col
= ippNew(); /* Collection value */
3519 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3522 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3524 if (!strcmp(token
, "}"))
3526 else if (!strcmp(token
, "{") && lastcol
)
3529 * Another collection value
3532 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3533 /* Collection value */
3536 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3540 else if (!_cups_strcasecmp(token
, "MEMBER"))
3548 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3550 print_fatal_error(outfile
, "Missing MEMBER value tag on line %d.", *linenum
);
3554 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3556 print_fatal_error(outfile
, "Bad MEMBER value tag \"%s\" on line %d.", token
,
3561 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3563 print_fatal_error(outfile
, "Missing MEMBER name on line %d.", *linenum
);
3567 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3569 print_fatal_error(outfile
, "Missing MEMBER value on line %d.", *linenum
);
3573 expand_variables(vars
, token
, temp
, sizeof(token
));
3577 case IPP_TAG_BOOLEAN
:
3578 if (!_cups_strcasecmp(token
, "true"))
3579 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3581 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3584 case IPP_TAG_INTEGER
:
3586 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3589 case IPP_TAG_RESOLUTION
:
3591 int xres
, /* X resolution */
3592 yres
; /* Y resolution */
3593 char units
[6]; /* Units */
3595 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3596 (_cups_strcasecmp(units
, "dpi") &&
3597 _cups_strcasecmp(units
, "dpc") &&
3598 _cups_strcasecmp(units
, "dpcm") &&
3599 _cups_strcasecmp(units
, "other")))
3601 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
3606 if (!_cups_strcasecmp(units
, "dpi"))
3607 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3608 else if (!_cups_strcasecmp(units
, "dpc") ||
3609 !_cups_strcasecmp(units
, "dpcm"))
3610 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3612 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3616 case IPP_TAG_RANGE
:
3618 int lowers
[4], /* Lower value */
3619 uppers
[4], /* Upper values */
3620 num_vals
; /* Number of values */
3623 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3624 lowers
+ 0, uppers
+ 0,
3625 lowers
+ 1, uppers
+ 1,
3626 lowers
+ 2, uppers
+ 2,
3627 lowers
+ 3, uppers
+ 3);
3629 if ((num_vals
& 1) || num_vals
== 0)
3631 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line %d.",
3636 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3641 case IPP_TAG_BEGIN_COLLECTION
:
3642 if (!strcmp(token
, "{"))
3644 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3645 /* Collection value */
3649 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3657 print_fatal_error(outfile
, "Bad collection value on line %d.", *linenum
);
3661 case IPP_TAG_STRING
:
3662 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3666 if (!strchr(token
, ','))
3667 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3671 * Multiple string values...
3674 int num_values
; /* Number of values */
3675 char *values
[100], /* Values */
3676 *ptr
; /* Pointer to next value */
3682 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3685 values
[num_values
] = ptr
;
3689 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3690 NULL
, (const char **)values
);
3700 * If we get here there was a parse error; free memory and return.
3712 * 'get_filename()' - Get a filename based on the current test file.
3715 static char * /* O - Filename */
3716 get_filename(const char *testfile
, /* I - Current test file */
3717 char *dst
, /* I - Destination filename */
3718 const char *src
, /* I - Source filename */
3719 size_t dstsize
) /* I - Size of destination buffer */
3721 char *dstptr
; /* Pointer into destination */
3722 _cups_globals_t
*cg
= _cupsGlobals();
3726 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3729 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3732 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3733 dstptr
= dst
+ strlen(dst
) - 1;
3737 else if (*src
== '/' || !strchr(testfile
, '/')
3739 || (isalpha(*src
& 255) && src
[1] == ':')
3744 * Use the path as-is...
3747 strlcpy(dst
, src
, dstsize
);
3752 * Make path relative to testfile...
3755 strlcpy(dst
, testfile
, dstsize
);
3756 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3759 dstptr
= dst
; /* Should never happen */
3761 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
3769 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3772 static char * /* O - Pointer to string */
3773 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3774 int element
, /* I - Element to fetch */
3775 int flags
, /* I - Value ("with") flags */
3776 char *buffer
, /* I - Temporary buffer */
3777 size_t bufsize
) /* I - Size of temporary buffer */
3779 char *ptr
, /* Value */
3780 scheme
[256], /* URI scheme */
3781 userpass
[256], /* Username/password */
3782 hostname
[256], /* Hostname */
3783 resource
[1024]; /* Resource */
3784 int port
; /* Port number */
3787 ptr
= attr
->values
[element
].string
.text
;
3789 if (flags
& _CUPS_WITH_HOSTNAME
)
3791 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3796 else if (flags
& _CUPS_WITH_RESOURCE
)
3798 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
3803 else if (flags
& _CUPS_WITH_SCHEME
)
3805 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3816 * 'get_token()' - Get a token from a file.
3819 static char * /* O - Token from file or NULL on EOF */
3820 get_token(FILE *fp
, /* I - File to read from */
3821 char *buf
, /* I - Buffer to read into */
3822 int buflen
, /* I - Length of buffer */
3823 int *linenum
) /* IO - Current line number */
3825 int ch
, /* Character from file */
3826 quote
; /* Quoting character */
3827 char *bufptr
, /* Pointer into buffer */
3828 *bufend
; /* End of buffer */
3834 * Skip whitespace...
3837 while (isspace(ch
= getc(fp
)))
3849 else if (ch
== '\'' || ch
== '\"')
3852 * Quoted text or regular expression...
3857 bufend
= buf
+ buflen
- 1;
3859 while ((ch
= getc(fp
)) != EOF
)
3864 * Escape next character...
3867 if (bufptr
< bufend
)
3868 *bufptr
++ = (char)ch
;
3870 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3871 *bufptr
++ = (char)ch
;
3873 else if (ch
== quote
)
3875 else if (bufptr
< bufend
)
3876 *bufptr
++ = (char)ch
;
3889 while ((ch
= getc(fp
)) != EOF
)
3895 else if (ch
== '{' || ch
== '}' || ch
== ',')
3905 * Whitespace delimited text...
3911 bufend
= buf
+ buflen
- 1;
3913 while ((ch
= getc(fp
)) != EOF
)
3914 if (isspace(ch
) || ch
== '#')
3916 else if (bufptr
< bufend
)
3917 *bufptr
++ = (char)ch
;
3921 else if (ch
== '\n')
3933 * 'get_variable()' - Get the value of a variable.
3936 static char * /* O - Value or NULL */
3937 get_variable(_cups_vars_t
*vars
, /* I - Variables */
3938 const char *name
) /* I - Variable name */
3940 _cups_var_t key
, /* Search key */
3941 *match
; /* Matching variable, if any */
3944 key
.name
= (char *)name
;
3945 match
= cupsArrayFind(vars
->vars
, &key
);
3947 return (match
? match
->value
: NULL
);
3952 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3956 static char * /* O - ISO 8601 date/time string */
3957 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
3959 time_t utctime
; /* UTC time since 1970 */
3960 struct tm
*utcdate
; /* UTC date/time */
3961 static char buffer
[255]; /* String buffer */
3964 utctime
= ippDateToTime(date
);
3965 utcdate
= gmtime(&utctime
);
3967 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3968 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
3969 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
3976 * 'password_cb()' - Password callback for authenticated tests.
3979 static const char * /* O - Password */
3980 password_cb(const char *prompt
) /* I - Prompt (unused) */
3984 if (PasswordTries
< 3)
3988 cupsSetUser(Username
);
3998 * 'pause_message()' - Display the message and pause until the user presses a key.
4002 pause_message(const char *message
) /* I - Message */
4005 HANDLE tty
; /* Console handle */
4006 DWORD mode
; /* Console mode */
4007 char key
; /* Key press */
4008 DWORD bytes
; /* Bytes read for key press */
4012 * Disable input echo and set raw input...
4015 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
4018 if (!GetConsoleMode(tty
, &mode
))
4021 if (!SetConsoleMode(tty
, 0))
4025 int tty
; /* /dev/tty - never read from stdin */
4026 struct termios original
, /* Original input mode */
4027 noecho
; /* No echo input mode */
4028 char key
; /* Current key press */
4032 * Disable input echo and set raw input...
4035 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
4038 if (tcgetattr(tty
, &original
))
4045 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
4047 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
4055 * Display the prompt...
4058 printf("%s\n---- PRESS ANY KEY ----", message
);
4066 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
4072 SetConsoleMode(tty
, mode
);
4085 tcsetattr(tty
, TCSAFLUSH
, &original
);
4090 * Erase the "press any key" prompt...
4093 fputs("\r \r", stdout
);
4099 * 'print_attr()' - Print an attribute on the screen.
4103 print_attr(FILE *outfile
, /* I - Output file */
4104 int format
, /* I - Output format */
4105 ipp_attribute_t
*attr
, /* I - Attribute to print */
4106 ipp_tag_t
*group
) /* IO - Current group */
4108 int i
; /* Looping var */
4109 ipp_attribute_t
*colattr
; /* Collection attribute */
4112 if (format
== _CUPS_OUTPUT_PLIST
)
4114 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
4116 if (attr
->group_tag
!= IPP_TAG_ZERO
)
4118 fputs("</dict>\n", outfile
);
4119 fputs("<dict>\n", outfile
);
4123 *group
= attr
->group_tag
;
4129 print_xml_string(outfile
, "key", attr
->name
);
4130 if (attr
->num_values
> 1)
4131 fputs("<array>\n", outfile
);
4133 switch (attr
->value_tag
)
4135 case IPP_TAG_INTEGER
:
4137 for (i
= 0; i
< attr
->num_values
; i
++)
4138 fprintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
4141 case IPP_TAG_BOOLEAN
:
4142 for (i
= 0; i
< attr
->num_values
; i
++)
4143 fputs(attr
->values
[i
].boolean
? "<true />\n" : "<false />\n", outfile
);
4146 case IPP_TAG_RANGE
:
4147 for (i
= 0; i
< attr
->num_values
; i
++)
4148 fprintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
4149 "<key>upper</key><integer>%d</integer></dict>\n",
4150 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4153 case IPP_TAG_RESOLUTION
:
4154 for (i
= 0; i
< attr
->num_values
; i
++)
4155 fprintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
4156 "<key>yres</key><integer>%d</integer>"
4157 "<key>units</key><string>%s</string></dict>\n",
4158 attr
->values
[i
].resolution
.xres
,
4159 attr
->values
[i
].resolution
.yres
,
4160 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4165 for (i
= 0; i
< attr
->num_values
; i
++)
4166 fprintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4169 case IPP_TAG_STRING
:
4170 for (i
= 0; i
< attr
->num_values
; i
++)
4172 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4175 fprintf(outfile
, "<data>%s</data>\n",
4176 httpEncode64_2(buffer
, sizeof(buffer
),
4177 attr
->values
[i
].unknown
.data
,
4178 attr
->values
[i
].unknown
.length
));
4184 case IPP_TAG_KEYWORD
:
4185 case IPP_TAG_CHARSET
:
4187 case IPP_TAG_MIMETYPE
:
4188 case IPP_TAG_LANGUAGE
:
4189 for (i
= 0; i
< attr
->num_values
; i
++)
4190 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
4193 case IPP_TAG_TEXTLANG
:
4194 case IPP_TAG_NAMELANG
:
4195 for (i
= 0; i
< attr
->num_values
; i
++)
4197 fputs("<dict><key>language</key><string>", outfile
);
4198 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
4199 fputs("</string><key>string</key><string>", outfile
);
4200 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
4201 fputs("</string></dict>\n", outfile
);
4205 case IPP_TAG_BEGIN_COLLECTION
:
4206 for (i
= 0; i
< attr
->num_values
; i
++)
4208 fputs("<dict>\n", outfile
);
4209 for (colattr
= attr
->values
[i
].collection
->attrs
;
4211 colattr
= colattr
->next
)
4212 print_attr(outfile
, format
, colattr
, NULL
);
4213 fputs("</dict>\n", outfile
);
4218 fprintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
4222 if (attr
->num_values
> 1)
4223 fputs("</array>\n", outfile
);
4227 char buffer
[8192]; /* Value buffer */
4229 if (format
== _CUPS_OUTPUT_TEST
)
4233 fputs(" -- separator --\n", outfile
);
4237 fprintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
4240 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4241 fprintf(outfile
, "%s\n", buffer
);
4247 * 'print_csv()' - Print a line of CSV text.
4252 FILE *outfile
, /* I - Output file */
4253 ipp_attribute_t
*attr
, /* I - First attribute for line */
4254 int num_displayed
, /* I - Number of attributes to display */
4255 char **displayed
, /* I - Attributes to display */
4256 size_t *widths
) /* I - Column widths */
4258 int i
; /* Looping var */
4259 size_t maxlength
; /* Max length of all columns */
4260 char *buffer
, /* String buffer */
4261 *bufptr
; /* Pointer into buffer */
4262 ipp_attribute_t
*current
; /* Current attribute */
4266 * Get the maximum string length we have to show and allocate...
4269 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4270 if (widths
[i
] > maxlength
)
4271 maxlength
= widths
[i
];
4275 if ((buffer
= malloc(maxlength
)) == NULL
)
4279 * Loop through the attributes to display...
4284 for (i
= 0; i
< num_displayed
; i
++)
4287 fputc(',', outfile
);
4291 for (current
= attr
; current
; current
= current
->next
)
4295 else if (!strcmp(current
->name
, displayed
[i
]))
4297 ippAttributeString(current
, buffer
, maxlength
);
4302 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4303 strchr(buffer
, '\\') != NULL
)
4305 putc('\"', outfile
);
4306 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4308 if (*bufptr
== '\\' || *bufptr
== '\"')
4309 putc('\\', outfile
);
4310 putc(*bufptr
, outfile
);
4312 putc('\"', outfile
);
4315 fputs(buffer
, outfile
);
4317 putc('\n', outfile
);
4321 for (i
= 0; i
< num_displayed
; i
++)
4326 fputs(displayed
[i
], outfile
);
4328 putc('\n', outfile
);
4336 * 'print_fatal_error()' - Print a fatal error message.
4340 print_fatal_error(FILE *outfile
, /* I - Output file */
4341 const char *s
, /* I - Printf-style format string */
4342 ...) /* I - Additional arguments as needed */
4344 char buffer
[10240]; /* Format buffer */
4345 va_list ap
; /* Pointer to arguments */
4349 * Format the error message...
4353 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4360 if (Output
== _CUPS_OUTPUT_PLIST
)
4362 print_xml_header(outfile
);
4363 print_xml_trailer(outfile
, 0, buffer
);
4366 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4371 * 'print_line()' - Print a line of formatted or CSV text.
4376 FILE *outfile
, /* I - Output file */
4377 ipp_attribute_t
*attr
, /* I - First attribute for line */
4378 int num_displayed
, /* I - Number of attributes to display */
4379 char **displayed
, /* I - Attributes to display */
4380 size_t *widths
) /* I - Column widths */
4382 int i
; /* Looping var */
4383 size_t maxlength
; /* Max length of all columns */
4384 char *buffer
; /* String buffer */
4385 ipp_attribute_t
*current
; /* Current attribute */
4389 * Get the maximum string length we have to show and allocate...
4392 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4393 if (widths
[i
] > maxlength
)
4394 maxlength
= widths
[i
];
4398 if ((buffer
= malloc(maxlength
)) == NULL
)
4402 * Loop through the attributes to display...
4407 for (i
= 0; i
< num_displayed
; i
++)
4414 for (current
= attr
; current
; current
= current
->next
)
4418 else if (!strcmp(current
->name
, displayed
[i
]))
4420 ippAttributeString(current
, buffer
, maxlength
);
4425 fprintf(outfile
, "%*s", (int)-widths
[i
], buffer
);
4427 putc('\n', outfile
);
4431 for (i
= 0; i
< num_displayed
; i
++)
4436 fprintf(outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
4438 putc('\n', outfile
);
4440 for (i
= 0; i
< num_displayed
; i
++)
4445 memset(buffer
, '-', widths
[i
]);
4446 buffer
[widths
[i
]] = '\0';
4447 fputs(buffer
, outfile
);
4449 putc('\n', outfile
);
4457 * 'print_xml_header()' - Print a standard XML plist header.
4461 print_xml_header(FILE *outfile
) /* I - Output file */
4465 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outfile
);
4466 fputs("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4467 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n", outfile
);
4468 fputs("<plist version=\"1.0\">\n", outfile
);
4469 fputs("<dict>\n", outfile
);
4470 fputs("<key>ipptoolVersion</key>\n", outfile
);
4471 fputs("<string>" CUPS_SVERSION
"</string>\n", outfile
);
4472 fputs("<key>Transfer</key>\n", outfile
);
4473 fprintf(outfile
, "<string>%s</string>\n",
4474 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4475 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4476 fputs("<key>Tests</key>\n", outfile
);
4477 fputs("<array>\n", outfile
);
4485 * 'print_xml_string()' - Print an XML string with escaping.
4489 print_xml_string(FILE *outfile
, /* I - Output file */
4490 const char *element
, /* I - Element name or NULL */
4491 const char *s
) /* I - String to print */
4494 fprintf(outfile
, "<%s>", element
);
4499 fputs("&", outfile
);
4501 fputs("<", outfile
);
4503 fputs(">", outfile
);
4504 else if ((*s
& 0xe0) == 0xc0)
4507 * Validate UTF-8 two-byte sequence...
4510 if ((s
[1] & 0xc0) != 0x80)
4517 putc(*s
++, outfile
);
4521 else if ((*s
& 0xf0) == 0xe0)
4524 * Validate UTF-8 three-byte sequence...
4527 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4534 putc(*s
++, outfile
);
4535 putc(*s
++, outfile
);
4539 else if ((*s
& 0xf8) == 0xf0)
4542 * Validate UTF-8 four-byte sequence...
4545 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4546 (s
[3] & 0xc0) != 0x80)
4553 putc(*s
++, outfile
);
4554 putc(*s
++, outfile
);
4555 putc(*s
++, outfile
);
4559 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4562 * Invalid control character...
4574 fprintf(outfile
, "</%s>\n", element
);
4579 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4583 print_xml_trailer(FILE *outfile
, /* I - Output file */
4584 int success
, /* I - 1 on success, 0 on failure */
4585 const char *message
) /* I - Error message or NULL */
4589 fputs("</array>\n", outfile
);
4590 fputs("<key>Successful</key>\n", outfile
);
4591 fputs(success
? "<true />\n" : "<false />\n", outfile
);
4594 fputs("<key>ErrorMessage</key>\n", outfile
);
4595 print_xml_string(outfile
, "string", message
);
4597 fputs("</dict>\n", outfile
);
4598 fputs("</plist>\n", outfile
);
4606 * 'set_variable()' - Set a variable value.
4610 set_variable(FILE *outfile
, /* I - Output file */
4611 _cups_vars_t
*vars
, /* I - Variables */
4612 const char *name
, /* I - Variable name */
4613 const char *value
) /* I - Value string */
4615 _cups_var_t key
, /* Search key */
4616 *var
; /* New variable */
4619 if (!_cups_strcasecmp(name
, "filename"))
4622 free(vars
->filename
);
4624 vars
->filename
= strdup(value
);
4628 key
.name
= (char *)name
;
4629 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4632 var
->value
= strdup(value
);
4634 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4636 print_fatal_error(outfile
, "Unable to allocate memory for variable \"%s\".", name
);
4641 var
->name
= strdup(name
);
4642 var
->value
= strdup(value
);
4644 cupsArrayAdd(vars
->vars
, var
);
4651 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4655 sigterm_handler(int sig
) /* I - Signal number (unused) */
4661 signal(SIGINT
, SIG_DFL
);
4662 signal(SIGTERM
, SIG_DFL
);
4668 * 'timeout_cb()' - Handle HTTP timeouts.
4671 static int /* O - 1 to continue, 0 to cancel */
4672 timeout_cb(http_t
*http
, /* I - Connection to server */
4673 void *user_data
) /* I - User data (unused) */
4675 int buffered
= 0; /* Bytes buffered but not yet sent */
4681 * If the socket still have data waiting to be sent to the printer (as can
4682 * happen if the printer runs out of paper), continue to wait until the output
4683 * buffer is empty...
4686 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4687 socklen_t len
= sizeof(buffered
); /* Size of return value */
4689 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4692 #elif defined(SIOCOUTQ) /* Others except Windows */
4693 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4696 #else /* Windows (not possible) */
4698 #endif /* SO_NWRITE */
4700 return (buffered
> 0);
4705 * 'usage()' - Show program usage.
4711 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4713 _cupsLangPuts(stderr
, _("Options:"));
4714 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4715 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4716 _cupsLangPuts(stderr
, _(" -C Send requests using "
4717 "chunking (default)."));
4718 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4720 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4721 _cupsLangPuts(stderr
, _(" -L Send requests using "
4722 "content-length."));
4723 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4725 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4726 "timeout in seconds."));
4727 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4729 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4731 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4733 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4735 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4736 "the given time interval."));
4737 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4738 "given number of times."));
4739 _cupsLangPuts(stderr
, _(" -q Run silently."));
4740 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4741 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4748 * 'validate_attr()' - Determine whether an attribute is valid.
4751 static int /* O - 1 if valid, 0 otherwise */
4752 validate_attr(FILE *outfile
, /* I - Output file */
4753 cups_array_t
*errors
, /* I - Errors array */
4754 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4756 int i
; /* Looping var */
4757 char scheme
[64], /* Scheme from URI */
4758 userpass
[256], /* Username/password from URI */
4759 hostname
[256], /* Hostname from URI */
4760 resource
[1024]; /* Resource from URI */
4761 int port
, /* Port number from URI */
4762 uri_status
, /* URI separation status */
4763 valid
= 1; /* Is the attribute valid? */
4764 const char *ptr
; /* Pointer into string */
4765 ipp_attribute_t
*colattr
; /* Collection attribute */
4766 regex_t re
; /* Regular expression */
4767 ipp_uchar_t
*date
; /* Current date value */
4778 * Validate the attribute name.
4781 for (ptr
= attr
->name
; *ptr
; ptr
++)
4782 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4785 if (*ptr
|| ptr
== attr
->name
)
4790 "\"%s\": Bad attribute name - invalid character "
4791 "(RFC 2911 section 4.1.3).", attr
->name
);
4794 if ((ptr
- attr
->name
) > 255)
4799 "\"%s\": Bad attribute name - bad length "
4800 "(RFC 2911 section 4.1.3).", attr
->name
);
4803 switch (attr
->value_tag
)
4805 case IPP_TAG_INTEGER
:
4808 case IPP_TAG_BOOLEAN
:
4809 for (i
= 0; i
< attr
->num_values
; i
++)
4811 if (attr
->values
[i
].boolean
!= 0 &&
4812 attr
->values
[i
].boolean
!= 1)
4817 "\"%s\": Bad boolen value %d "
4818 "(RFC 2911 section 4.1.11).", attr
->name
,
4819 attr
->values
[i
].boolean
);
4825 for (i
= 0; i
< attr
->num_values
; i
++)
4827 if (attr
->values
[i
].integer
< 1)
4832 "\"%s\": Bad enum value %d - out of range "
4833 "(RFC 2911 section 4.1.4).", attr
->name
,
4834 attr
->values
[i
].integer
);
4839 case IPP_TAG_STRING
:
4840 for (i
= 0; i
< attr
->num_values
; i
++)
4842 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4847 "\"%s\": Bad octetString value - bad length %d "
4848 "(RFC 2911 section 4.1.10).", attr
->name
,
4849 attr
->values
[i
].unknown
.length
);
4855 for (i
= 0; i
< attr
->num_values
; i
++)
4857 date
= attr
->values
[i
].date
;
4859 if (date
[2] < 1 || date
[2] > 12)
4864 "\"%s\": Bad dateTime month %u "
4865 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4868 if (date
[3] < 1 || date
[3] > 31)
4873 "\"%s\": Bad dateTime day %u "
4874 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4882 "\"%s\": Bad dateTime hours %u "
4883 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4891 "\"%s\": Bad dateTime minutes %u "
4892 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
4900 "\"%s\": Bad dateTime seconds %u "
4901 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
4909 "\"%s\": Bad dateTime deciseconds %u "
4910 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
4913 if (date
[8] != '-' && date
[8] != '+')
4918 "\"%s\": Bad dateTime UTC sign '%c' "
4919 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
4927 "\"%s\": Bad dateTime UTC hours %u "
4928 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
4936 "\"%s\": Bad dateTime UTC minutes %u "
4937 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
4942 case IPP_TAG_RESOLUTION
:
4943 for (i
= 0; i
< attr
->num_values
; i
++)
4945 if (attr
->values
[i
].resolution
.xres
<= 0)
4950 "\"%s\": Bad resolution value %dx%d%s - cross "
4951 "feed resolution must be positive "
4952 "(RFC 2911 section 4.1.15).", attr
->name
,
4953 attr
->values
[i
].resolution
.xres
,
4954 attr
->values
[i
].resolution
.yres
,
4955 attr
->values
[i
].resolution
.units
==
4956 IPP_RES_PER_INCH
? "dpi" :
4957 attr
->values
[i
].resolution
.units
==
4958 IPP_RES_PER_CM
? "dpcm" : "unknown");
4961 if (attr
->values
[i
].resolution
.yres
<= 0)
4966 "\"%s\": Bad resolution value %dx%d%s - feed "
4967 "resolution must be positive "
4968 "(RFC 2911 section 4.1.15).", attr
->name
,
4969 attr
->values
[i
].resolution
.xres
,
4970 attr
->values
[i
].resolution
.yres
,
4971 attr
->values
[i
].resolution
.units
==
4972 IPP_RES_PER_INCH
? "dpi" :
4973 attr
->values
[i
].resolution
.units
==
4974 IPP_RES_PER_CM
? "dpcm" : "unknown");
4977 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
4978 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
4983 "\"%s\": Bad resolution value %dx%d%s - bad "
4984 "units value (RFC 2911 section 4.1.15).",
4985 attr
->name
, attr
->values
[i
].resolution
.xres
,
4986 attr
->values
[i
].resolution
.yres
,
4987 attr
->values
[i
].resolution
.units
==
4988 IPP_RES_PER_INCH
? "dpi" :
4989 attr
->values
[i
].resolution
.units
==
4990 IPP_RES_PER_CM
? "dpcm" : "unknown");
4995 case IPP_TAG_RANGE
:
4996 for (i
= 0; i
< attr
->num_values
; i
++)
4998 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5003 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5004 "greater than upper (RFC 2911 section 4.1.13).",
5005 attr
->name
, attr
->values
[i
].range
.lower
,
5006 attr
->values
[i
].range
.upper
);
5011 case IPP_TAG_BEGIN_COLLECTION
:
5012 for (i
= 0; i
< attr
->num_values
; i
++)
5014 for (colattr
= attr
->values
[i
].collection
->attrs
;
5016 colattr
= colattr
->next
)
5018 if (!validate_attr(outfile
, NULL
, colattr
))
5025 if (colattr
&& errors
)
5027 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5031 validate_attr(outfile
, errors
, colattr
);
5032 colattr
= colattr
->next
;
5039 case IPP_TAG_TEXTLANG
:
5040 for (i
= 0; i
< attr
->num_values
; i
++)
5042 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5044 if ((*ptr
& 0xe0) == 0xc0)
5047 if ((*ptr
& 0xc0) != 0x80)
5050 else if ((*ptr
& 0xf0) == 0xe0)
5053 if ((*ptr
& 0xc0) != 0x80)
5056 if ((*ptr
& 0xc0) != 0x80)
5059 else if ((*ptr
& 0xf8) == 0xf0)
5062 if ((*ptr
& 0xc0) != 0x80)
5065 if ((*ptr
& 0xc0) != 0x80)
5068 if ((*ptr
& 0xc0) != 0x80)
5071 else if (*ptr
& 0x80)
5080 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5081 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5082 attr
->values
[i
].string
.text
);
5085 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5090 "\"%s\": Bad text value \"%s\" - bad length %d "
5091 "(RFC 2911 section 4.1.1).", attr
->name
,
5092 attr
->values
[i
].string
.text
,
5093 (int)strlen(attr
->values
[i
].string
.text
));
5099 case IPP_TAG_NAMELANG
:
5100 for (i
= 0; i
< attr
->num_values
; i
++)
5102 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5104 if ((*ptr
& 0xe0) == 0xc0)
5107 if ((*ptr
& 0xc0) != 0x80)
5110 else if ((*ptr
& 0xf0) == 0xe0)
5113 if ((*ptr
& 0xc0) != 0x80)
5116 if ((*ptr
& 0xc0) != 0x80)
5119 else if ((*ptr
& 0xf8) == 0xf0)
5122 if ((*ptr
& 0xc0) != 0x80)
5125 if ((*ptr
& 0xc0) != 0x80)
5128 if ((*ptr
& 0xc0) != 0x80)
5131 else if (*ptr
& 0x80)
5140 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5141 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5142 attr
->values
[i
].string
.text
);
5145 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5150 "\"%s\": Bad name value \"%s\" - bad length %d "
5151 "(RFC 2911 section 4.1.2).", attr
->name
,
5152 attr
->values
[i
].string
.text
,
5153 (int)strlen(attr
->values
[i
].string
.text
));
5158 case IPP_TAG_KEYWORD
:
5159 for (i
= 0; i
< attr
->num_values
; i
++)
5161 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5162 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5166 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5171 "\"%s\": Bad keyword value \"%s\" - invalid "
5172 "character (RFC 2911 section 4.1.3).",
5173 attr
->name
, attr
->values
[i
].string
.text
);
5176 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5181 "\"%s\": Bad keyword value \"%s\" - bad "
5182 "length %d (RFC 2911 section 4.1.3).",
5183 attr
->name
, attr
->values
[i
].string
.text
,
5184 (int)strlen(attr
->values
[i
].string
.text
));
5190 for (i
= 0; i
< attr
->num_values
; i
++)
5192 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5193 attr
->values
[i
].string
.text
,
5194 scheme
, sizeof(scheme
),
5195 userpass
, sizeof(userpass
),
5196 hostname
, sizeof(hostname
),
5197 &port
, resource
, sizeof(resource
));
5199 if (uri_status
< HTTP_URI_OK
)
5204 "\"%s\": Bad URI value \"%s\" - %s "
5205 "(RFC 2911 section 4.1.5).", attr
->name
,
5206 attr
->values
[i
].string
.text
,
5207 httpURIStatusString(uri_status
));
5210 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5215 "\"%s\": Bad URI value \"%s\" - bad length %d "
5216 "(RFC 2911 section 4.1.5).", attr
->name
,
5217 attr
->values
[i
].string
.text
,
5218 (int)strlen(attr
->values
[i
].string
.text
));
5223 case IPP_TAG_URISCHEME
:
5224 for (i
= 0; i
< attr
->num_values
; i
++)
5226 ptr
= attr
->values
[i
].string
.text
;
5227 if (islower(*ptr
& 255))
5229 for (ptr
++; *ptr
; ptr
++)
5230 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5231 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5235 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5240 "\"%s\": Bad uriScheme value \"%s\" - bad "
5241 "characters (RFC 2911 section 4.1.6).",
5242 attr
->name
, attr
->values
[i
].string
.text
);
5245 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5250 "\"%s\": Bad uriScheme value \"%s\" - bad "
5251 "length %d (RFC 2911 section 4.1.6).",
5252 attr
->name
, attr
->values
[i
].string
.text
,
5253 (int)strlen(attr
->values
[i
].string
.text
));
5258 case IPP_TAG_CHARSET
:
5259 for (i
= 0; i
< attr
->num_values
; i
++)
5261 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5262 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5263 isspace(*ptr
& 255))
5266 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5271 "\"%s\": Bad charset value \"%s\" - bad "
5272 "characters (RFC 2911 section 4.1.7).",
5273 attr
->name
, attr
->values
[i
].string
.text
);
5276 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5281 "\"%s\": Bad charset value \"%s\" - bad "
5282 "length %d (RFC 2911 section 4.1.7).",
5283 attr
->name
, attr
->values
[i
].string
.text
,
5284 (int)strlen(attr
->values
[i
].string
.text
));
5289 case IPP_TAG_LANGUAGE
:
5291 * The following regular expression is derived from the ABNF for
5292 * language tags in RFC 4646. All I can say is that this is the
5293 * easiest way to check the values...
5296 if ((i
= regcomp(&re
,
5298 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5300 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5301 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5302 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5303 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5304 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5306 "x(-[a-z0-9]{1,8})+" /* privateuse */
5308 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5310 REG_NOSUB
| REG_EXTENDED
)) != 0)
5312 char temp
[256]; /* Temporary error string */
5314 regerror(i
, &re
, temp
, sizeof(temp
));
5315 print_fatal_error(outfile
, "Unable to compile naturalLanguage regular "
5316 "expression: %s.", temp
);
5320 for (i
= 0; i
< attr
->num_values
; i
++)
5322 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5327 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5328 "characters (RFC 2911 section 4.1.8).",
5329 attr
->name
, attr
->values
[i
].string
.text
);
5332 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5337 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5338 "length %d (RFC 2911 section 4.1.8).",
5339 attr
->name
, attr
->values
[i
].string
.text
,
5340 (int)strlen(attr
->values
[i
].string
.text
));
5347 case IPP_TAG_MIMETYPE
:
5349 * The following regular expression is derived from the ABNF for
5350 * language tags in RFC 2045 and 4288. All I can say is that this is
5351 * the easiest way to check the values...
5354 if ((i
= regcomp(&re
,
5356 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5358 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5359 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5360 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5363 REG_NOSUB
| REG_EXTENDED
)) != 0)
5365 char temp
[256]; /* Temporary error string */
5367 regerror(i
, &re
, temp
, sizeof(temp
));
5368 print_fatal_error(outfile
, "Unable to compile mimeMediaType regular "
5369 "expression: %s.", temp
);
5373 for (i
= 0; i
< attr
->num_values
; i
++)
5375 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5380 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5381 "characters (RFC 2911 section 4.1.9).",
5382 attr
->name
, attr
->values
[i
].string
.text
);
5385 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5390 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5391 "length %d (RFC 2911 section 4.1.9).",
5392 attr
->name
, attr
->values
[i
].string
.text
,
5393 (int)strlen(attr
->values
[i
].string
.text
));
5409 * 'with_value()' - Test a WITH-VALUE predicate.
5412 static int /* O - 1 on match, 0 on non-match */
5413 with_value(FILE *outfile
, /* I - Output file */
5414 cups_array_t
*errors
, /* I - Errors array */
5415 char *value
, /* I - Value string */
5416 int flags
, /* I - Flags for match */
5417 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5418 char *matchbuf
, /* I - Buffer to hold matching value */
5419 size_t matchlen
) /* I - Length of match buffer */
5421 int i
, /* Looping var */
5423 char temp
[1024], /* Temporary value string */
5424 *valptr
; /* Pointer into value */
5428 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5431 * NULL matches everything.
5434 if (!value
|| !*value
)
5438 * Compare the value string to the attribute value.
5441 switch (attr
->value_tag
)
5443 case IPP_TAG_INTEGER
:
5445 for (i
= 0; i
< attr
->num_values
; i
++)
5447 char op
, /* Comparison operator */
5448 *nextptr
; /* Next pointer */
5449 int intvalue
, /* Integer value */
5450 valmatch
= 0; /* Does the current value match? */
5454 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5455 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5456 *valptr
== '=' || *valptr
== '>')
5459 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5461 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5469 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5470 if (nextptr
== valptr
)
5474 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5475 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5476 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5479 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5486 if (flags
& _CUPS_WITH_ALL
)
5501 if (!match
&& errors
)
5503 for (i
= 0; i
< attr
->num_values
; i
++)
5504 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5505 attr
->values
[i
].integer
);
5509 case IPP_TAG_RANGE
:
5510 for (i
= 0; i
< attr
->num_values
; i
++)
5512 char op
, /* Comparison operator */
5513 *nextptr
; /* Next pointer */
5514 int intvalue
, /* Integer value */
5515 valmatch
= 0; /* Does the current value match? */
5519 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5520 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5521 *valptr
== '=' || *valptr
== '>')
5524 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5526 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5534 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5535 if (nextptr
== valptr
)
5539 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5540 attr
->values
[i
].range
.upper
== intvalue
)) ||
5541 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5542 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5545 snprintf(matchbuf
, matchlen
, "%d-%d",
5546 attr
->values
[0].range
.lower
,
5547 attr
->values
[0].range
.upper
);
5554 if (flags
& _CUPS_WITH_ALL
)
5569 if (!match
&& errors
)
5571 for (i
= 0; i
< attr
->num_values
; i
++)
5572 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5573 attr
->values
[i
].range
.lower
,
5574 attr
->values
[i
].range
.upper
);
5578 case IPP_TAG_BOOLEAN
:
5579 for (i
= 0; i
< attr
->num_values
; i
++)
5581 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5584 strlcpy(matchbuf
, value
, matchlen
);
5586 if (!(flags
& _CUPS_WITH_ALL
))
5592 else if (flags
& _CUPS_WITH_ALL
)
5599 if (!match
&& errors
)
5601 for (i
= 0; i
< attr
->num_values
; i
++)
5602 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5603 attr
->values
[i
].boolean
? "true" : "false");
5607 case IPP_TAG_RESOLUTION
:
5608 for (i
= 0; i
< attr
->num_values
; i
++)
5610 if (attr
->values
[i
].resolution
.xres
==
5611 attr
->values
[i
].resolution
.yres
)
5612 snprintf(temp
, sizeof(temp
), "%d%s",
5613 attr
->values
[i
].resolution
.xres
,
5614 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5617 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5618 attr
->values
[i
].resolution
.xres
,
5619 attr
->values
[i
].resolution
.yres
,
5620 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5623 if (!strcmp(value
, temp
))
5626 strlcpy(matchbuf
, value
, matchlen
);
5628 if (!(flags
& _CUPS_WITH_ALL
))
5634 else if (flags
& _CUPS_WITH_ALL
)
5641 if (!match
&& errors
)
5643 for (i
= 0; i
< attr
->num_values
; i
++)
5645 if (attr
->values
[i
].resolution
.xres
==
5646 attr
->values
[i
].resolution
.yres
)
5647 snprintf(temp
, sizeof(temp
), "%d%s",
5648 attr
->values
[i
].resolution
.xres
,
5649 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5652 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5653 attr
->values
[i
].resolution
.xres
,
5654 attr
->values
[i
].resolution
.yres
,
5655 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5658 if (strcmp(value
, temp
))
5659 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5664 case IPP_TAG_NOVALUE
:
5665 case IPP_TAG_UNKNOWN
:
5668 case IPP_TAG_CHARSET
:
5669 case IPP_TAG_KEYWORD
:
5670 case IPP_TAG_LANGUAGE
:
5671 case IPP_TAG_MIMETYPE
:
5673 case IPP_TAG_NAMELANG
:
5675 case IPP_TAG_TEXTLANG
:
5677 case IPP_TAG_URISCHEME
:
5678 if (flags
& _CUPS_WITH_REGEX
)
5681 * Value is an extended, case-sensitive POSIX regular expression...
5684 regex_t re
; /* Regular expression */
5686 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5688 regerror(i
, &re
, temp
, sizeof(temp
));
5690 print_fatal_error(outfile
, "Unable to compile WITH-VALUE regular expression "
5691 "\"%s\" - %s", value
, temp
);
5696 * See if ALL of the values match the given regular expression.
5699 for (i
= 0; i
< attr
->num_values
; i
++)
5701 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5706 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5709 if (!(flags
& _CUPS_WITH_ALL
))
5715 else if (flags
& _CUPS_WITH_ALL
)
5727 * Value is a literal string, see if the value(s) match...
5730 for (i
= 0; i
< attr
->num_values
; i
++)
5732 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5736 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5739 if (!(flags
& _CUPS_WITH_ALL
))
5745 else if (flags
& _CUPS_WITH_ALL
)
5753 if (!match
&& errors
)
5755 for (i
= 0; i
< attr
->num_values
; i
++)
5756 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5757 attr
->values
[i
].string
.text
);