4 * ipptool command for CUPS.
6 * Copyright 2007-2015 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>
37 #endif /* !O_BINARY */
44 typedef enum _cups_transfer_e
/**** How to send request data ****/
46 _CUPS_TRANSFER_AUTO
, /* Chunk for files, length for static */
47 _CUPS_TRANSFER_CHUNKED
, /* Chunk always */
48 _CUPS_TRANSFER_LENGTH
/* Length always */
51 typedef enum _cups_output_e
/**** Output mode ****/
53 _CUPS_OUTPUT_QUIET
, /* No output */
54 _CUPS_OUTPUT_TEST
, /* Traditional CUPS test output */
55 _CUPS_OUTPUT_PLIST
, /* XML plist test output */
56 _CUPS_OUTPUT_LIST
, /* Tabular list output */
57 _CUPS_OUTPUT_CSV
/* Comma-separated values output */
60 typedef enum _cups_with_e
/**** WITH flags ****/
62 _CUPS_WITH_LITERAL
= 0, /* Match string is a literal value */
63 _CUPS_WITH_ALL
= 1, /* Must match all values */
64 _CUPS_WITH_REGEX
= 2, /* Match string is a regular expression */
65 _CUPS_WITH_HOSTNAME
= 4, /* Match string is a URI hostname */
66 _CUPS_WITH_RESOURCE
= 8, /* Match string is a URI resource */
67 _CUPS_WITH_SCHEME
= 16 /* Match string is a URI scheme */
70 typedef struct _cups_expect_s
/**** Expected attribute info ****/
72 int optional
, /* Optional attribute? */
73 not_expect
; /* Don't expect attribute? */
74 char *name
, /* Attribute name */
75 *of_type
, /* Type name */
76 *same_count_as
, /* Parallel attribute name */
77 *if_defined
, /* Only required if variable defined */
78 *if_not_defined
, /* Only required if variable is not defined */
79 *with_value
, /* Attribute must include this value */
80 *define_match
, /* Variable to define on match */
81 *define_no_match
, /* Variable to define on no-match */
82 *define_value
; /* Variable to define with value */
83 int repeat_limit
, /* Maximum number of times to repeat */
84 repeat_match
, /* Repeat test on match */
85 repeat_no_match
, /* Repeat test on no match */
86 with_flags
, /* WITH flags */
87 count
; /* Expected count if > 0 */
88 ipp_tag_t in_group
; /* IN-GROUP value */
91 typedef struct _cups_status_s
/**** Status info ****/
93 ipp_status_t status
; /* Expected status code */
94 char *if_defined
, /* Only if variable is defined */
95 *if_not_defined
, /* Only if variable is not defined */
96 *define_match
, /* Variable to define on match */
97 *define_no_match
, /* Variable to define on no-match */
98 *define_value
; /* Variable to define with value */
99 int repeat_limit
, /* Maximum number of times to repeat */
100 repeat_match
, /* Repeat the test when it does not match */
101 repeat_no_match
; /* Repeat the test when it matches */
104 typedef struct _cups_var_s
/**** Variable ****/
106 char *name
, /* Name of variable */
107 *value
; /* Value of variable */
110 typedef struct _cups_vars_s
/**** Set of variables ****/
112 char *uri
, /* URI for printer */
113 *filename
, /* Filename */
114 scheme
[64], /* Scheme from URI */
115 userpass
[256], /* Username/password from URI */
116 hostname
[256], /* Hostname from URI */
117 resource
[1024]; /* Resource path from URI */
118 int port
; /* Port number from URI */
119 http_encryption_t encryption
; /* Encryption for connection? */
120 double timeout
; /* Timeout for connection */
121 int family
; /* Address family */
122 cups_array_t
*vars
; /* Array of variables */
130 static _cups_transfer_t Transfer
= _CUPS_TRANSFER_AUTO
;
131 /* How to transfer requests */
132 static _cups_output_t Output
= _CUPS_OUTPUT_LIST
;
134 static int Cancel
= 0, /* Cancel test? */
135 IgnoreErrors
= 0, /* Ignore errors? */
136 StopAfterIncludeError
= 0,
137 /* Stop after include errors? */
138 Verbosity
= 0, /* Show all attributes? */
139 Version
= 11, /* Default IPP version */
140 XMLHeader
= 0, /* 1 if header is written */
141 TestCount
= 0, /* Number of tests run */
142 PassCount
= 0, /* Number of passing tests */
143 FailCount
= 0, /* Number of failing tests */
144 SkipCount
= 0; /* Number of skipped tests */
145 static char *Username
= NULL
, /* Username from URI */
146 *Password
= NULL
; /* Password from URI */
147 static int PasswordTries
= 0; /* Number of tries with password */
154 static void add_stringf(cups_array_t
*a
, const char *s
, ...)
155 __attribute__ ((__format__ (__printf__
, 2, 3)));
156 static int compare_vars(_cups_var_t
*a
, _cups_var_t
*b
);
157 static int do_tests(FILE *outfile
, _cups_vars_t
*vars
, const char *testfile
);
158 static void expand_variables(_cups_vars_t
*vars
, char *dst
, const char *src
,
159 size_t dstsize
) __attribute__((nonnull(1,2,3)));
160 static int expect_matches(_cups_expect_t
*expect
, ipp_tag_t value_tag
);
161 static ipp_t
*get_collection(FILE *outfile
, _cups_vars_t
*vars
, FILE *fp
, int *linenum
);
162 static char *get_filename(const char *testfile
, char *dst
, const char *src
,
164 static char *get_string(ipp_attribute_t
*attr
, int element
, int flags
,
165 char *buffer
, size_t bufsize
);
166 static char *get_token(FILE *fp
, char *buf
, int buflen
,
168 static char *get_variable(_cups_vars_t
*vars
, const char *name
);
169 static char *iso_date(ipp_uchar_t
*date
);
170 static const char *password_cb(const char *prompt
);
171 static void pause_message(const char *message
);
172 static void print_attr(FILE *outfile
, int format
, ipp_attribute_t
*attr
, ipp_tag_t
*group
);
173 static void print_csv(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
,
174 char **displayed
, size_t *widths
);
175 static void print_fatal_error(FILE *outfile
, const char *s
, ...)
176 __attribute__ ((__format__ (__printf__
, 2, 3)));
177 static void print_line(FILE *outfile
, ipp_attribute_t
*attr
, int num_displayed
,
178 char **displayed
, size_t *widths
);
179 static void print_xml_header(FILE *outfile
);
180 static void print_xml_string(FILE *outfile
, const char *element
, const char *s
);
181 static void print_xml_trailer(FILE *outfile
, int success
, const char *message
);
182 static void set_variable(FILE *outfile
, _cups_vars_t
*vars
, const char *name
, const char *value
);
184 static void sigterm_handler(int sig
);
186 static int timeout_cb(http_t
*http
, void *user_data
);
187 static void usage(void) __attribute__((noreturn
));
188 static int validate_attr(FILE *outfile
, cups_array_t
*errors
, ipp_attribute_t
*attr
);
189 static int with_value(FILE *outfile
, cups_array_t
*errors
, char *value
, int flags
,
190 ipp_attribute_t
*attr
, char *matchbuf
,
195 * 'main()' - Parse options and do tests.
198 int /* O - Exit status */
199 main(int argc
, /* I - Number of command-line args */
200 char *argv
[]) /* I - Command-line arguments */
202 int i
; /* Looping var */
203 int status
; /* Status of tests... */
204 FILE *outfile
= stdout
;
206 char *opt
, /* Current option */
207 name
[1024], /* Name/value buffer */
208 *value
, /* Pointer to value */
209 filename
[1024], /* Real filename */
210 testname
[1024], /* Real test filename */
211 uri
[1024]; /* Copy of printer URI */
212 const char *ext
, /* Extension on filename */
213 *testfile
; /* Test file to use */
214 int interval
, /* Test interval in microseconds */
215 repeat
; /* Repeat count */
216 _cups_vars_t vars
; /* Variables */
217 http_uri_status_t uri_status
; /* URI separation status */
218 _cups_globals_t
*cg
= _cupsGlobals();
224 * Catch SIGINT and SIGTERM...
227 signal(SIGINT
, sigterm_handler
);
228 signal(SIGTERM
, sigterm_handler
);
232 * Initialize the locale and variables...
235 _cupsSetLocale(argv
);
237 memset(&vars
, 0, sizeof(vars
));
238 vars
.family
= AF_UNSPEC
;
239 vars
.vars
= cupsArrayNew((cups_array_func_t
)compare_vars
, NULL
);
244 * ipptool URI testfile
252 for (i
= 1; i
< argc
; i
++)
254 if (!strcmp(argv
[i
], "--help"))
258 else if (!strcmp(argv
[i
], "--stop-after-include-error"))
260 StopAfterIncludeError
= 1;
262 else if (!strcmp(argv
[i
], "--version"))
267 else if (argv
[i
][0] == '-')
269 for (opt
= argv
[i
] + 1; *opt
; opt
++)
273 case '4' : /* Connect using IPv4 only */
274 vars
.family
= AF_INET
;
278 case '6' : /* Connect using IPv6 only */
279 vars
.family
= AF_INET6
;
281 #endif /* AF_INET6 */
283 case 'C' : /* Enable HTTP chunking */
284 Transfer
= _CUPS_TRANSFER_CHUNKED
;
287 case 'E' : /* Encrypt with TLS */
289 vars
.encryption
= HTTP_ENCRYPT_REQUIRED
;
291 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
293 #endif /* HAVE_SSL */
296 case 'I' : /* Ignore errors */
300 case 'L' : /* Disable HTTP chunking */
301 Transfer
= _CUPS_TRANSFER_LENGTH
;
304 case 'P' : /* Output to plist file */
309 _cupsLangPrintf(stderr
, _("%s: Missing filename for \"-P\"."), "ipptool");
313 if (outfile
!= stdout
)
316 if ((outfile
= fopen(argv
[i
], "w")) == NULL
)
318 _cupsLangPrintf(stderr
, _("%s: Unable to open \"%s\": %s"), "ipptool", argv
[i
], strerror(errno
));
322 Output
= _CUPS_OUTPUT_PLIST
;
324 if (interval
|| repeat
)
326 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
331 case 'S' : /* Encrypt with SSL */
333 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
335 _cupsLangPrintf(stderr
, _("%s: Sorry, no encryption support."),
337 #endif /* HAVE_SSL */
340 case 'T' : /* Set timeout */
345 _cupsLangPrintf(stderr
,
346 _("%s: Missing timeout for \"-T\"."),
351 vars
.timeout
= _cupsStrScand(argv
[i
], NULL
, localeconv());
354 case 'V' : /* Set IPP version */
359 _cupsLangPrintf(stderr
,
360 _("%s: Missing version for \"-V\"."),
365 if (!strcmp(argv
[i
], "1.0"))
367 else if (!strcmp(argv
[i
], "1.1"))
369 else if (!strcmp(argv
[i
], "2.0"))
371 else if (!strcmp(argv
[i
], "2.1"))
373 else if (!strcmp(argv
[i
], "2.2"))
377 _cupsLangPrintf(stderr
,
378 _("%s: Bad version %s for \"-V\"."),
384 case 'X' : /* Produce XML output */
385 Output
= _CUPS_OUTPUT_PLIST
;
387 if (interval
|| repeat
)
389 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
394 case 'c' : /* CSV output */
395 Output
= _CUPS_OUTPUT_CSV
;
398 case 'd' : /* Define a variable */
403 _cupsLangPuts(stderr
,
404 _("ipptool: Missing name=value for \"-d\"."));
408 strlcpy(name
, argv
[i
], sizeof(name
));
409 if ((value
= strchr(name
, '=')) != NULL
)
412 value
= name
+ strlen(name
);
414 set_variable(outfile
, &vars
, name
, value
);
417 case 'f' : /* Set the default test filename */
422 _cupsLangPuts(stderr
,
423 _("ipptool: Missing filename for \"-f\"."));
430 vars
.filename
= NULL
;
433 if (access(argv
[i
], 0))
439 snprintf(filename
, sizeof(filename
), "%s.gz", argv
[i
]);
440 if (access(filename
, 0) && filename
[0] != '/'
442 && (!isalpha(filename
[0] & 255) || filename
[1] != ':')
446 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s",
447 cg
->cups_datadir
, argv
[i
]);
448 if (access(filename
, 0))
450 snprintf(filename
, sizeof(filename
), "%s/ipptool/%s.gz",
451 cg
->cups_datadir
, argv
[i
]);
452 if (access(filename
, 0))
453 vars
.filename
= strdup(argv
[i
]);
455 vars
.filename
= strdup(filename
);
458 vars
.filename
= strdup(filename
);
461 vars
.filename
= strdup(filename
);
464 vars
.filename
= strdup(argv
[i
]);
466 if ((ext
= strrchr(vars
.filename
, '.')) != NULL
)
469 * Guess the MIME media type based on the extension...
472 if (!_cups_strcasecmp(ext
, ".gif"))
473 set_variable(outfile
, &vars
, "filetype", "image/gif");
474 else if (!_cups_strcasecmp(ext
, ".htm") ||
475 !_cups_strcasecmp(ext
, ".htm.gz") ||
476 !_cups_strcasecmp(ext
, ".html") ||
477 !_cups_strcasecmp(ext
, ".html.gz"))
478 set_variable(outfile
, &vars
, "filetype", "text/html");
479 else if (!_cups_strcasecmp(ext
, ".jpg") ||
480 !_cups_strcasecmp(ext
, ".jpeg"))
481 set_variable(outfile
, &vars
, "filetype", "image/jpeg");
482 else if (!_cups_strcasecmp(ext
, ".pcl") ||
483 !_cups_strcasecmp(ext
, ".pcl.gz"))
484 set_variable(outfile
, &vars
, "filetype", "application/vnd.hp-PCL");
485 else if (!_cups_strcasecmp(ext
, ".pdf"))
486 set_variable(outfile
, &vars
, "filetype", "application/pdf");
487 else if (!_cups_strcasecmp(ext
, ".png"))
488 set_variable(outfile
, &vars
, "filetype", "image/png");
489 else if (!_cups_strcasecmp(ext
, ".ps") ||
490 !_cups_strcasecmp(ext
, ".ps.gz"))
491 set_variable(outfile
, &vars
, "filetype", "application/postscript");
492 else if (!_cups_strcasecmp(ext
, ".pwg") ||
493 !_cups_strcasecmp(ext
, ".pwg.gz") ||
494 !_cups_strcasecmp(ext
, ".ras") ||
495 !_cups_strcasecmp(ext
, ".ras.gz"))
496 set_variable(outfile
, &vars
, "filetype", "image/pwg-raster");
497 else if (!_cups_strcasecmp(ext
, ".tif") ||
498 !_cups_strcasecmp(ext
, ".tiff"))
499 set_variable(outfile
, &vars
, "filetype", "image/tiff");
500 else if (!_cups_strcasecmp(ext
, ".txt") ||
501 !_cups_strcasecmp(ext
, ".txt.gz"))
502 set_variable(outfile
, &vars
, "filetype", "text/plain");
503 else if (!_cups_strcasecmp(ext
, ".urf") ||
504 !_cups_strcasecmp(ext
, ".urf.gz"))
505 set_variable(outfile
, &vars
, "filetype", "image/urf");
506 else if (!_cups_strcasecmp(ext
, ".xps"))
507 set_variable(outfile
, &vars
, "filetype", "application/openxps");
509 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
514 * Use the "auto-type" MIME media type...
517 set_variable(outfile
, &vars
, "filetype", "application/octet-stream");
521 case 'i' : /* Test every N seconds */
526 _cupsLangPuts(stderr
,
527 _("ipptool: Missing seconds for \"-i\"."));
532 interval
= (int)(_cupsStrScand(argv
[i
], NULL
, localeconv()) *
536 _cupsLangPuts(stderr
,
537 _("ipptool: Invalid seconds for \"-i\"."));
542 if (Output
== _CUPS_OUTPUT_PLIST
&& interval
)
544 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
549 case 'l' : /* List as a table */
550 Output
= _CUPS_OUTPUT_LIST
;
553 case 'n' : /* Repeat count */
558 _cupsLangPuts(stderr
,
559 _("ipptool: Missing count for \"-n\"."));
563 repeat
= atoi(argv
[i
]);
565 if (Output
== _CUPS_OUTPUT_PLIST
&& repeat
)
567 _cupsLangPuts(stderr
, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
572 case 'q' : /* Be quiet */
573 Output
= _CUPS_OUTPUT_QUIET
;
576 case 't' : /* CUPS test output */
577 Output
= _CUPS_OUTPUT_TEST
;
580 case 'v' : /* Be verbose */
585 _cupsLangPrintf(stderr
, _("ipptool: Unknown option \"-%c\"."),
591 else if (!strncmp(argv
[i
], "ipp://", 6) || !strncmp(argv
[i
], "http://", 7)
593 || !strncmp(argv
[i
], "ipps://", 7)
594 || !strncmp(argv
[i
], "https://", 8)
595 #endif /* HAVE_SSL */
604 _cupsLangPuts(stderr
, _("ipptool: May only specify a single URI."));
609 if (!strncmp(argv
[i
], "ipps://", 7) || !strncmp(argv
[i
], "https://", 8))
610 vars
.encryption
= HTTP_ENCRYPT_ALWAYS
;
611 #endif /* HAVE_SSL */
614 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, vars
.uri
,
615 vars
.scheme
, sizeof(vars
.scheme
),
616 vars
.userpass
, sizeof(vars
.userpass
),
617 vars
.hostname
, sizeof(vars
.hostname
),
619 vars
.resource
, sizeof(vars
.resource
));
621 if (uri_status
!= HTTP_URI_OK
)
623 _cupsLangPrintf(stderr
, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status
));
627 if (vars
.userpass
[0])
629 if ((Password
= strchr(vars
.userpass
, ':')) != NULL
)
632 Username
= vars
.userpass
;
633 cupsSetPasswordCB(password_cb
);
634 set_variable(outfile
, &vars
, "uriuser", vars
.userpass
);
637 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), vars
.scheme
, NULL
,
638 vars
.hostname
, vars
.port
, vars
.resource
);
649 _cupsLangPuts(stderr
, _("ipptool: URI required before test file."));
650 _cupsLangPuts(stderr
, argv
[i
]);
654 if (access(argv
[i
], 0) && argv
[i
][0] != '/'
656 && (!isalpha(argv
[i
][0] & 255) || argv
[i
][1] != ':')
660 snprintf(testname
, sizeof(testname
), "%s/ipptool/%s", cg
->cups_datadir
,
662 if (access(testname
, 0))
670 if (!do_tests(outfile
, &vars
, testfile
))
675 if (!vars
.uri
|| !testfile
)
679 * Loop if the interval is set...
682 if (Output
== _CUPS_OUTPUT_PLIST
)
683 print_xml_trailer(outfile
, !status
, NULL
);
684 else if (interval
> 0 && repeat
> 0)
688 usleep((useconds_t
)interval
);
689 do_tests(outfile
, &vars
, testfile
);
693 else if (interval
> 0)
697 usleep((useconds_t
)interval
);
698 do_tests(outfile
, &vars
, testfile
);
702 if ((Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
)) && TestCount
> 1)
705 * Show a summary report if there were multiple tests...
708 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
709 "Score: %d%%\n", TestCount
, PassCount
, FailCount
, SkipCount
,
710 100 * (PassCount
+ SkipCount
) / TestCount
);
722 * 'add_stringf()' - Add a formatted string to an array.
726 add_stringf(cups_array_t
*a
, /* I - Array */
727 const char *s
, /* I - Printf-style format string */
728 ...) /* I - Additional args as needed */
730 char buffer
[10240]; /* Format buffer */
731 va_list ap
; /* Argument pointer */
735 * Don't bother is the array is NULL...
742 * Format the message...
746 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
750 * Add it to the array...
753 cupsArrayAdd(a
, buffer
);
758 * 'compare_vars()' - Compare two variables.
761 static int /* O - Result of comparison */
762 compare_vars(_cups_var_t
*a
, /* I - First variable */
763 _cups_var_t
*b
) /* I - Second variable */
765 return (_cups_strcasecmp(a
->name
, b
->name
));
770 * 'do_tests()' - Do tests as specified in the test file.
773 static int /* 1 = success, 0 = failure */
774 do_tests(FILE *outfile
, /* I - Output file */
775 _cups_vars_t
*vars
, /* I - Variables */
776 const char *testfile
) /* I - Test file to use */
778 int i
, /* Looping var */
779 linenum
, /* Current line number */
780 pass
, /* Did we pass the test? */
781 prev_pass
= 1, /* Did we pass the previous test? */
782 request_id
, /* Current request ID */
783 show_header
= 1, /* Show the test header? */
784 ignore_errors
, /* Ignore test failures? */
785 skip_previous
= 0, /* Skip on previous test failure? */
786 repeat_count
, /* Repeat count */
787 repeat_interval
, /* Repeat interval */
788 repeat_prev
, /* Previous repeat interval */
789 repeat_test
; /* Repeat a test? */
790 http_t
*http
= NULL
; /* HTTP connection to server */
791 FILE *fp
= NULL
; /* Test file */
792 char resource
[512], /* Resource for request */
793 token
[1024], /* Token from file */
794 *tokenptr
, /* Pointer into token */
795 temp
[1024], /* Temporary string */
796 buffer
[8192], /* Copy buffer */
797 compression
[16]; /* COMPRESSION value */
798 ipp_t
*request
= NULL
, /* IPP request */
799 *response
= NULL
; /* IPP response */
800 size_t length
; /* Length of IPP request */
801 http_status_t status
; /* HTTP status */
802 cups_file_t
*reqfile
; /* File to send */
803 ssize_t bytes
; /* Bytes read/written */
804 char attr
[128]; /* Attribute name */
805 ipp_op_t op
; /* Operation */
806 ipp_tag_t group
; /* Current group */
807 ipp_tag_t value
; /* Current value type */
808 ipp_attribute_t
*attrptr
, /* Attribute pointer */
809 *found
, /* Found attribute */
810 *lastcol
= NULL
; /* Last collection attribute */
811 char name
[1024], /* Name of test */
812 file_id
[1024], /* File identifier */
813 test_id
[1024]; /* Test identifier */
814 char filename
[1024]; /* Filename */
815 _cups_transfer_t transfer
; /* To chunk or not to chunk */
816 int version
, /* IPP version number to use */
817 skip_test
; /* Skip this test? */
818 int num_statuses
= 0; /* Number of valid status codes */
819 _cups_status_t statuses
[100], /* Valid status codes */
820 *last_status
; /* Last STATUS (for predicates) */
821 int num_expects
= 0; /* Number of expected attributes */
822 _cups_expect_t expects
[200], /* Expected attributes */
823 *expect
, /* Current expected attribute */
824 *last_expect
; /* Last EXPECT (for predicates) */
825 int num_displayed
= 0; /* Number of displayed attributes */
826 char *displayed
[200]; /* Displayed attributes */
827 size_t widths
[200]; /* Width of columns */
828 cups_array_t
*a
, /* Duplicate attribute array */
829 *errors
= NULL
; /* Errors array */
830 const char *error
; /* Current error */
834 * Open the test file...
837 if ((fp
= fopen(testfile
, "r")) == NULL
)
839 print_fatal_error(outfile
, "Unable to open test file %s - %s", testfile
,
846 * Connect to the server...
849 if ((http
= httpConnect2(vars
->hostname
, vars
->port
, NULL
, vars
->family
,
850 vars
->encryption
, 1, 30000, NULL
)) == NULL
)
852 print_fatal_error(outfile
, "Unable to connect to %s on port %d - %s", vars
->hostname
,
853 vars
->port
, cupsLastErrorString());
859 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
,
860 "deflate, gzip, identity");
862 httpSetDefaultField(http
, HTTP_FIELD_ACCEPT_ENCODING
, "identity");
863 #endif /* HAVE_LIBZ */
865 if (vars
->timeout
> 0.0)
866 httpSetTimeout(http
, vars
->timeout
, timeout_cb
, NULL
);
872 CUPS_SRAND(time(NULL
));
874 errors
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, (cups_acopy_func_t
)strdup
,
875 (cups_afree_func_t
)free
);
879 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
881 while (!Cancel
&& get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
884 * Expect an open brace...
887 if (!strcmp(token
, "DEFINE"))
893 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
894 get_token(fp
, temp
, sizeof(temp
), &linenum
))
896 expand_variables(vars
, token
, temp
, sizeof(token
));
897 set_variable(outfile
, vars
, attr
, token
);
901 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
909 else if (!strcmp(token
, "DEFINE-DEFAULT"))
912 * DEFINE-DEFAULT name value
915 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
916 get_token(fp
, temp
, sizeof(temp
), &linenum
))
918 expand_variables(vars
, token
, temp
, sizeof(token
));
919 if (!get_variable(vars
, attr
))
920 set_variable(outfile
, vars
, attr
, token
);
924 print_fatal_error(outfile
, "Missing DEFINE-DEFAULT name and/or value on line "
932 else if (!strcmp(token
, "FILE-ID"))
938 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
940 expand_variables(vars
, file_id
, temp
, sizeof(file_id
));
944 print_fatal_error(outfile
, "Missing FILE-ID value on line %d.", linenum
);
951 else if (!strcmp(token
, "IGNORE-ERRORS"))
958 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
959 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
961 IgnoreErrors
= !_cups_strcasecmp(temp
, "yes");
965 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
972 else if (!strcmp(token
, "INCLUDE"))
979 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
982 * Map the filename to and then run the tests...
985 if (!do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
989 if (StopAfterIncludeError
)
995 print_fatal_error(outfile
, "Missing INCLUDE filename on line %d.", linenum
);
1003 else if (!strcmp(token
, "INCLUDE-IF-DEFINED"))
1006 * INCLUDE-IF-DEFINED name "filename"
1007 * INCLUDE-IF-DEFINED name <filename>
1010 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1011 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1014 * Map the filename to and then run the tests...
1017 if (get_variable(vars
, attr
) &&
1018 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1022 if (StopAfterIncludeError
)
1028 print_fatal_error(outfile
, "Missing INCLUDE-IF-DEFINED name or filename on line "
1037 else if (!strcmp(token
, "INCLUDE-IF-NOT-DEFINED"))
1040 * INCLUDE-IF-NOT-DEFINED name "filename"
1041 * INCLUDE-IF-NOT-DEFINED name <filename>
1044 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1045 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1048 * Map the filename to and then run the tests...
1051 if (!get_variable(vars
, attr
) &&
1052 !do_tests(outfile
, vars
, get_filename(testfile
, filename
, temp
, sizeof(filename
))))
1056 if (StopAfterIncludeError
)
1062 print_fatal_error(outfile
, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
1063 "line %d.", linenum
);
1071 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1074 * SKIP-IF-DEFINED variable
1077 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1079 if (get_variable(vars
, temp
))
1084 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED variable on line %d.",
1090 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1093 * SKIP-IF-NOT-DEFINED variable
1096 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1098 if (!get_variable(vars
, temp
))
1103 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
1109 else if (!strcmp(token
, "STOP-AFTER-INCLUDE-ERROR"))
1112 * STOP-AFTER-INCLUDE-ERROR yes
1113 * STOP-AFTER-INCLUDE-ERROR no
1116 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1117 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1119 StopAfterIncludeError
= !_cups_strcasecmp(temp
, "yes");
1123 print_fatal_error(outfile
, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
1131 else if (!strcmp(token
, "TRANSFER"))
1139 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1141 if (!strcmp(temp
, "auto"))
1142 Transfer
= _CUPS_TRANSFER_AUTO
;
1143 else if (!strcmp(temp
, "chunked"))
1144 Transfer
= _CUPS_TRANSFER_CHUNKED
;
1145 else if (!strcmp(temp
, "length"))
1146 Transfer
= _CUPS_TRANSFER_LENGTH
;
1149 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1157 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1164 else if (!strcmp(token
, "VERSION"))
1166 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1168 if (!strcmp(temp
, "1.0"))
1170 else if (!strcmp(temp
, "1.1"))
1172 else if (!strcmp(temp
, "2.0"))
1174 else if (!strcmp(temp
, "2.1"))
1176 else if (!strcmp(temp
, "2.2"))
1180 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1187 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1194 else if (strcmp(token
, "{"))
1196 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
, linenum
);
1202 * Initialize things...
1207 if (Output
== _CUPS_OUTPUT_PLIST
)
1208 print_xml_header(outfile
);
1209 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
1210 printf("\"%s\":\n", testfile
);
1215 strlcpy(resource
, vars
->resource
, sizeof(resource
));
1220 group
= IPP_TAG_ZERO
;
1221 ignore_errors
= IgnoreErrors
;
1229 transfer
= Transfer
;
1230 compression
[0] = '\0';
1232 strlcpy(name
, testfile
, sizeof(name
));
1233 if (strrchr(name
, '.') != NULL
)
1234 *strrchr(name
, '.') = '\0';
1237 * Parse until we see a close brace...
1240 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
1242 if (_cups_strcasecmp(token
, "COUNT") &&
1243 _cups_strcasecmp(token
, "DEFINE-MATCH") &&
1244 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1245 _cups_strcasecmp(token
, "DEFINE-VALUE") &&
1246 _cups_strcasecmp(token
, "IF-DEFINED") &&
1247 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1248 _cups_strcasecmp(token
, "IN-GROUP") &&
1249 _cups_strcasecmp(token
, "OF-TYPE") &&
1250 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1251 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1252 _cups_strcasecmp(token
, "REPEAT-NO-MATCH") &&
1253 _cups_strcasecmp(token
, "SAME-COUNT-AS") &&
1254 _cups_strcasecmp(token
, "WITH-ALL-VALUES") &&
1255 _cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") &&
1256 _cups_strcasecmp(token
, "WITH-ALL-RESOURCES") &&
1257 _cups_strcasecmp(token
, "WITH-ALL-SCHEMES") &&
1258 _cups_strcasecmp(token
, "WITH-HOSTNAME") &&
1259 _cups_strcasecmp(token
, "WITH-RESOURCE") &&
1260 _cups_strcasecmp(token
, "WITH-SCHEME") &&
1261 _cups_strcasecmp(token
, "WITH-VALUE"))
1264 if (_cups_strcasecmp(token
, "DEFINE-MATCH") &&
1265 _cups_strcasecmp(token
, "DEFINE-NO-MATCH") &&
1266 _cups_strcasecmp(token
, "IF-DEFINED") &&
1267 _cups_strcasecmp(token
, "IF-NOT-DEFINED") &&
1268 _cups_strcasecmp(token
, "REPEAT-LIMIT") &&
1269 _cups_strcasecmp(token
, "REPEAT-MATCH") &&
1270 _cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
1273 if (!strcmp(token
, "}"))
1275 else if (!strcmp(token
, "{") && lastcol
)
1278 * Another collection value
1281 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1282 /* Collection value */
1286 ippSetCollection(request
, &lastcol
, ippGetCount(lastcol
), col
);
1294 else if (!strcmp(token
, "COMPRESSION"))
1298 * COMPRESSION deflate
1302 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1304 expand_variables(vars
, compression
, temp
, sizeof(compression
));
1306 if (strcmp(compression
, "none") && strcmp(compression
, "deflate") &&
1307 strcmp(compression
, "gzip"))
1309 if (strcmp(compression
, "none"))
1310 #endif /* HAVE_LIBZ */
1312 print_fatal_error(outfile
, "Unsupported COMPRESSION value '%s' on line %d.",
1313 compression
, linenum
);
1318 if (!strcmp(compression
, "none"))
1319 compression
[0] = '\0';
1323 print_fatal_error(outfile
, "Missing COMPRESSION value on line %d.", linenum
);
1328 else if (!strcmp(token
, "DEFINE"))
1334 if (get_token(fp
, attr
, sizeof(attr
), &linenum
) &&
1335 get_token(fp
, temp
, sizeof(temp
), &linenum
))
1337 expand_variables(vars
, token
, temp
, sizeof(token
));
1338 set_variable(outfile
, vars
, attr
, token
);
1342 print_fatal_error(outfile
, "Missing DEFINE name and/or value on line %d.",
1348 else if (!strcmp(token
, "IGNORE-ERRORS"))
1355 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1356 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1358 ignore_errors
= !_cups_strcasecmp(temp
, "yes");
1362 print_fatal_error(outfile
, "Missing IGNORE-ERRORS value on line %d.", linenum
);
1369 else if (!_cups_strcasecmp(token
, "NAME"))
1375 get_token(fp
, temp
, sizeof(temp
), &linenum
);
1376 expand_variables(vars
, name
, temp
, sizeof(name
));
1378 else if (!_cups_strcasecmp(token
, "PAUSE"))
1381 * Pause with a message...
1384 get_token(fp
, token
, sizeof(token
), &linenum
);
1385 pause_message(token
);
1387 else if (!strcmp(token
, "REQUEST-ID"))
1394 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1396 if (isdigit(temp
[0] & 255))
1397 request_id
= atoi(temp
);
1398 else if (!_cups_strcasecmp(temp
, "random"))
1399 request_id
= (CUPS_RAND() % 1000) * 137 + 1;
1402 print_fatal_error(outfile
, "Bad REQUEST-ID value \"%s\" on line %d.", temp
,
1410 print_fatal_error(outfile
, "Missing REQUEST-ID value on line %d.", linenum
);
1415 else if (!strcmp(token
, "SKIP-IF-DEFINED"))
1418 * SKIP-IF-DEFINED variable
1421 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1423 if (get_variable(vars
, temp
))
1428 print_fatal_error(outfile
, "Missing SKIP-IF-DEFINED value on line %d.",
1434 else if (!strcmp(token
, "SKIP-IF-MISSING"))
1437 * SKIP-IF-MISSING filename
1440 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1442 expand_variables(vars
, token
, temp
, sizeof(token
));
1443 get_filename(testfile
, filename
, token
, sizeof(filename
));
1445 if (access(filename
, R_OK
))
1450 print_fatal_error(outfile
, "Missing SKIP-IF-MISSING filename on line %d.",
1456 else if (!strcmp(token
, "SKIP-IF-NOT-DEFINED"))
1459 * SKIP-IF-NOT-DEFINED variable
1462 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1464 if (!get_variable(vars
, temp
))
1469 print_fatal_error(outfile
, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
1475 else if (!strcmp(token
, "SKIP-PREVIOUS-ERROR"))
1478 * SKIP-PREVIOUS-ERROR yes
1479 * SKIP-PREVIOUS-ERROR no
1482 if (get_token(fp
, temp
, sizeof(temp
), &linenum
) &&
1483 (!_cups_strcasecmp(temp
, "yes") || !_cups_strcasecmp(temp
, "no")))
1485 skip_previous
= !_cups_strcasecmp(temp
, "yes");
1489 print_fatal_error(outfile
, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum
);
1496 else if (!strcmp(token
, "TEST-ID"))
1502 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1504 expand_variables(vars
, test_id
, temp
, sizeof(test_id
));
1508 print_fatal_error(outfile
, "Missing TEST-ID value on line %d.", linenum
);
1515 else if (!strcmp(token
, "TRANSFER"))
1523 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1525 if (!strcmp(temp
, "auto"))
1526 transfer
= _CUPS_TRANSFER_AUTO
;
1527 else if (!strcmp(temp
, "chunked"))
1528 transfer
= _CUPS_TRANSFER_CHUNKED
;
1529 else if (!strcmp(temp
, "length"))
1530 transfer
= _CUPS_TRANSFER_LENGTH
;
1533 print_fatal_error(outfile
, "Bad TRANSFER value \"%s\" on line %d.", temp
,
1541 print_fatal_error(outfile
, "Missing TRANSFER value on line %d.", linenum
);
1546 else if (!_cups_strcasecmp(token
, "VERSION"))
1548 if (get_token(fp
, temp
, sizeof(temp
), &linenum
))
1550 if (!strcmp(temp
, "0.0"))
1552 else if (!strcmp(temp
, "1.0"))
1554 else if (!strcmp(temp
, "1.1"))
1556 else if (!strcmp(temp
, "2.0"))
1558 else if (!strcmp(temp
, "2.1"))
1560 else if (!strcmp(temp
, "2.2"))
1564 print_fatal_error(outfile
, "Bad VERSION \"%s\" on line %d.", temp
, linenum
);
1571 print_fatal_error(outfile
, "Missing VERSION number on line %d.", linenum
);
1576 else if (!_cups_strcasecmp(token
, "RESOURCE"))
1582 if (!get_token(fp
, resource
, sizeof(resource
), &linenum
))
1584 print_fatal_error(outfile
, "Missing RESOURCE path on line %d.", linenum
);
1589 else if (!_cups_strcasecmp(token
, "OPERATION"))
1595 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1597 print_fatal_error(outfile
, "Missing OPERATION code on line %d.", linenum
);
1602 expand_variables(vars
, token
, temp
, sizeof(token
));
1604 if ((op
= ippOpValue(token
)) == (ipp_op_t
)-1 &&
1605 (op
= (ipp_op_t
)strtol(token
, NULL
, 0)) == 0)
1607 print_fatal_error(outfile
, "Bad OPERATION code \"%s\" on line %d.", token
,
1613 else if (!_cups_strcasecmp(token
, "GROUP"))
1616 * Attribute group...
1619 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1621 print_fatal_error(outfile
, "Missing GROUP tag on line %d.", linenum
);
1626 if ((value
= ippTagValue(token
)) < 0)
1628 print_fatal_error(outfile
, "Bad GROUP tag \"%s\" on line %d.", token
, linenum
);
1634 ippAddSeparator(request
);
1638 else if (!_cups_strcasecmp(token
, "DELAY"))
1641 * Delay before operation...
1646 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1648 print_fatal_error(outfile
, "Missing DELAY value on line %d.", linenum
);
1653 expand_variables(vars
, token
, temp
, sizeof(token
));
1655 if ((delay
= _cupsStrScand(token
, NULL
, localeconv())) <= 0.0)
1657 print_fatal_error(outfile
, "Bad DELAY value \"%s\" on line %d.", token
,
1664 if (Output
== _CUPS_OUTPUT_TEST
)
1665 printf(" [%g second delay]\n", delay
);
1667 usleep((useconds_t
)(1000000.0 * delay
));
1670 else if (!_cups_strcasecmp(token
, "ATTR"))
1676 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1678 print_fatal_error(outfile
, "Missing ATTR value tag on line %d.", linenum
);
1683 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
1685 print_fatal_error(outfile
, "Bad ATTR value tag \"%s\" on line %d.", token
,
1691 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
1693 print_fatal_error(outfile
, "Missing ATTR name on line %d.", linenum
);
1698 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1700 print_fatal_error(outfile
, "Missing ATTR value on line %d.", linenum
);
1705 expand_variables(vars
, token
, temp
, sizeof(token
));
1710 case IPP_TAG_BOOLEAN
:
1711 if (!_cups_strcasecmp(token
, "true"))
1712 attrptr
= ippAddBoolean(request
, group
, attr
, 1);
1714 attrptr
= ippAddBoolean(request
, group
, attr
, (char)atoi(token
));
1717 case IPP_TAG_INTEGER
:
1719 if (!strchr(token
, ','))
1720 attrptr
= ippAddInteger(request
, group
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
1723 int values
[100], /* Values */
1724 num_values
= 1; /* Number of values */
1726 values
[0] = (int)strtol(token
, &tokenptr
, 10);
1727 while (tokenptr
&& *tokenptr
&&
1728 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
1730 if (*tokenptr
== ',')
1732 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
1735 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
1739 attrptr
= ippAddIntegers(request
, group
, value
, attr
, num_values
, values
);
1742 if (!tokenptr
|| *tokenptr
)
1744 print_fatal_error(outfile
, "Bad %s value \"%s\" on line %d.",
1745 ippTagString(value
), token
, linenum
);
1751 case IPP_TAG_RESOLUTION
:
1753 int xres
, /* X resolution */
1754 yres
; /* Y resolution */
1755 char *ptr
; /* Pointer into value */
1757 xres
= yres
= (int)strtol(token
, (char **)&ptr
, 10);
1758 if (ptr
> token
&& xres
> 0)
1761 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
1764 if (ptr
<= token
|| xres
<= 0 || yres
<= 0 || !ptr
||
1765 (_cups_strcasecmp(ptr
, "dpi") &&
1766 _cups_strcasecmp(ptr
, "dpc") &&
1767 _cups_strcasecmp(ptr
, "dpcm") &&
1768 _cups_strcasecmp(ptr
, "other")))
1770 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
1776 if (!_cups_strcasecmp(ptr
, "dpi"))
1777 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
1778 else if (!_cups_strcasecmp(ptr
, "dpc") ||
1779 !_cups_strcasecmp(ptr
, "dpcm"))
1780 attrptr
= ippAddResolution(request
, group
, attr
, IPP_RES_PER_CM
, xres
, yres
);
1782 attrptr
= ippAddResolution(request
, group
, attr
, (ipp_res_t
)0, xres
, yres
);
1786 case IPP_TAG_RANGE
:
1788 int lowers
[4], /* Lower value */
1789 uppers
[4], /* Upper values */
1790 num_vals
; /* Number of values */
1793 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
1794 lowers
+ 0, uppers
+ 0,
1795 lowers
+ 1, uppers
+ 1,
1796 lowers
+ 2, uppers
+ 2,
1797 lowers
+ 3, uppers
+ 3);
1799 if ((num_vals
& 1) || num_vals
== 0)
1801 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line "
1802 "%d.", token
, linenum
);
1807 attrptr
= ippAddRanges(request
, group
, attr
, num_vals
/ 2, lowers
,
1812 case IPP_TAG_BEGIN_COLLECTION
:
1813 if (!strcmp(token
, "{"))
1815 ipp_t
*col
= get_collection(outfile
, vars
, fp
, &linenum
);
1816 /* Collection value */
1820 attrptr
= lastcol
= ippAddCollection(request
, group
, attr
, col
);
1831 print_fatal_error(outfile
, "Bad ATTR collection value on line %d.",
1839 ipp_t
*col
; /* Collection value */
1840 long pos
= ftell(fp
); /* Save position of file */
1842 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1845 if (strcmp(token
, ","))
1847 fseek(fp
, pos
, SEEK_SET
);
1851 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
1853 print_fatal_error(outfile
, "Unexpected \"%s\" on line %d.", token
, linenum
);
1859 if ((col
= get_collection(outfile
, vars
, fp
, &linenum
)) == NULL
)
1862 ippSetCollection(request
, &attrptr
, ippGetCount(attrptr
), col
);
1865 while (!strcmp(token
, "{"));
1868 case IPP_TAG_STRING
:
1869 attrptr
= ippAddOctetString(request
, group
, attr
, token
, (int)strlen(token
));
1873 print_fatal_error(outfile
, "Unsupported ATTR value tag %s on line %d.",
1874 ippTagString(value
), linenum
);
1878 case IPP_TAG_TEXTLANG
:
1879 case IPP_TAG_NAMELANG
:
1882 case IPP_TAG_KEYWORD
:
1884 case IPP_TAG_URISCHEME
:
1885 case IPP_TAG_CHARSET
:
1886 case IPP_TAG_LANGUAGE
:
1887 case IPP_TAG_MIMETYPE
:
1888 if (!strchr(token
, ','))
1889 attrptr
= ippAddString(request
, group
, value
, attr
, NULL
, token
);
1893 * Multiple string values...
1896 int num_values
; /* Number of values */
1897 char *values
[100], /* Values */
1898 *ptr
; /* Pointer to next value */
1904 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
1906 if (ptr
> token
&& ptr
[-1] == '\\')
1907 _cups_strcpy(ptr
- 1, ptr
);
1911 values
[num_values
] = ptr
;
1916 attrptr
= ippAddStrings(request
, group
, value
, attr
, num_values
,
1917 NULL
, (const char **)values
);
1924 print_fatal_error(outfile
, "Unable to add attribute on line %d: %s", linenum
,
1925 cupsLastErrorString());
1930 else if (!_cups_strcasecmp(token
, "FILE"))
1936 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
1938 print_fatal_error(outfile
, "Missing FILE filename on line %d.", linenum
);
1943 expand_variables(vars
, token
, temp
, sizeof(token
));
1944 get_filename(testfile
, filename
, token
, sizeof(filename
));
1946 if (access(filename
, R_OK
))
1948 print_fatal_error(outfile
, "Filename \"%s\" on line %d cannot be read.",
1950 print_fatal_error(outfile
, "Filename mapped to \"%s\".", filename
);
1955 else if (!_cups_strcasecmp(token
, "STATUS"))
1961 if (num_statuses
>= (int)(sizeof(statuses
) / sizeof(statuses
[0])))
1963 print_fatal_error(outfile
, "Too many STATUS's on line %d.", linenum
);
1968 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
1970 print_fatal_error(outfile
, "Missing STATUS code on line %d.", linenum
);
1975 if ((statuses
[num_statuses
].status
= ippErrorValue(token
))
1976 == (ipp_status_t
)-1 &&
1977 (statuses
[num_statuses
].status
= (ipp_status_t
)strtol(token
, NULL
, 0)) == 0)
1979 print_fatal_error(outfile
, "Bad STATUS code \"%s\" on line %d.", token
,
1985 last_status
= statuses
+ num_statuses
;
1988 last_status
->define_match
= NULL
;
1989 last_status
->define_no_match
= NULL
;
1990 last_status
->if_defined
= NULL
;
1991 last_status
->if_not_defined
= NULL
;
1992 last_status
->repeat_limit
= 1000;
1993 last_status
->repeat_match
= 0;
1994 last_status
->repeat_no_match
= 0;
1996 else if (!_cups_strcasecmp(token
, "EXPECT"))
1999 * Expected attributes...
2002 if (num_expects
>= (int)(sizeof(expects
) / sizeof(expects
[0])))
2004 print_fatal_error(outfile
, "Too many EXPECT's on line %d.", linenum
);
2009 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2011 print_fatal_error(outfile
, "Missing EXPECT name on line %d.", linenum
);
2016 last_expect
= expects
+ num_expects
;
2019 memset(last_expect
, 0, sizeof(_cups_expect_t
));
2020 last_expect
->repeat_limit
= 1000;
2022 if (token
[0] == '!')
2024 last_expect
->not_expect
= 1;
2025 last_expect
->name
= strdup(token
+ 1);
2027 else if (token
[0] == '?')
2029 last_expect
->optional
= 1;
2030 last_expect
->name
= strdup(token
+ 1);
2033 last_expect
->name
= strdup(token
);
2035 else if (!_cups_strcasecmp(token
, "COUNT"))
2037 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2039 print_fatal_error(outfile
, "Missing COUNT number on line %d.", linenum
);
2044 if ((i
= atoi(token
)) <= 0)
2046 print_fatal_error(outfile
, "Bad COUNT \"%s\" on line %d.", token
, linenum
);
2052 last_expect
->count
= i
;
2055 print_fatal_error(outfile
, "COUNT without a preceding EXPECT on line %d.",
2061 else if (!_cups_strcasecmp(token
, "DEFINE-MATCH"))
2063 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2065 print_fatal_error(outfile
, "Missing DEFINE-MATCH variable on line %d.",
2072 last_expect
->define_match
= strdup(token
);
2073 else if (last_status
)
2074 last_status
->define_match
= strdup(token
);
2077 print_fatal_error(outfile
, "DEFINE-MATCH without a preceding EXPECT or STATUS "
2078 "on line %d.", linenum
);
2083 else if (!_cups_strcasecmp(token
, "DEFINE-NO-MATCH"))
2085 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2087 print_fatal_error(outfile
, "Missing DEFINE-NO-MATCH variable on line %d.",
2094 last_expect
->define_no_match
= strdup(token
);
2095 else if (last_status
)
2096 last_status
->define_no_match
= strdup(token
);
2099 print_fatal_error(outfile
, "DEFINE-NO-MATCH without a preceding EXPECT or "
2100 "STATUS on line %d.", linenum
);
2105 else if (!_cups_strcasecmp(token
, "DEFINE-VALUE"))
2107 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2109 print_fatal_error(outfile
, "Missing DEFINE-VALUE variable on line %d.",
2116 last_expect
->define_value
= strdup(token
);
2119 print_fatal_error(outfile
, "DEFINE-VALUE without a preceding EXPECT on "
2120 "line %d.", linenum
);
2125 else if (!_cups_strcasecmp(token
, "OF-TYPE"))
2127 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2129 print_fatal_error(outfile
, "Missing OF-TYPE value tag(s) on line %d.",
2136 last_expect
->of_type
= strdup(token
);
2139 print_fatal_error(outfile
, "OF-TYPE without a preceding EXPECT on line %d.",
2145 else if (!_cups_strcasecmp(token
, "IN-GROUP"))
2147 ipp_tag_t in_group
; /* IN-GROUP value */
2150 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2152 print_fatal_error(outfile
, "Missing IN-GROUP group tag on line %d.", linenum
);
2157 if ((in_group
= ippTagValue(token
)) == (ipp_tag_t
)-1)
2160 else if (last_expect
)
2161 last_expect
->in_group
= in_group
;
2164 print_fatal_error(outfile
, "IN-GROUP without a preceding EXPECT on line %d.",
2170 else if (!_cups_strcasecmp(token
, "REPEAT-LIMIT"))
2172 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2174 print_fatal_error(outfile
, "Missing REPEAT-LIMIT value on line %d.", linenum
);
2178 else if (atoi(token
) <= 0)
2180 print_fatal_error(outfile
, "Bad REPEAT-LIMIT value on line %d.", linenum
);
2186 last_status
->repeat_limit
= atoi(token
);
2187 else if (last_expect
)
2188 last_expect
->repeat_limit
= atoi(token
);
2191 print_fatal_error(outfile
, "REPEAT-LIMIT without a preceding EXPECT or STATUS "
2192 "on line %d.", linenum
);
2197 else if (!_cups_strcasecmp(token
, "REPEAT-MATCH"))
2200 last_status
->repeat_match
= 1;
2201 else if (last_expect
)
2202 last_expect
->repeat_match
= 1;
2205 print_fatal_error(outfile
, "REPEAT-MATCH without a preceding EXPECT or STATUS "
2206 "on line %d.", linenum
);
2211 else if (!_cups_strcasecmp(token
, "REPEAT-NO-MATCH"))
2214 last_status
->repeat_no_match
= 1;
2215 else if (last_expect
)
2216 last_expect
->repeat_no_match
= 1;
2219 print_fatal_error(outfile
, "REPEAT-NO-MATCH without a preceding EXPECT or "
2220 "STATUS on ine %d.", linenum
);
2225 else if (!_cups_strcasecmp(token
, "SAME-COUNT-AS"))
2227 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2229 print_fatal_error(outfile
, "Missing SAME-COUNT-AS name on line %d.", linenum
);
2235 last_expect
->same_count_as
= strdup(token
);
2238 print_fatal_error(outfile
, "SAME-COUNT-AS without a preceding EXPECT on line "
2244 else if (!_cups_strcasecmp(token
, "IF-DEFINED"))
2246 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2248 print_fatal_error(outfile
, "Missing IF-DEFINED name on line %d.", linenum
);
2254 last_expect
->if_defined
= strdup(token
);
2255 else if (last_status
)
2256 last_status
->if_defined
= strdup(token
);
2259 print_fatal_error(outfile
, "IF-DEFINED without a preceding EXPECT or STATUS "
2260 "on line %d.", linenum
);
2265 else if (!_cups_strcasecmp(token
, "IF-NOT-DEFINED"))
2267 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2269 print_fatal_error(outfile
, "Missing IF-NOT-DEFINED name on line %d.", linenum
);
2275 last_expect
->if_not_defined
= strdup(token
);
2276 else if (last_status
)
2277 last_status
->if_not_defined
= strdup(token
);
2280 print_fatal_error(outfile
, "IF-NOT-DEFINED without a preceding EXPECT or STATUS "
2281 "on line %d.", linenum
);
2286 else if (!_cups_strcasecmp(token
, "WITH-ALL-VALUES") ||
2287 !_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2288 !_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2289 !_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2290 !_cups_strcasecmp(token
, "WITH-HOSTNAME") ||
2291 !_cups_strcasecmp(token
, "WITH-RESOURCE") ||
2292 !_cups_strcasecmp(token
, "WITH-SCHEME") ||
2293 !_cups_strcasecmp(token
, "WITH-VALUE"))
2297 if (!_cups_strcasecmp(token
, "WITH-ALL-HOSTNAMES") ||
2298 !_cups_strcasecmp(token
, "WITH-HOSTNAME"))
2299 last_expect
->with_flags
= _CUPS_WITH_HOSTNAME
;
2300 else if (!_cups_strcasecmp(token
, "WITH-ALL-RESOURCES") ||
2301 !_cups_strcasecmp(token
, "WITH-RESOURCE"))
2302 last_expect
->with_flags
= _CUPS_WITH_RESOURCE
;
2303 else if (!_cups_strcasecmp(token
, "WITH-ALL-SCHEMES") ||
2304 !_cups_strcasecmp(token
, "WITH-SCHEME"))
2305 last_expect
->with_flags
= _CUPS_WITH_SCHEME
;
2307 if (!_cups_strncasecmp(token
, "WITH-ALL-", 9))
2308 last_expect
->with_flags
|= _CUPS_WITH_ALL
;
2311 if (!get_token(fp
, temp
, sizeof(temp
), &linenum
))
2313 print_fatal_error(outfile
, "Missing %s value on line %d.", token
, linenum
);
2321 * Expand any variables in the value and then save it.
2324 expand_variables(vars
, token
, temp
, sizeof(token
));
2326 tokenptr
= token
+ strlen(token
) - 1;
2328 if (token
[0] == '/' && tokenptr
> token
&& *tokenptr
== '/')
2331 * WITH-VALUE is a POSIX extended regular expression.
2334 last_expect
->with_value
= calloc(1, (size_t)(tokenptr
- token
));
2335 last_expect
->with_flags
|= _CUPS_WITH_REGEX
;
2337 if (last_expect
->with_value
)
2338 memcpy(last_expect
->with_value
, token
+ 1, (size_t)(tokenptr
- token
- 1));
2343 * WITH-VALUE is a literal value...
2346 char *ptr
; /* Pointer into value */
2348 for (ptr
= token
; *ptr
; ptr
++)
2350 if (*ptr
== '\\' && ptr
[1])
2353 * Remove \ from \foo...
2356 _cups_strcpy(ptr
, ptr
+ 1);
2360 last_expect
->with_value
= strdup(token
);
2361 last_expect
->with_flags
|= _CUPS_WITH_LITERAL
;
2366 print_fatal_error(outfile
, "%s without a preceding EXPECT on line %d.", token
,
2372 else if (!_cups_strcasecmp(token
, "DISPLAY"))
2375 * Display attributes...
2378 if (num_displayed
>= (int)(sizeof(displayed
) / sizeof(displayed
[0])))
2380 print_fatal_error(outfile
, "Too many DISPLAY's on line %d", linenum
);
2385 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
2387 print_fatal_error(outfile
, "Missing DISPLAY name on line %d.", linenum
);
2392 displayed
[num_displayed
] = strdup(token
);
2397 print_fatal_error(outfile
, "Unexpected token %s seen on line %d.", token
,
2405 * Submit the IPP request...
2410 ippSetVersion(request
, version
/ 10, version
% 10);
2411 ippSetOperation(request
, op
);
2412 ippSetRequestId(request
, request_id
);
2414 if (Output
== _CUPS_OUTPUT_PLIST
)
2416 fputs("<dict>\n", outfile
);
2417 fputs("<key>Name</key>\n", outfile
);
2418 print_xml_string(outfile
, "string", name
);
2421 fputs("<key>FileId</key>\n", outfile
);
2422 print_xml_string(outfile
, "string", file_id
);
2426 fputs("<key>TestId</key>\n", outfile
);
2427 print_xml_string(outfile
, "string", test_id
);
2429 fputs("<key>Version</key>\n", outfile
);
2430 fprintf(outfile
, "<string>%d.%d</string>\n", version
/ 10, version
% 10);
2431 fputs("<key>Operation</key>\n", outfile
);
2432 print_xml_string(outfile
, "string", ippOpString(op
));
2433 fputs("<key>RequestId</key>\n", outfile
);
2434 fprintf(outfile
, "<integer>%d</integer>\n", request_id
);
2435 fputs("<key>RequestAttributes</key>\n", outfile
);
2436 fputs("<array>\n", outfile
);
2439 fputs("<dict>\n", outfile
);
2440 for (attrptr
= request
->attrs
,
2441 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2443 attrptr
= attrptr
->next
)
2444 print_attr(outfile
, Output
, attrptr
, &group
);
2445 fputs("</dict>\n", outfile
);
2447 fputs("</array>\n", outfile
);
2450 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2454 printf(" %s:\n", ippOpString(op
));
2456 for (attrptr
= request
->attrs
; attrptr
; attrptr
= attrptr
->next
)
2457 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
2460 printf(" %-68.68s [", name
);
2464 if ((skip_previous
&& !prev_pass
) || skip_test
)
2471 if (Output
== _CUPS_OUTPUT_PLIST
)
2473 fputs("<key>Successful</key>\n", outfile
);
2474 fputs("<true />\n", outfile
);
2475 fputs("<key>Skipped</key>\n", outfile
);
2476 fputs("<true />\n", outfile
);
2477 fputs("<key>StatusCode</key>\n", outfile
);
2478 print_xml_string(outfile
, "string", "skip");
2479 fputs("<key>ResponseAttributes</key>\n", outfile
);
2480 fputs("<dict />\n", outfile
);
2483 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
2491 repeat_interval
= 1;
2498 status
= HTTP_STATUS_OK
;
2500 if (transfer
== _CUPS_TRANSFER_CHUNKED
||
2501 (transfer
== _CUPS_TRANSFER_AUTO
&& filename
[0]))
2504 * Send request using chunking - a 0 length means "chunk".
2512 * Send request using content length...
2515 length
= ippLength(request
);
2517 if (filename
[0] && (reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2520 * Read the file to get the uncompressed file size...
2523 while ((bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2524 length
+= (size_t)bytes
;
2526 cupsFileClose(reqfile
);
2531 * Send the request...
2538 if (status
!= HTTP_STATUS_ERROR
)
2540 while (!response
&& !Cancel
&& prev_pass
)
2542 status
= cupsSendRequest(http
, request
, resource
, length
);
2546 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
2547 #endif /* HAVE_LIBZ */
2549 if (!Cancel
&& status
== HTTP_STATUS_CONTINUE
&&
2550 request
->state
== IPP_DATA
&& filename
[0])
2552 if ((reqfile
= cupsFileOpen(filename
, "r")) != NULL
)
2555 (bytes
= cupsFileRead(reqfile
, buffer
, sizeof(buffer
))) > 0)
2556 if ((status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
)) != HTTP_STATUS_CONTINUE
)
2559 cupsFileClose(reqfile
);
2563 snprintf(buffer
, sizeof(buffer
), "%s: %s", filename
,
2565 _cupsSetError(IPP_INTERNAL_ERROR
, buffer
, 0);
2567 status
= HTTP_STATUS_ERROR
;
2572 * Get the server's response...
2575 if (!Cancel
&& status
!= HTTP_STATUS_ERROR
)
2577 response
= cupsGetResponse(http
, resource
);
2578 status
= httpGetStatus(http
);
2581 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2583 http
->error
!= WSAETIMEDOUT
)
2585 http
->error
!= ETIMEDOUT
)
2588 if (httpReconnect(http
))
2591 else if (status
== HTTP_STATUS_ERROR
|| status
== HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
)
2596 else if (status
!= HTTP_STATUS_OK
)
2600 if (status
== HTTP_STATUS_UNAUTHORIZED
)
2608 if (!Cancel
&& status
== HTTP_STATUS_ERROR
&& http
->error
!= EINVAL
&&
2610 http
->error
!= WSAETIMEDOUT
)
2612 http
->error
!= ETIMEDOUT
)
2615 if (httpReconnect(http
))
2618 else if (status
== HTTP_STATUS_ERROR
)
2621 httpReconnect(http
);
2625 else if (status
!= HTTP_STATUS_OK
)
2632 * Check results of request...
2635 cupsArrayClear(errors
);
2637 if (http
->version
!= HTTP_1_1
)
2638 add_stringf(errors
, "Bad HTTP version (%d.%d)", http
->version
/ 100,
2639 http
->version
% 100);
2644 * No response, log error...
2647 add_stringf(errors
, "IPP request failed with status %s (%s)",
2648 ippErrorString(cupsLastError()),
2649 cupsLastErrorString());
2654 * Collect common attribute values...
2657 if ((attrptr
= ippFindAttribute(response
, "job-id",
2658 IPP_TAG_INTEGER
)) != NULL
)
2660 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2661 set_variable(outfile
, vars
, "job-id", temp
);
2664 if ((attrptr
= ippFindAttribute(response
, "job-uri",
2665 IPP_TAG_URI
)) != NULL
)
2666 set_variable(outfile
, vars
, "job-uri", attrptr
->values
[0].string
.text
);
2668 if ((attrptr
= ippFindAttribute(response
, "notify-subscription-id",
2669 IPP_TAG_INTEGER
)) != NULL
)
2671 snprintf(temp
, sizeof(temp
), "%d", attrptr
->values
[0].integer
);
2672 set_variable(outfile
, vars
, "notify-subscription-id", temp
);
2676 * Check response, validating groups and attributes and logging errors
2680 if (response
->state
!= IPP_DATA
)
2682 "Missing end-of-attributes-tag in response "
2683 "(RFC 2910 section 3.5.1)");
2686 (response
->request
.status
.version
[0] != (version
/ 10) ||
2687 response
->request
.status
.version
[1] != (version
% 10)))
2689 "Bad version %d.%d in response - expected %d.%d "
2690 "(RFC 2911 section 3.1.8).",
2691 response
->request
.status
.version
[0],
2692 response
->request
.status
.version
[1],
2693 version
/ 10, version
% 10);
2695 if (response
->request
.status
.request_id
!= request_id
)
2697 "Bad request ID %d in response - expected %d "
2698 "(RFC 2911 section 3.1.1)",
2699 response
->request
.status
.request_id
, request_id
);
2701 attrptr
= response
->attrs
;
2704 "Missing first attribute \"attributes-charset "
2705 "(charset)\" in group operation-attributes-tag "
2706 "(RFC 2911 section 3.1.4).");
2709 if (!attrptr
->name
||
2710 attrptr
->value_tag
!= IPP_TAG_CHARSET
||
2711 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2712 attrptr
->num_values
!= 1 ||
2713 strcmp(attrptr
->name
, "attributes-charset"))
2715 "Bad first attribute \"%s (%s%s)\" in group %s, "
2716 "expected \"attributes-charset (charset)\" in "
2717 "group operation-attributes-tag (RFC 2911 section "
2719 attrptr
->name
? attrptr
->name
: "(null)",
2720 attrptr
->num_values
> 1 ? "1setOf " : "",
2721 ippTagString(attrptr
->value_tag
),
2722 ippTagString(attrptr
->group_tag
));
2724 attrptr
= attrptr
->next
;
2727 "Missing second attribute \"attributes-natural-"
2728 "language (naturalLanguage)\" in group "
2729 "operation-attributes-tag (RFC 2911 section "
2731 else if (!attrptr
->name
||
2732 attrptr
->value_tag
!= IPP_TAG_LANGUAGE
||
2733 attrptr
->group_tag
!= IPP_TAG_OPERATION
||
2734 attrptr
->num_values
!= 1 ||
2735 strcmp(attrptr
->name
, "attributes-natural-language"))
2737 "Bad first attribute \"%s (%s%s)\" in group %s, "
2738 "expected \"attributes-natural-language "
2739 "(naturalLanguage)\" in group "
2740 "operation-attributes-tag (RFC 2911 section "
2742 attrptr
->name
? attrptr
->name
: "(null)",
2743 attrptr
->num_values
> 1 ? "1setOf " : "",
2744 ippTagString(attrptr
->value_tag
),
2745 ippTagString(attrptr
->group_tag
));
2748 if ((attrptr
= ippFindAttribute(response
, "status-message",
2749 IPP_TAG_ZERO
)) != NULL
)
2751 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2753 "status-message (text(255)) has wrong value tag "
2754 "%s (RFC 2911 section 3.1.6.2).",
2755 ippTagString(attrptr
->value_tag
));
2756 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2758 "status-message (text(255)) has wrong group tag "
2759 "%s (RFC 2911 section 3.1.6.2).",
2760 ippTagString(attrptr
->group_tag
));
2761 if (attrptr
->num_values
!= 1)
2763 "status-message (text(255)) has %d values "
2764 "(RFC 2911 section 3.1.6.2).",
2765 attrptr
->num_values
);
2766 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2767 strlen(attrptr
->values
[0].string
.text
) > 255)
2769 "status-message (text(255)) has bad length %d"
2770 " (RFC 2911 section 3.1.6.2).",
2771 (int)strlen(attrptr
->values
[0].string
.text
));
2774 if ((attrptr
= ippFindAttribute(response
, "detailed-status-message",
2775 IPP_TAG_ZERO
)) != NULL
)
2777 if (attrptr
->value_tag
!= IPP_TAG_TEXT
)
2779 "detailed-status-message (text(MAX)) has wrong "
2780 "value tag %s (RFC 2911 section 3.1.6.3).",
2781 ippTagString(attrptr
->value_tag
));
2782 if (attrptr
->group_tag
!= IPP_TAG_OPERATION
)
2784 "detailed-status-message (text(MAX)) has wrong "
2785 "group tag %s (RFC 2911 section 3.1.6.3).",
2786 ippTagString(attrptr
->group_tag
));
2787 if (attrptr
->num_values
!= 1)
2789 "detailed-status-message (text(MAX)) has %d values"
2790 " (RFC 2911 section 3.1.6.3).",
2791 attrptr
->num_values
);
2792 if (attrptr
->value_tag
== IPP_TAG_TEXT
&&
2793 strlen(attrptr
->values
[0].string
.text
) > 1023)
2795 "detailed-status-message (text(MAX)) has bad "
2796 "length %d (RFC 2911 section 3.1.6.3).",
2797 (int)strlen(attrptr
->values
[0].string
.text
));
2800 a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2802 for (attrptr
= response
->attrs
,
2803 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
2805 attrptr
= attrptr
->next
)
2807 if (attrptr
->group_tag
!= group
)
2809 int out_of_order
= 0; /* Are attribute groups out-of-order? */
2812 switch (attrptr
->group_tag
)
2817 case IPP_TAG_OPERATION
:
2821 case IPP_TAG_UNSUPPORTED_GROUP
:
2822 if (group
!= IPP_TAG_OPERATION
)
2827 case IPP_TAG_PRINTER
:
2828 if (group
!= IPP_TAG_OPERATION
&&
2829 group
!= IPP_TAG_UNSUPPORTED_GROUP
)
2833 case IPP_TAG_SUBSCRIPTION
:
2834 if (group
> attrptr
->group_tag
&&
2835 group
!= IPP_TAG_DOCUMENT
)
2840 if (group
> attrptr
->group_tag
)
2846 add_stringf(errors
, "Attribute groups out of order (%s < %s)",
2847 ippTagString(attrptr
->group_tag
),
2848 ippTagString(group
));
2850 if (attrptr
->group_tag
!= IPP_TAG_ZERO
)
2851 group
= attrptr
->group_tag
;
2854 validate_attr(outfile
, errors
, attrptr
);
2858 if (cupsArrayFind(a
, attrptr
->name
))
2859 add_stringf(errors
, "Duplicate \"%s\" attribute in %s group",
2860 attrptr
->name
, ippTagString(group
));
2862 cupsArrayAdd(a
, attrptr
->name
);
2869 * Now check the test-defined expected status-code and attribute
2873 for (i
= 0; i
< num_statuses
; i
++)
2875 if (statuses
[i
].if_defined
&&
2876 !get_variable(vars
, statuses
[i
].if_defined
))
2879 if (statuses
[i
].if_not_defined
&&
2880 get_variable(vars
, statuses
[i
].if_not_defined
))
2883 if (response
->request
.status
.status_code
== statuses
[i
].status
)
2885 if (statuses
[i
].repeat_match
&&
2886 repeat_count
< statuses
[i
].repeat_limit
)
2889 if (statuses
[i
].define_match
)
2890 set_variable(outfile
, vars
, statuses
[i
].define_match
, "1");
2896 if (statuses
[i
].repeat_no_match
&&
2897 repeat_count
< statuses
[i
].repeat_limit
)
2900 if (statuses
[i
].define_no_match
)
2902 set_variable(outfile
, vars
, statuses
[i
].define_no_match
, "1");
2908 if (i
== num_statuses
&& num_statuses
> 0)
2910 for (i
= 0; i
< num_statuses
; i
++)
2912 if (statuses
[i
].if_defined
&&
2913 !get_variable(vars
, statuses
[i
].if_defined
))
2916 if (statuses
[i
].if_not_defined
&&
2917 get_variable(vars
, statuses
[i
].if_not_defined
))
2920 if (!statuses
[i
].repeat_match
)
2921 add_stringf(errors
, "EXPECTED: STATUS %s (got %s)",
2922 ippErrorString(statuses
[i
].status
),
2923 ippErrorString(cupsLastError()));
2926 if ((attrptr
= ippFindAttribute(response
, "status-message",
2927 IPP_TAG_TEXT
)) != NULL
)
2928 add_stringf(errors
, "status-message=\"%s\"",
2929 attrptr
->values
[0].string
.text
);
2932 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
2934 if (expect
->if_defined
&& !get_variable(vars
, expect
->if_defined
))
2937 if (expect
->if_not_defined
&&
2938 get_variable(vars
, expect
->if_not_defined
))
2941 found
= ippFindAttribute(response
, expect
->name
, IPP_TAG_ZERO
);
2943 if ((found
&& expect
->not_expect
) ||
2944 (!found
&& !(expect
->not_expect
|| expect
->optional
)) ||
2945 (found
&& !expect_matches(expect
, found
->value_tag
)) ||
2946 (found
&& expect
->in_group
&&
2947 found
->group_tag
!= expect
->in_group
))
2949 if (expect
->define_no_match
)
2950 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
2951 else if (!expect
->define_match
&& !expect
->define_value
)
2953 if (found
&& expect
->not_expect
)
2954 add_stringf(errors
, "NOT EXPECTED: %s", expect
->name
);
2955 else if (!found
&& !(expect
->not_expect
|| expect
->optional
))
2956 add_stringf(errors
, "EXPECTED: %s", expect
->name
);
2959 if (!expect_matches(expect
, found
->value_tag
))
2960 add_stringf(errors
, "EXPECTED: %s OF-TYPE %s (got %s)",
2961 expect
->name
, expect
->of_type
,
2962 ippTagString(found
->value_tag
));
2964 if (expect
->in_group
&& found
->group_tag
!= expect
->in_group
)
2965 add_stringf(errors
, "EXPECTED: %s IN-GROUP %s (got %s).",
2966 expect
->name
, ippTagString(expect
->in_group
),
2967 ippTagString(found
->group_tag
));
2971 if (expect
->repeat_no_match
&&
2972 repeat_count
< expect
->repeat_limit
)
2979 ippAttributeString(found
, buffer
, sizeof(buffer
));
2982 !with_value(outfile
, NULL
, expect
->with_value
, expect
->with_flags
, found
,
2983 buffer
, sizeof(buffer
)))
2985 if (expect
->define_no_match
)
2986 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
2987 else if (!expect
->define_match
&& !expect
->define_value
&&
2988 !expect
->repeat_match
&& !expect
->repeat_no_match
)
2990 if (expect
->with_flags
& _CUPS_WITH_REGEX
)
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 add_stringf(errors
, "EXPECTED: %s %s \"%s\"",
2999 (expect
->with_flags
& _CUPS_WITH_ALL
) ?
3000 "WITH-ALL-VALUES" : "WITH-VALUE",
3001 expect
->with_value
);
3003 with_value(outfile
, errors
, expect
->with_value
, expect
->with_flags
, found
,
3004 buffer
, sizeof(buffer
));
3007 if (expect
->repeat_no_match
&&
3008 repeat_count
< expect
->repeat_limit
)
3014 if (found
&& expect
->count
> 0 &&
3015 found
->num_values
!= expect
->count
)
3017 if (expect
->define_no_match
)
3018 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3019 else if (!expect
->define_match
&& !expect
->define_value
)
3021 add_stringf(errors
, "EXPECTED: %s COUNT %d (got %d)", expect
->name
,
3022 expect
->count
, found
->num_values
);
3025 if (expect
->repeat_no_match
&&
3026 repeat_count
< expect
->repeat_limit
)
3032 if (found
&& expect
->same_count_as
)
3034 attrptr
= ippFindAttribute(response
, expect
->same_count_as
,
3037 if (!attrptr
|| attrptr
->num_values
!= found
->num_values
)
3039 if (expect
->define_no_match
)
3040 set_variable(outfile
, vars
, expect
->define_no_match
, "1");
3041 else if (!expect
->define_match
&& !expect
->define_value
)
3045 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3046 "(not returned)", expect
->name
,
3047 found
->num_values
, expect
->same_count_as
);
3048 else if (attrptr
->num_values
!= found
->num_values
)
3050 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3051 "(%d values)", expect
->name
, found
->num_values
,
3052 expect
->same_count_as
, attrptr
->num_values
);
3055 if (expect
->repeat_no_match
&&
3056 repeat_count
< expect
->repeat_limit
)
3063 if (found
&& expect
->define_match
)
3064 set_variable(outfile
, vars
, expect
->define_match
, "1");
3066 if (found
&& expect
->define_value
)
3068 if (!expect
->with_value
)
3070 int last
= ippGetCount(found
) - 1;
3071 /* Last element in attribute */
3073 switch (ippGetValueTag(found
))
3076 case IPP_TAG_INTEGER
:
3077 snprintf(buffer
, sizeof(buffer
), "%d", ippGetInteger(found
, last
));
3080 case IPP_TAG_BOOLEAN
:
3081 if (ippGetBoolean(found
, last
))
3082 strlcpy(buffer
, "true", sizeof(buffer
));
3084 strlcpy(buffer
, "false", sizeof(buffer
));
3087 case IPP_TAG_RESOLUTION
:
3089 int xres
, /* Horizontal resolution */
3090 yres
; /* Vertical resolution */
3091 ipp_res_t units
; /* Resolution units */
3093 xres
= ippGetResolution(found
, last
, &yres
, &units
);
3096 snprintf(buffer
, sizeof(buffer
), "%d%s", xres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3098 snprintf(buffer
, sizeof(buffer
), "%dx%d%s", xres
, yres
, units
== IPP_RES_PER_INCH
? "dpi" : "dpcm");
3102 case IPP_TAG_CHARSET
:
3103 case IPP_TAG_KEYWORD
:
3104 case IPP_TAG_LANGUAGE
:
3105 case IPP_TAG_MIMETYPE
:
3107 case IPP_TAG_NAMELANG
:
3109 case IPP_TAG_TEXTLANG
:
3111 case IPP_TAG_URISCHEME
:
3112 strlcpy(buffer
, ippGetString(found
, last
, NULL
), sizeof(buffer
));
3116 ippAttributeString(found
, buffer
, sizeof(buffer
));
3121 set_variable(outfile
, vars
, expect
->define_value
, buffer
);
3124 if (found
&& expect
->repeat_match
&&
3125 repeat_count
< expect
->repeat_limit
)
3131 * If we are going to repeat this test, sleep 1 second so we don't flood
3132 * the printer with requests...
3137 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3139 printf("%04d]\n", repeat_count
);
3142 if (num_displayed
> 0)
3144 for (attrptr
= ippFirstAttribute(response
); attrptr
; attrptr
= ippNextAttribute(response
))
3146 const char *attrname
= ippGetName(attrptr
);
3149 for (i
= 0; i
< num_displayed
; i
++)
3151 if (!strcmp(displayed
[i
], attrname
))
3153 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3162 sleep((unsigned)repeat_interval
);
3163 repeat_interval
= _cupsNextDelay(repeat_interval
, &repeat_prev
);
3165 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3167 printf(" %-68.68s [", name
);
3172 while (repeat_test
);
3178 if (cupsArrayCount(errors
) > 0)
3179 prev_pass
= pass
= 0;
3186 if (Output
== _CUPS_OUTPUT_PLIST
)
3188 fputs("<key>Successful</key>\n", outfile
);
3189 fputs(prev_pass
? "<true />\n" : "<false />\n", outfile
);
3190 fputs("<key>StatusCode</key>\n", outfile
);
3191 print_xml_string(outfile
, "string", ippErrorString(cupsLastError()));
3192 fputs("<key>ResponseAttributes</key>\n", outfile
);
3193 fputs("<array>\n", outfile
);
3194 fputs("<dict>\n", outfile
);
3195 for (attrptr
= response
? response
->attrs
: NULL
,
3196 group
= attrptr
? attrptr
->group_tag
: IPP_TAG_ZERO
;
3198 attrptr
= attrptr
->next
)
3199 print_attr(outfile
, Output
, attrptr
, &group
);
3200 fputs("</dict>\n", outfile
);
3201 fputs("</array>\n", outfile
);
3204 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3206 puts(prev_pass
? "PASS]" : "FAIL]");
3208 if (!prev_pass
|| (Verbosity
&& response
))
3210 printf(" RECEIVED: %lu bytes in response\n",
3211 (unsigned long)ippLength(response
));
3212 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3213 cupsLastErrorString());
3215 if (Verbosity
&& response
)
3217 for (attrptr
= response
->attrs
;
3219 attrptr
= attrptr
->next
)
3220 print_attr(stdout
, _CUPS_OUTPUT_TEST
, attrptr
, NULL
);
3224 else if (!prev_pass
&& Output
!= _CUPS_OUTPUT_QUIET
)
3225 fprintf(stderr
, "%s\n", cupsLastErrorString());
3227 if (prev_pass
&& Output
>= _CUPS_OUTPUT_LIST
&& !Verbosity
&&
3230 size_t width
; /* Length of value */
3232 for (i
= 0; i
< num_displayed
; i
++)
3234 widths
[i
] = strlen(displayed
[i
]);
3236 for (attrptr
= ippFindAttribute(response
, displayed
[i
], IPP_TAG_ZERO
);
3238 attrptr
= ippFindNextAttribute(response
, displayed
[i
],
3241 width
= ippAttributeString(attrptr
, NULL
, 0);
3242 if (width
> widths
[i
])
3247 if (Output
== _CUPS_OUTPUT_CSV
)
3248 print_csv(outfile
, NULL
, num_displayed
, displayed
, widths
);
3250 print_line(outfile
, NULL
, num_displayed
, displayed
, widths
);
3252 attrptr
= response
->attrs
;
3256 while (attrptr
&& attrptr
->group_tag
<= IPP_TAG_OPERATION
)
3257 attrptr
= attrptr
->next
;
3261 if (Output
== _CUPS_OUTPUT_CSV
)
3262 print_csv(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3264 print_line(outfile
, attrptr
, num_displayed
, displayed
, widths
);
3266 while (attrptr
&& attrptr
->group_tag
> IPP_TAG_OPERATION
)
3267 attrptr
= attrptr
->next
;
3271 else if (!prev_pass
)
3273 if (Output
== _CUPS_OUTPUT_PLIST
)
3275 fputs("<key>Errors</key>\n", outfile
);
3276 fputs("<array>\n", outfile
);
3278 for (error
= (char *)cupsArrayFirst(errors
);
3280 error
= (char *)cupsArrayNext(errors
))
3281 print_xml_string(outfile
, "string", error
);
3283 fputs("</array>\n", outfile
);
3286 if (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
))
3288 for (error
= (char *)cupsArrayFirst(errors
);
3290 error
= (char *)cupsArrayNext(errors
))
3291 printf(" %s\n", error
);
3295 if (num_displayed
> 0 && !Verbosity
&& response
&& (Output
== _CUPS_OUTPUT_TEST
|| (Output
== _CUPS_OUTPUT_PLIST
&& outfile
!= stdout
)))
3297 for (attrptr
= response
->attrs
;
3299 attrptr
= attrptr
->next
)
3303 for (i
= 0; i
< num_displayed
; i
++)
3305 if (!strcmp(displayed
[i
], attrptr
->name
))
3307 print_attr(outfile
, Output
, attrptr
, NULL
);
3317 if (Output
== _CUPS_OUTPUT_PLIST
)
3318 fputs("</dict>\n", outfile
);
3322 ippDelete(response
);
3325 for (i
= 0; i
< num_statuses
; i
++)
3327 if (statuses
[i
].if_defined
)
3328 free(statuses
[i
].if_defined
);
3329 if (statuses
[i
].if_not_defined
)
3330 free(statuses
[i
].if_not_defined
);
3331 if (statuses
[i
].define_match
)
3332 free(statuses
[i
].define_match
);
3333 if (statuses
[i
].define_no_match
)
3334 free(statuses
[i
].define_no_match
);
3338 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3341 if (expect
->of_type
)
3342 free(expect
->of_type
);
3343 if (expect
->same_count_as
)
3344 free(expect
->same_count_as
);
3345 if (expect
->if_defined
)
3346 free(expect
->if_defined
);
3347 if (expect
->if_not_defined
)
3348 free(expect
->if_not_defined
);
3349 if (expect
->with_value
)
3350 free(expect
->with_value
);
3351 if (expect
->define_match
)
3352 free(expect
->define_match
);
3353 if (expect
->define_no_match
)
3354 free(expect
->define_no_match
);
3355 if (expect
->define_value
)
3356 free(expect
->define_value
);
3360 for (i
= 0; i
< num_displayed
; i
++)
3364 if (!ignore_errors
&& !prev_pass
)
3370 cupsArrayDelete(errors
);
3377 ippDelete(response
);
3379 for (i
= 0; i
< num_statuses
; i
++)
3381 if (statuses
[i
].if_defined
)
3382 free(statuses
[i
].if_defined
);
3383 if (statuses
[i
].if_not_defined
)
3384 free(statuses
[i
].if_not_defined
);
3385 if (statuses
[i
].define_match
)
3386 free(statuses
[i
].define_match
);
3387 if (statuses
[i
].define_no_match
)
3388 free(statuses
[i
].define_no_match
);
3391 for (i
= num_expects
, expect
= expects
; i
> 0; i
--, expect
++)
3394 if (expect
->of_type
)
3395 free(expect
->of_type
);
3396 if (expect
->same_count_as
)
3397 free(expect
->same_count_as
);
3398 if (expect
->if_defined
)
3399 free(expect
->if_defined
);
3400 if (expect
->if_not_defined
)
3401 free(expect
->if_not_defined
);
3402 if (expect
->with_value
)
3403 free(expect
->with_value
);
3404 if (expect
->define_match
)
3405 free(expect
->define_match
);
3406 if (expect
->define_no_match
)
3407 free(expect
->define_no_match
);
3408 if (expect
->define_value
)
3409 free(expect
->define_value
);
3412 for (i
= 0; i
< num_displayed
; i
++)
3420 * 'expand_variables()' - Expand variables in a string.
3424 expand_variables(_cups_vars_t
*vars
, /* I - Variables */
3425 char *dst
, /* I - Destination string buffer */
3426 const char *src
, /* I - Source string */
3427 size_t dstsize
) /* I - Size of destination buffer */
3429 char *dstptr
, /* Pointer into destination */
3430 *dstend
, /* End of destination */
3431 temp
[256], /* Temporary string */
3432 *tempptr
; /* Pointer into temporary string */
3433 const char *value
; /* Value to substitute */
3437 dstend
= dst
+ dstsize
- 1;
3439 while (*src
&& dstptr
< dstend
)
3444 * Substitute a string/number...
3447 if (!strncmp(src
, "$$", 2))
3452 else if (!strncmp(src
, "$ENV[", 5))
3454 strlcpy(temp
, src
+ 5, sizeof(temp
));
3456 for (tempptr
= temp
; *tempptr
; tempptr
++)
3457 if (*tempptr
== ']')
3463 value
= getenv(temp
);
3464 src
+= tempptr
- temp
+ 5;
3471 strlcpy(temp
, src
, sizeof(temp
));
3472 if ((tempptr
= strchr(temp
, '}')) != NULL
)
3475 tempptr
= temp
+ strlen(temp
);
3479 strlcpy(temp
, src
+ 1, sizeof(temp
));
3481 for (tempptr
= temp
; *tempptr
; tempptr
++)
3482 if (!isalnum(*tempptr
& 255) && *tempptr
!= '-' && *tempptr
!= '_')
3489 if (!strcmp(temp
, "uri"))
3491 else if (!strcmp(temp
, "filename"))
3492 value
= vars
->filename
;
3493 else if (!strcmp(temp
, "scheme") || !strcmp(temp
, "method"))
3494 value
= vars
->scheme
;
3495 else if (!strcmp(temp
, "username"))
3496 value
= vars
->userpass
;
3497 else if (!strcmp(temp
, "hostname"))
3498 value
= vars
->hostname
;
3499 else if (!strcmp(temp
, "port"))
3501 snprintf(temp
, sizeof(temp
), "%d", vars
->port
);
3504 else if (!strcmp(temp
, "resource"))
3505 value
= vars
->resource
;
3506 else if (!strcmp(temp
, "user"))
3509 value
= get_variable(vars
, temp
);
3511 src
+= tempptr
- temp
+ 1;
3521 strlcpy(dstptr
, value
, (size_t)(dstend
- dstptr
+ 1));
3522 dstptr
+= strlen(dstptr
);
3534 * 'expect_matches()' - Return true if the tag matches the specification.
3537 static int /* O - 1 if matches, 0 otherwise */
3539 _cups_expect_t
*expect
, /* I - Expected attribute */
3540 ipp_tag_t value_tag
) /* I - Value tag for attribute */
3542 int match
; /* Match? */
3543 char *of_type
, /* Type name to match */
3544 *next
, /* Next name to match */
3545 sep
; /* Separator character */
3549 * If we don't expect a particular type, return immediately...
3552 if (!expect
->of_type
)
3556 * Parse the "of_type" value since the string can contain multiple attribute
3557 * types separated by "," or "|"...
3560 for (of_type
= expect
->of_type
, match
= 0; !match
&& *of_type
; of_type
= next
)
3563 * Find the next separator, and set it (temporarily) to nul if present.
3566 for (next
= of_type
; *next
&& *next
!= '|' && *next
!= ','; next
++);
3568 if ((sep
= *next
) != '\0')
3572 * Support some meta-types to make it easier to write the test file.
3575 if (!strcmp(of_type
, "text"))
3576 match
= value_tag
== IPP_TAG_TEXTLANG
|| value_tag
== IPP_TAG_TEXT
;
3577 else if (!strcmp(of_type
, "name"))
3578 match
= value_tag
== IPP_TAG_NAMELANG
|| value_tag
== IPP_TAG_NAME
;
3579 else if (!strcmp(of_type
, "collection"))
3580 match
= value_tag
== IPP_TAG_BEGIN_COLLECTION
;
3582 match
= value_tag
== ippTagValue(of_type
);
3585 * Restore the separator if we have one...
3597 * 'get_collection()' - Get a collection value from the current test file.
3600 static ipp_t
* /* O - Collection value */
3601 get_collection(FILE *outfile
, /* I - Output file */
3602 _cups_vars_t
*vars
, /* I - Variables */
3603 FILE *fp
, /* I - File to read from */
3604 int *linenum
) /* IO - Line number */
3606 char token
[1024], /* Token from file */
3607 temp
[1024], /* Temporary string */
3608 attr
[128]; /* Attribute name */
3609 ipp_tag_t value
; /* Current value type */
3610 ipp_t
*col
= ippNew(); /* Collection value */
3611 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
3614 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
3616 if (!strcmp(token
, "}"))
3618 else if (!strcmp(token
, "{") && lastcol
)
3621 * Another collection value
3624 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3625 /* Collection value */
3628 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
3632 else if (!_cups_strcasecmp(token
, "MEMBER"))
3640 if (!get_token(fp
, token
, sizeof(token
), linenum
))
3642 print_fatal_error(outfile
, "Missing MEMBER value tag on line %d.", *linenum
);
3646 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
3648 print_fatal_error(outfile
, "Bad MEMBER value tag \"%s\" on line %d.", token
,
3653 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
3655 print_fatal_error(outfile
, "Missing MEMBER name on line %d.", *linenum
);
3659 if (!get_token(fp
, temp
, sizeof(temp
), linenum
))
3661 print_fatal_error(outfile
, "Missing MEMBER value on line %d.", *linenum
);
3665 expand_variables(vars
, token
, temp
, sizeof(token
));
3669 case IPP_TAG_BOOLEAN
:
3670 if (!_cups_strcasecmp(token
, "true"))
3671 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
3673 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
3676 case IPP_TAG_INTEGER
:
3678 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
3681 case IPP_TAG_RESOLUTION
:
3683 int xres
, /* X resolution */
3684 yres
; /* Y resolution */
3685 char units
[6]; /* Units */
3687 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
3688 (_cups_strcasecmp(units
, "dpi") &&
3689 _cups_strcasecmp(units
, "dpc") &&
3690 _cups_strcasecmp(units
, "dpcm") &&
3691 _cups_strcasecmp(units
, "other")))
3693 print_fatal_error(outfile
, "Bad resolution value \"%s\" on line %d.",
3698 if (!_cups_strcasecmp(units
, "dpi"))
3699 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
3700 else if (!_cups_strcasecmp(units
, "dpc") ||
3701 !_cups_strcasecmp(units
, "dpcm"))
3702 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
3704 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
3708 case IPP_TAG_RANGE
:
3710 int lowers
[4], /* Lower value */
3711 uppers
[4], /* Upper values */
3712 num_vals
; /* Number of values */
3715 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
3716 lowers
+ 0, uppers
+ 0,
3717 lowers
+ 1, uppers
+ 1,
3718 lowers
+ 2, uppers
+ 2,
3719 lowers
+ 3, uppers
+ 3);
3721 if ((num_vals
& 1) || num_vals
== 0)
3723 print_fatal_error(outfile
, "Bad rangeOfInteger value \"%s\" on line %d.",
3728 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
3733 case IPP_TAG_BEGIN_COLLECTION
:
3734 if (!strcmp(token
, "{"))
3736 ipp_t
*subcol
= get_collection(outfile
, vars
, fp
, linenum
);
3737 /* Collection value */
3741 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
3749 print_fatal_error(outfile
, "Bad collection value on line %d.", *linenum
);
3753 case IPP_TAG_STRING
:
3754 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
3758 if (!strchr(token
, ','))
3759 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
3763 * Multiple string values...
3766 int num_values
; /* Number of values */
3767 char *values
[100], /* Values */
3768 *ptr
; /* Pointer to next value */
3774 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
3777 values
[num_values
] = ptr
;
3781 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
3782 NULL
, (const char **)values
);
3792 * If we get here there was a parse error; free memory and return.
3804 * 'get_filename()' - Get a filename based on the current test file.
3807 static char * /* O - Filename */
3808 get_filename(const char *testfile
, /* I - Current test file */
3809 char *dst
, /* I - Destination filename */
3810 const char *src
, /* I - Source filename */
3811 size_t dstsize
) /* I - Size of destination buffer */
3813 char *dstptr
; /* Pointer into destination */
3814 _cups_globals_t
*cg
= _cupsGlobals();
3818 if (*src
== '<' && src
[strlen(src
) - 1] == '>')
3821 * Map <filename> to CUPS_DATADIR/ipptool/filename...
3824 snprintf(dst
, dstsize
, "%s/ipptool/%s", cg
->cups_datadir
, src
+ 1);
3825 dstptr
= dst
+ strlen(dst
) - 1;
3829 else if (*src
== '/' || !strchr(testfile
, '/')
3831 || (isalpha(*src
& 255) && src
[1] == ':')
3836 * Use the path as-is...
3839 strlcpy(dst
, src
, dstsize
);
3844 * Make path relative to testfile...
3847 strlcpy(dst
, testfile
, dstsize
);
3848 if ((dstptr
= strrchr(dst
, '/')) != NULL
)
3851 dstptr
= dst
; /* Should never happen */
3853 strlcpy(dstptr
, src
, dstsize
- (size_t)(dstptr
- dst
));
3861 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3864 static char * /* O - Pointer to string */
3865 get_string(ipp_attribute_t
*attr
, /* I - IPP attribute */
3866 int element
, /* I - Element to fetch */
3867 int flags
, /* I - Value ("with") flags */
3868 char *buffer
, /* I - Temporary buffer */
3869 size_t bufsize
) /* I - Size of temporary buffer */
3871 char *ptr
, /* Value */
3872 scheme
[256], /* URI scheme */
3873 userpass
[256], /* Username/password */
3874 hostname
[256], /* Hostname */
3875 resource
[1024]; /* Resource */
3876 int port
; /* Port number */
3879 ptr
= attr
->values
[element
].string
.text
;
3881 if (flags
& _CUPS_WITH_HOSTNAME
)
3883 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), buffer
, (int)bufsize
, &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3886 ptr
= buffer
+ strlen(buffer
) - 1;
3887 if (ptr
>= buffer
&& *ptr
== '.')
3888 *ptr
= '\0'; /* Drop trailing "." */
3892 else if (flags
& _CUPS_WITH_RESOURCE
)
3894 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, buffer
, (int)bufsize
) < HTTP_URI_STATUS_OK
)
3899 else if (flags
& _CUPS_WITH_SCHEME
)
3901 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ptr
, buffer
, (int)bufsize
, userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
3912 * 'get_token()' - Get a token from a file.
3915 static char * /* O - Token from file or NULL on EOF */
3916 get_token(FILE *fp
, /* I - File to read from */
3917 char *buf
, /* I - Buffer to read into */
3918 int buflen
, /* I - Length of buffer */
3919 int *linenum
) /* IO - Current line number */
3921 int ch
, /* Character from file */
3922 quote
; /* Quoting character */
3923 char *bufptr
, /* Pointer into buffer */
3924 *bufend
; /* End of buffer */
3930 * Skip whitespace...
3933 while (isspace(ch
= getc(fp
)))
3945 else if (ch
== '\'' || ch
== '\"')
3948 * Quoted text or regular expression...
3953 bufend
= buf
+ buflen
- 1;
3955 while ((ch
= getc(fp
)) != EOF
)
3960 * Escape next character...
3963 if (bufptr
< bufend
)
3964 *bufptr
++ = (char)ch
;
3966 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
3967 *bufptr
++ = (char)ch
;
3969 else if (ch
== quote
)
3971 else if (bufptr
< bufend
)
3972 *bufptr
++ = (char)ch
;
3985 while ((ch
= getc(fp
)) != EOF
)
3991 else if (ch
== '{' || ch
== '}' || ch
== ',')
4001 * Whitespace delimited text...
4007 bufend
= buf
+ buflen
- 1;
4009 while ((ch
= getc(fp
)) != EOF
)
4010 if (isspace(ch
) || ch
== '#')
4012 else if (bufptr
< bufend
)
4013 *bufptr
++ = (char)ch
;
4017 else if (ch
== '\n')
4029 * 'get_variable()' - Get the value of a variable.
4032 static char * /* O - Value or NULL */
4033 get_variable(_cups_vars_t
*vars
, /* I - Variables */
4034 const char *name
) /* I - Variable name */
4036 _cups_var_t key
, /* Search key */
4037 *match
; /* Matching variable, if any */
4040 key
.name
= (char *)name
;
4041 match
= cupsArrayFind(vars
->vars
, &key
);
4043 return (match
? match
->value
: NULL
);
4048 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
4052 static char * /* O - ISO 8601 date/time string */
4053 iso_date(ipp_uchar_t
*date
) /* I - IPP (RFC 1903) date/time value */
4055 time_t utctime
; /* UTC time since 1970 */
4056 struct tm
*utcdate
; /* UTC date/time */
4057 static char buffer
[255]; /* String buffer */
4060 utctime
= ippDateToTime(date
);
4061 utcdate
= gmtime(&utctime
);
4063 snprintf(buffer
, sizeof(buffer
), "%04d-%02d-%02dT%02d:%02d:%02dZ",
4064 utcdate
->tm_year
+ 1900, utcdate
->tm_mon
+ 1, utcdate
->tm_mday
,
4065 utcdate
->tm_hour
, utcdate
->tm_min
, utcdate
->tm_sec
);
4072 * 'password_cb()' - Password callback for authenticated tests.
4075 static const char * /* O - Password */
4076 password_cb(const char *prompt
) /* I - Prompt (unused) */
4080 if (PasswordTries
< 3)
4084 cupsSetUser(Username
);
4094 * 'pause_message()' - Display the message and pause until the user presses a key.
4098 pause_message(const char *message
) /* I - Message */
4101 HANDLE tty
; /* Console handle */
4102 DWORD mode
; /* Console mode */
4103 char key
; /* Key press */
4104 DWORD bytes
; /* Bytes read for key press */
4108 * Disable input echo and set raw input...
4111 if ((tty
= GetStdHandle(STD_INPUT_HANDLE
)) == INVALID_HANDLE_VALUE
)
4114 if (!GetConsoleMode(tty
, &mode
))
4117 if (!SetConsoleMode(tty
, 0))
4121 int tty
; /* /dev/tty - never read from stdin */
4122 struct termios original
, /* Original input mode */
4123 noecho
; /* No echo input mode */
4124 char key
; /* Current key press */
4128 * Disable input echo and set raw input...
4131 if ((tty
= open("/dev/tty", O_RDONLY
)) < 0)
4134 if (tcgetattr(tty
, &original
))
4141 noecho
.c_lflag
&= (tcflag_t
)~(ICANON
| ECHO
| ECHOE
| ISIG
);
4143 if (tcsetattr(tty
, TCSAFLUSH
, &noecho
))
4151 * Display the prompt...
4154 printf("%s\n---- PRESS ANY KEY ----", message
);
4162 ReadFile(tty
, &key
, 1, &bytes
, NULL
);
4168 SetConsoleMode(tty
, mode
);
4181 tcsetattr(tty
, TCSAFLUSH
, &original
);
4186 * Erase the "press any key" prompt...
4189 fputs("\r \r", stdout
);
4195 * 'print_attr()' - Print an attribute on the screen.
4199 print_attr(FILE *outfile
, /* I - Output file */
4200 int format
, /* I - Output format */
4201 ipp_attribute_t
*attr
, /* I - Attribute to print */
4202 ipp_tag_t
*group
) /* IO - Current group */
4204 int i
; /* Looping var */
4205 ipp_attribute_t
*colattr
; /* Collection attribute */
4208 if (format
== _CUPS_OUTPUT_PLIST
)
4210 if (!attr
->name
|| (group
&& *group
!= attr
->group_tag
))
4212 if (attr
->group_tag
!= IPP_TAG_ZERO
)
4214 fputs("</dict>\n", outfile
);
4215 fputs("<dict>\n", outfile
);
4219 *group
= attr
->group_tag
;
4225 print_xml_string(outfile
, "key", attr
->name
);
4226 if (attr
->num_values
> 1)
4227 fputs("<array>\n", outfile
);
4229 switch (attr
->value_tag
)
4231 case IPP_TAG_INTEGER
:
4233 for (i
= 0; i
< attr
->num_values
; i
++)
4234 fprintf(outfile
, "<integer>%d</integer>\n", attr
->values
[i
].integer
);
4237 case IPP_TAG_BOOLEAN
:
4238 for (i
= 0; i
< attr
->num_values
; i
++)
4239 fputs(attr
->values
[i
].boolean
? "<true />\n" : "<false />\n", outfile
);
4242 case IPP_TAG_RANGE
:
4243 for (i
= 0; i
< attr
->num_values
; i
++)
4244 fprintf(outfile
, "<dict><key>lower</key><integer>%d</integer>"
4245 "<key>upper</key><integer>%d</integer></dict>\n",
4246 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4249 case IPP_TAG_RESOLUTION
:
4250 for (i
= 0; i
< attr
->num_values
; i
++)
4251 fprintf(outfile
, "<dict><key>xres</key><integer>%d</integer>"
4252 "<key>yres</key><integer>%d</integer>"
4253 "<key>units</key><string>%s</string></dict>\n",
4254 attr
->values
[i
].resolution
.xres
,
4255 attr
->values
[i
].resolution
.yres
,
4256 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
4261 for (i
= 0; i
< attr
->num_values
; i
++)
4262 fprintf(outfile
, "<date>%s</date>\n", iso_date(attr
->values
[i
].date
));
4265 case IPP_TAG_STRING
:
4266 for (i
= 0; i
< attr
->num_values
; i
++)
4268 char buffer
[IPP_MAX_LENGTH
* 5 / 4 + 1];
4271 fprintf(outfile
, "<data>%s</data>\n",
4272 httpEncode64_2(buffer
, sizeof(buffer
),
4273 attr
->values
[i
].unknown
.data
,
4274 attr
->values
[i
].unknown
.length
));
4280 case IPP_TAG_KEYWORD
:
4281 case IPP_TAG_CHARSET
:
4283 case IPP_TAG_MIMETYPE
:
4284 case IPP_TAG_LANGUAGE
:
4285 for (i
= 0; i
< attr
->num_values
; i
++)
4286 print_xml_string(outfile
, "string", attr
->values
[i
].string
.text
);
4289 case IPP_TAG_TEXTLANG
:
4290 case IPP_TAG_NAMELANG
:
4291 for (i
= 0; i
< attr
->num_values
; i
++)
4293 fputs("<dict><key>language</key><string>", outfile
);
4294 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.language
);
4295 fputs("</string><key>string</key><string>", outfile
);
4296 print_xml_string(outfile
, NULL
, attr
->values
[i
].string
.text
);
4297 fputs("</string></dict>\n", outfile
);
4301 case IPP_TAG_BEGIN_COLLECTION
:
4302 for (i
= 0; i
< attr
->num_values
; i
++)
4304 fputs("<dict>\n", outfile
);
4305 for (colattr
= attr
->values
[i
].collection
->attrs
;
4307 colattr
= colattr
->next
)
4308 print_attr(outfile
, format
, colattr
, NULL
);
4309 fputs("</dict>\n", outfile
);
4314 fprintf(outfile
, "<string><<%s>></string>\n", ippTagString(attr
->value_tag
));
4318 if (attr
->num_values
> 1)
4319 fputs("</array>\n", outfile
);
4323 char buffer
[8192]; /* Value buffer */
4325 if (format
== _CUPS_OUTPUT_TEST
)
4329 fputs(" -- separator --\n", outfile
);
4333 fprintf(outfile
, " %s (%s%s) = ", attr
->name
, attr
->num_values
> 1 ? "1setOf " : "", ippTagString(attr
->value_tag
));
4336 ippAttributeString(attr
, buffer
, sizeof(buffer
));
4337 fprintf(outfile
, "%s\n", buffer
);
4343 * 'print_csv()' - Print a line of CSV text.
4348 FILE *outfile
, /* I - Output file */
4349 ipp_attribute_t
*attr
, /* I - First attribute for line */
4350 int num_displayed
, /* I - Number of attributes to display */
4351 char **displayed
, /* I - Attributes to display */
4352 size_t *widths
) /* I - Column widths */
4354 int i
; /* Looping var */
4355 size_t maxlength
; /* Max length of all columns */
4356 char *buffer
, /* String buffer */
4357 *bufptr
; /* Pointer into buffer */
4358 ipp_attribute_t
*current
; /* Current attribute */
4362 * Get the maximum string length we have to show and allocate...
4365 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4366 if (widths
[i
] > maxlength
)
4367 maxlength
= widths
[i
];
4371 if ((buffer
= malloc(maxlength
)) == NULL
)
4375 * Loop through the attributes to display...
4380 for (i
= 0; i
< num_displayed
; i
++)
4383 fputc(',', outfile
);
4387 for (current
= attr
; current
; current
= current
->next
)
4391 else if (!strcmp(current
->name
, displayed
[i
]))
4393 ippAttributeString(current
, buffer
, maxlength
);
4398 if (strchr(buffer
, ',') != NULL
|| strchr(buffer
, '\"') != NULL
||
4399 strchr(buffer
, '\\') != NULL
)
4401 putc('\"', outfile
);
4402 for (bufptr
= buffer
; *bufptr
; bufptr
++)
4404 if (*bufptr
== '\\' || *bufptr
== '\"')
4405 putc('\\', outfile
);
4406 putc(*bufptr
, outfile
);
4408 putc('\"', outfile
);
4411 fputs(buffer
, outfile
);
4413 putc('\n', outfile
);
4417 for (i
= 0; i
< num_displayed
; i
++)
4422 fputs(displayed
[i
], outfile
);
4424 putc('\n', outfile
);
4432 * 'print_fatal_error()' - Print a fatal error message.
4436 print_fatal_error(FILE *outfile
, /* I - Output file */
4437 const char *s
, /* I - Printf-style format string */
4438 ...) /* I - Additional arguments as needed */
4440 char buffer
[10240]; /* Format buffer */
4441 va_list ap
; /* Pointer to arguments */
4445 * Format the error message...
4449 vsnprintf(buffer
, sizeof(buffer
), s
, ap
);
4456 if (Output
== _CUPS_OUTPUT_PLIST
)
4458 print_xml_header(outfile
);
4459 print_xml_trailer(outfile
, 0, buffer
);
4462 _cupsLangPrintf(stderr
, "ipptool: %s", buffer
);
4467 * 'print_line()' - Print a line of formatted or CSV text.
4472 FILE *outfile
, /* I - Output file */
4473 ipp_attribute_t
*attr
, /* I - First attribute for line */
4474 int num_displayed
, /* I - Number of attributes to display */
4475 char **displayed
, /* I - Attributes to display */
4476 size_t *widths
) /* I - Column widths */
4478 int i
; /* Looping var */
4479 size_t maxlength
; /* Max length of all columns */
4480 char *buffer
; /* String buffer */
4481 ipp_attribute_t
*current
; /* Current attribute */
4485 * Get the maximum string length we have to show and allocate...
4488 for (i
= 1, maxlength
= widths
[0]; i
< num_displayed
; i
++)
4489 if (widths
[i
] > maxlength
)
4490 maxlength
= widths
[i
];
4494 if ((buffer
= malloc(maxlength
)) == NULL
)
4498 * Loop through the attributes to display...
4503 for (i
= 0; i
< num_displayed
; i
++)
4510 for (current
= attr
; current
; current
= current
->next
)
4514 else if (!strcmp(current
->name
, displayed
[i
]))
4516 ippAttributeString(current
, buffer
, maxlength
);
4521 fprintf(outfile
, "%*s", (int)-widths
[i
], buffer
);
4523 putc('\n', outfile
);
4527 for (i
= 0; i
< num_displayed
; i
++)
4532 fprintf(outfile
, "%*s", (int)-widths
[i
], displayed
[i
]);
4534 putc('\n', outfile
);
4536 for (i
= 0; i
< num_displayed
; i
++)
4541 memset(buffer
, '-', widths
[i
]);
4542 buffer
[widths
[i
]] = '\0';
4543 fputs(buffer
, outfile
);
4545 putc('\n', outfile
);
4553 * 'print_xml_header()' - Print a standard XML plist header.
4557 print_xml_header(FILE *outfile
) /* I - Output file */
4561 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outfile
);
4562 fputs("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4563 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n", outfile
);
4564 fputs("<plist version=\"1.0\">\n", outfile
);
4565 fputs("<dict>\n", outfile
);
4566 fputs("<key>ipptoolVersion</key>\n", outfile
);
4567 fputs("<string>" CUPS_SVERSION
"</string>\n", outfile
);
4568 fputs("<key>Transfer</key>\n", outfile
);
4569 fprintf(outfile
, "<string>%s</string>\n",
4570 Transfer
== _CUPS_TRANSFER_AUTO
? "auto" :
4571 Transfer
== _CUPS_TRANSFER_CHUNKED
? "chunked" : "length");
4572 fputs("<key>Tests</key>\n", outfile
);
4573 fputs("<array>\n", outfile
);
4581 * 'print_xml_string()' - Print an XML string with escaping.
4585 print_xml_string(FILE *outfile
, /* I - Output file */
4586 const char *element
, /* I - Element name or NULL */
4587 const char *s
) /* I - String to print */
4590 fprintf(outfile
, "<%s>", element
);
4595 fputs("&", outfile
);
4597 fputs("<", outfile
);
4599 fputs(">", outfile
);
4600 else if ((*s
& 0xe0) == 0xc0)
4603 * Validate UTF-8 two-byte sequence...
4606 if ((s
[1] & 0xc0) != 0x80)
4613 putc(*s
++, outfile
);
4617 else if ((*s
& 0xf0) == 0xe0)
4620 * Validate UTF-8 three-byte sequence...
4623 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80)
4630 putc(*s
++, outfile
);
4631 putc(*s
++, outfile
);
4635 else if ((*s
& 0xf8) == 0xf0)
4638 * Validate UTF-8 four-byte sequence...
4641 if ((s
[1] & 0xc0) != 0x80 || (s
[2] & 0xc0) != 0x80 ||
4642 (s
[3] & 0xc0) != 0x80)
4649 putc(*s
++, outfile
);
4650 putc(*s
++, outfile
);
4651 putc(*s
++, outfile
);
4655 else if ((*s
& 0x80) || (*s
< ' ' && !isspace(*s
& 255)))
4658 * Invalid control character...
4670 fprintf(outfile
, "</%s>\n", element
);
4675 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4679 print_xml_trailer(FILE *outfile
, /* I - Output file */
4680 int success
, /* I - 1 on success, 0 on failure */
4681 const char *message
) /* I - Error message or NULL */
4685 fputs("</array>\n", outfile
);
4686 fputs("<key>Successful</key>\n", outfile
);
4687 fputs(success
? "<true />\n" : "<false />\n", outfile
);
4690 fputs("<key>ErrorMessage</key>\n", outfile
);
4691 print_xml_string(outfile
, "string", message
);
4693 fputs("</dict>\n", outfile
);
4694 fputs("</plist>\n", outfile
);
4702 * 'set_variable()' - Set a variable value.
4706 set_variable(FILE *outfile
, /* I - Output file */
4707 _cups_vars_t
*vars
, /* I - Variables */
4708 const char *name
, /* I - Variable name */
4709 const char *value
) /* I - Value string */
4711 _cups_var_t key
, /* Search key */
4712 *var
; /* New variable */
4715 if (!_cups_strcasecmp(name
, "filename"))
4718 free(vars
->filename
);
4720 vars
->filename
= strdup(value
);
4724 key
.name
= (char *)name
;
4725 if ((var
= cupsArrayFind(vars
->vars
, &key
)) != NULL
)
4728 var
->value
= strdup(value
);
4730 else if ((var
= malloc(sizeof(_cups_var_t
))) == NULL
)
4732 print_fatal_error(outfile
, "Unable to allocate memory for variable \"%s\".", name
);
4737 var
->name
= strdup(name
);
4738 var
->value
= strdup(value
);
4740 cupsArrayAdd(vars
->vars
, var
);
4747 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4751 sigterm_handler(int sig
) /* I - Signal number (unused) */
4757 signal(SIGINT
, SIG_DFL
);
4758 signal(SIGTERM
, SIG_DFL
);
4764 * 'timeout_cb()' - Handle HTTP timeouts.
4767 static int /* O - 1 to continue, 0 to cancel */
4768 timeout_cb(http_t
*http
, /* I - Connection to server */
4769 void *user_data
) /* I - User data (unused) */
4771 int buffered
= 0; /* Bytes buffered but not yet sent */
4777 * If the socket still have data waiting to be sent to the printer (as can
4778 * happen if the printer runs out of paper), continue to wait until the output
4779 * buffer is empty...
4782 #ifdef SO_NWRITE /* OS X and some versions of Linux */
4783 socklen_t len
= sizeof(buffered
); /* Size of return value */
4785 if (getsockopt(httpGetFd(http
), SOL_SOCKET
, SO_NWRITE
, &buffered
, &len
))
4788 #elif defined(SIOCOUTQ) /* Others except Windows */
4789 if (ioctl(httpGetFd(http
), SIOCOUTQ
, &buffered
))
4792 #else /* Windows (not possible) */
4794 #endif /* SO_NWRITE */
4796 return (buffered
> 0);
4801 * 'usage()' - Show program usage.
4807 _cupsLangPuts(stderr
, _("Usage: ipptool [options] URI filename [ ... "
4809 _cupsLangPuts(stderr
, _("Options:"));
4810 _cupsLangPuts(stderr
, _(" --help Show help."));
4811 _cupsLangPuts(stderr
, _(" --stop-after-include-error\n"
4812 " Stop tests after a failed INCLUDE."));
4813 _cupsLangPuts(stderr
, _(" --version Show version."));
4814 _cupsLangPuts(stderr
, _(" -4 Connect using IPv4."));
4815 _cupsLangPuts(stderr
, _(" -6 Connect using IPv6."));
4816 _cupsLangPuts(stderr
, _(" -C Send requests using "
4817 "chunking (default)."));
4818 _cupsLangPuts(stdout
, _(" -E Test with HTTP Upgrade to "
4820 _cupsLangPuts(stderr
, _(" -I Ignore errors."));
4821 _cupsLangPuts(stderr
, _(" -L Send requests using "
4822 "content-length."));
4823 _cupsLangPuts(stderr
, _(" -P filename.plist Produce XML plist to a file and test report to standard output."));
4824 _cupsLangPuts(stderr
, _(" -S Test with SSL "
4826 _cupsLangPuts(stderr
, _(" -T seconds Set the receive/send "
4827 "timeout in seconds."));
4828 _cupsLangPuts(stderr
, _(" -V version Set default IPP "
4830 _cupsLangPuts(stderr
, _(" -X Produce XML plist instead "
4832 _cupsLangPuts(stderr
, _(" -c Produce CSV output."));
4833 _cupsLangPuts(stderr
, _(" -d name=value Set named variable to "
4835 _cupsLangPuts(stderr
, _(" -f filename Set default request "
4837 _cupsLangPuts(stderr
, _(" -i seconds Repeat the last file with "
4838 "the given time interval."));
4839 _cupsLangPuts(stderr
, _(" -l Produce plain text output."));
4840 _cupsLangPuts(stderr
, _(" -n count Repeat the last file the "
4841 "given number of times."));
4842 _cupsLangPuts(stderr
, _(" -q Run silently."));
4843 _cupsLangPuts(stderr
, _(" -t Produce a test report."));
4844 _cupsLangPuts(stderr
, _(" -v Be verbose."));
4851 * 'validate_attr()' - Determine whether an attribute is valid.
4854 static int /* O - 1 if valid, 0 otherwise */
4855 validate_attr(FILE *outfile
, /* I - Output file */
4856 cups_array_t
*errors
, /* I - Errors array */
4857 ipp_attribute_t
*attr
) /* I - Attribute to validate */
4859 int i
; /* Looping var */
4860 char scheme
[64], /* Scheme from URI */
4861 userpass
[256], /* Username/password from URI */
4862 hostname
[256], /* Hostname from URI */
4863 resource
[1024]; /* Resource from URI */
4864 int port
, /* Port number from URI */
4865 uri_status
, /* URI separation status */
4866 valid
= 1; /* Is the attribute valid? */
4867 const char *ptr
; /* Pointer into string */
4868 ipp_attribute_t
*colattr
; /* Collection attribute */
4869 regex_t re
; /* Regular expression */
4870 ipp_uchar_t
*date
; /* Current date value */
4881 * Validate the attribute name.
4884 for (ptr
= attr
->name
; *ptr
; ptr
++)
4885 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' && *ptr
!= '_')
4888 if (*ptr
|| ptr
== attr
->name
)
4893 "\"%s\": Bad attribute name - invalid character "
4894 "(RFC 2911 section 4.1.3).", attr
->name
);
4897 if ((ptr
- attr
->name
) > 255)
4902 "\"%s\": Bad attribute name - bad length "
4903 "(RFC 2911 section 4.1.3).", attr
->name
);
4906 switch (attr
->value_tag
)
4908 case IPP_TAG_INTEGER
:
4911 case IPP_TAG_BOOLEAN
:
4912 for (i
= 0; i
< attr
->num_values
; i
++)
4914 if (attr
->values
[i
].boolean
!= 0 &&
4915 attr
->values
[i
].boolean
!= 1)
4920 "\"%s\": Bad boolen value %d "
4921 "(RFC 2911 section 4.1.11).", attr
->name
,
4922 attr
->values
[i
].boolean
);
4928 for (i
= 0; i
< attr
->num_values
; i
++)
4930 if (attr
->values
[i
].integer
< 1)
4935 "\"%s\": Bad enum value %d - out of range "
4936 "(RFC 2911 section 4.1.4).", attr
->name
,
4937 attr
->values
[i
].integer
);
4942 case IPP_TAG_STRING
:
4943 for (i
= 0; i
< attr
->num_values
; i
++)
4945 if (attr
->values
[i
].unknown
.length
> IPP_MAX_OCTETSTRING
)
4950 "\"%s\": Bad octetString value - bad length %d "
4951 "(RFC 2911 section 4.1.10).", attr
->name
,
4952 attr
->values
[i
].unknown
.length
);
4958 for (i
= 0; i
< attr
->num_values
; i
++)
4960 date
= attr
->values
[i
].date
;
4962 if (date
[2] < 1 || date
[2] > 12)
4967 "\"%s\": Bad dateTime month %u "
4968 "(RFC 2911 section 4.1.14).", attr
->name
, date
[2]);
4971 if (date
[3] < 1 || date
[3] > 31)
4976 "\"%s\": Bad dateTime day %u "
4977 "(RFC 2911 section 4.1.14).", attr
->name
, date
[3]);
4985 "\"%s\": Bad dateTime hours %u "
4986 "(RFC 2911 section 4.1.14).", attr
->name
, date
[4]);
4994 "\"%s\": Bad dateTime minutes %u "
4995 "(RFC 2911 section 4.1.14).", attr
->name
, date
[5]);
5003 "\"%s\": Bad dateTime seconds %u "
5004 "(RFC 2911 section 4.1.14).", attr
->name
, date
[6]);
5012 "\"%s\": Bad dateTime deciseconds %u "
5013 "(RFC 2911 section 4.1.14).", attr
->name
, date
[7]);
5016 if (date
[8] != '-' && date
[8] != '+')
5021 "\"%s\": Bad dateTime UTC sign '%c' "
5022 "(RFC 2911 section 4.1.14).", attr
->name
, date
[8]);
5030 "\"%s\": Bad dateTime UTC hours %u "
5031 "(RFC 2911 section 4.1.14).", attr
->name
, date
[9]);
5039 "\"%s\": Bad dateTime UTC minutes %u "
5040 "(RFC 2911 section 4.1.14).", attr
->name
, date
[10]);
5045 case IPP_TAG_RESOLUTION
:
5046 for (i
= 0; i
< attr
->num_values
; i
++)
5048 if (attr
->values
[i
].resolution
.xres
<= 0)
5053 "\"%s\": Bad resolution value %dx%d%s - cross "
5054 "feed resolution must be positive "
5055 "(RFC 2911 section 4.1.15).", attr
->name
,
5056 attr
->values
[i
].resolution
.xres
,
5057 attr
->values
[i
].resolution
.yres
,
5058 attr
->values
[i
].resolution
.units
==
5059 IPP_RES_PER_INCH
? "dpi" :
5060 attr
->values
[i
].resolution
.units
==
5061 IPP_RES_PER_CM
? "dpcm" : "unknown");
5064 if (attr
->values
[i
].resolution
.yres
<= 0)
5069 "\"%s\": Bad resolution value %dx%d%s - feed "
5070 "resolution must be positive "
5071 "(RFC 2911 section 4.1.15).", attr
->name
,
5072 attr
->values
[i
].resolution
.xres
,
5073 attr
->values
[i
].resolution
.yres
,
5074 attr
->values
[i
].resolution
.units
==
5075 IPP_RES_PER_INCH
? "dpi" :
5076 attr
->values
[i
].resolution
.units
==
5077 IPP_RES_PER_CM
? "dpcm" : "unknown");
5080 if (attr
->values
[i
].resolution
.units
!= IPP_RES_PER_INCH
&&
5081 attr
->values
[i
].resolution
.units
!= IPP_RES_PER_CM
)
5086 "\"%s\": Bad resolution value %dx%d%s - bad "
5087 "units value (RFC 2911 section 4.1.15).",
5088 attr
->name
, attr
->values
[i
].resolution
.xres
,
5089 attr
->values
[i
].resolution
.yres
,
5090 attr
->values
[i
].resolution
.units
==
5091 IPP_RES_PER_INCH
? "dpi" :
5092 attr
->values
[i
].resolution
.units
==
5093 IPP_RES_PER_CM
? "dpcm" : "unknown");
5098 case IPP_TAG_RANGE
:
5099 for (i
= 0; i
< attr
->num_values
; i
++)
5101 if (attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
5106 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5107 "greater than upper (RFC 2911 section 4.1.13).",
5108 attr
->name
, attr
->values
[i
].range
.lower
,
5109 attr
->values
[i
].range
.upper
);
5114 case IPP_TAG_BEGIN_COLLECTION
:
5115 for (i
= 0; i
< attr
->num_values
; i
++)
5117 for (colattr
= attr
->values
[i
].collection
->attrs
;
5119 colattr
= colattr
->next
)
5121 if (!validate_attr(outfile
, NULL
, colattr
))
5128 if (colattr
&& errors
)
5130 add_stringf(errors
, "\"%s\": Bad collection value.", attr
->name
);
5134 validate_attr(outfile
, errors
, colattr
);
5135 colattr
= colattr
->next
;
5142 case IPP_TAG_TEXTLANG
:
5143 for (i
= 0; i
< attr
->num_values
; i
++)
5145 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5147 if ((*ptr
& 0xe0) == 0xc0)
5150 if ((*ptr
& 0xc0) != 0x80)
5153 else if ((*ptr
& 0xf0) == 0xe0)
5156 if ((*ptr
& 0xc0) != 0x80)
5159 if ((*ptr
& 0xc0) != 0x80)
5162 else if ((*ptr
& 0xf8) == 0xf0)
5165 if ((*ptr
& 0xc0) != 0x80)
5168 if ((*ptr
& 0xc0) != 0x80)
5171 if ((*ptr
& 0xc0) != 0x80)
5174 else if (*ptr
& 0x80)
5183 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5184 "sequence (RFC 2911 section 4.1.1).", attr
->name
,
5185 attr
->values
[i
].string
.text
);
5188 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_TEXT
- 1))
5193 "\"%s\": Bad text value \"%s\" - bad length %d "
5194 "(RFC 2911 section 4.1.1).", attr
->name
,
5195 attr
->values
[i
].string
.text
,
5196 (int)strlen(attr
->values
[i
].string
.text
));
5202 case IPP_TAG_NAMELANG
:
5203 for (i
= 0; i
< attr
->num_values
; i
++)
5205 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5207 if ((*ptr
& 0xe0) == 0xc0)
5210 if ((*ptr
& 0xc0) != 0x80)
5213 else if ((*ptr
& 0xf0) == 0xe0)
5216 if ((*ptr
& 0xc0) != 0x80)
5219 if ((*ptr
& 0xc0) != 0x80)
5222 else if ((*ptr
& 0xf8) == 0xf0)
5225 if ((*ptr
& 0xc0) != 0x80)
5228 if ((*ptr
& 0xc0) != 0x80)
5231 if ((*ptr
& 0xc0) != 0x80)
5234 else if (*ptr
& 0x80)
5243 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5244 "sequence (RFC 2911 section 4.1.2).", attr
->name
,
5245 attr
->values
[i
].string
.text
);
5248 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_NAME
- 1))
5253 "\"%s\": Bad name value \"%s\" - bad length %d "
5254 "(RFC 2911 section 4.1.2).", attr
->name
,
5255 attr
->values
[i
].string
.text
,
5256 (int)strlen(attr
->values
[i
].string
.text
));
5261 case IPP_TAG_KEYWORD
:
5262 for (i
= 0; i
< attr
->num_values
; i
++)
5264 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5265 if (!isalnum(*ptr
& 255) && *ptr
!= '-' && *ptr
!= '.' &&
5269 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5274 "\"%s\": Bad keyword value \"%s\" - invalid "
5275 "character (RFC 2911 section 4.1.3).",
5276 attr
->name
, attr
->values
[i
].string
.text
);
5279 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_KEYWORD
- 1))
5284 "\"%s\": Bad keyword value \"%s\" - bad "
5285 "length %d (RFC 2911 section 4.1.3).",
5286 attr
->name
, attr
->values
[i
].string
.text
,
5287 (int)strlen(attr
->values
[i
].string
.text
));
5293 for (i
= 0; i
< attr
->num_values
; i
++)
5295 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
,
5296 attr
->values
[i
].string
.text
,
5297 scheme
, sizeof(scheme
),
5298 userpass
, sizeof(userpass
),
5299 hostname
, sizeof(hostname
),
5300 &port
, resource
, sizeof(resource
));
5302 if (uri_status
< HTTP_URI_OK
)
5307 "\"%s\": Bad URI value \"%s\" - %s "
5308 "(RFC 2911 section 4.1.5).", attr
->name
,
5309 attr
->values
[i
].string
.text
,
5310 httpURIStatusString(uri_status
));
5313 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_URI
- 1))
5318 "\"%s\": Bad URI value \"%s\" - bad length %d "
5319 "(RFC 2911 section 4.1.5).", attr
->name
,
5320 attr
->values
[i
].string
.text
,
5321 (int)strlen(attr
->values
[i
].string
.text
));
5326 case IPP_TAG_URISCHEME
:
5327 for (i
= 0; i
< attr
->num_values
; i
++)
5329 ptr
= attr
->values
[i
].string
.text
;
5330 if (islower(*ptr
& 255))
5332 for (ptr
++; *ptr
; ptr
++)
5333 if (!islower(*ptr
& 255) && !isdigit(*ptr
& 255) &&
5334 *ptr
!= '+' && *ptr
!= '-' && *ptr
!= '.')
5338 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5343 "\"%s\": Bad uriScheme value \"%s\" - bad "
5344 "characters (RFC 2911 section 4.1.6).",
5345 attr
->name
, attr
->values
[i
].string
.text
);
5348 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_URISCHEME
- 1))
5353 "\"%s\": Bad uriScheme value \"%s\" - bad "
5354 "length %d (RFC 2911 section 4.1.6).",
5355 attr
->name
, attr
->values
[i
].string
.text
,
5356 (int)strlen(attr
->values
[i
].string
.text
));
5361 case IPP_TAG_CHARSET
:
5362 for (i
= 0; i
< attr
->num_values
; i
++)
5364 for (ptr
= attr
->values
[i
].string
.text
; *ptr
; ptr
++)
5365 if (!isprint(*ptr
& 255) || isupper(*ptr
& 255) ||
5366 isspace(*ptr
& 255))
5369 if (*ptr
|| ptr
== attr
->values
[i
].string
.text
)
5374 "\"%s\": Bad charset value \"%s\" - bad "
5375 "characters (RFC 2911 section 4.1.7).",
5376 attr
->name
, attr
->values
[i
].string
.text
);
5379 if ((ptr
- attr
->values
[i
].string
.text
) > (IPP_MAX_CHARSET
- 1))
5384 "\"%s\": Bad charset value \"%s\" - bad "
5385 "length %d (RFC 2911 section 4.1.7).",
5386 attr
->name
, attr
->values
[i
].string
.text
,
5387 (int)strlen(attr
->values
[i
].string
.text
));
5392 case IPP_TAG_LANGUAGE
:
5394 * The following regular expression is derived from the ABNF for
5395 * language tags in RFC 4646. All I can say is that this is the
5396 * easiest way to check the values...
5399 if ((i
= regcomp(&re
,
5401 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5403 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5404 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5405 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5406 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5407 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5409 "x(-[a-z0-9]{1,8})+" /* privateuse */
5411 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5413 REG_NOSUB
| REG_EXTENDED
)) != 0)
5415 char temp
[256]; /* Temporary error string */
5417 regerror(i
, &re
, temp
, sizeof(temp
));
5418 print_fatal_error(outfile
, "Unable to compile naturalLanguage regular "
5419 "expression: %s.", temp
);
5423 for (i
= 0; i
< attr
->num_values
; i
++)
5425 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5430 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5431 "characters (RFC 2911 section 4.1.8).",
5432 attr
->name
, attr
->values
[i
].string
.text
);
5435 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_LANGUAGE
- 1))
5440 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5441 "length %d (RFC 2911 section 4.1.8).",
5442 attr
->name
, attr
->values
[i
].string
.text
,
5443 (int)strlen(attr
->values
[i
].string
.text
));
5450 case IPP_TAG_MIMETYPE
:
5452 * The following regular expression is derived from the ABNF for
5453 * language tags in RFC 2045 and 4288. All I can say is that this is
5454 * the easiest way to check the values...
5457 if ((i
= regcomp(&re
,
5459 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5461 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5462 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5463 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5466 REG_NOSUB
| REG_EXTENDED
)) != 0)
5468 char temp
[256]; /* Temporary error string */
5470 regerror(i
, &re
, temp
, sizeof(temp
));
5471 print_fatal_error(outfile
, "Unable to compile mimeMediaType regular "
5472 "expression: %s.", temp
);
5476 for (i
= 0; i
< attr
->num_values
; i
++)
5478 if (regexec(&re
, attr
->values
[i
].string
.text
, 0, NULL
, 0))
5483 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5484 "characters (RFC 2911 section 4.1.9).",
5485 attr
->name
, attr
->values
[i
].string
.text
);
5488 if (strlen(attr
->values
[i
].string
.text
) > (IPP_MAX_MIMETYPE
- 1))
5493 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5494 "length %d (RFC 2911 section 4.1.9).",
5495 attr
->name
, attr
->values
[i
].string
.text
,
5496 (int)strlen(attr
->values
[i
].string
.text
));
5512 * 'with_value()' - Test a WITH-VALUE predicate.
5515 static int /* O - 1 on match, 0 on non-match */
5516 with_value(FILE *outfile
, /* I - Output file */
5517 cups_array_t
*errors
, /* I - Errors array */
5518 char *value
, /* I - Value string */
5519 int flags
, /* I - Flags for match */
5520 ipp_attribute_t
*attr
, /* I - Attribute to compare */
5521 char *matchbuf
, /* I - Buffer to hold matching value */
5522 size_t matchlen
) /* I - Length of match buffer */
5524 int i
, /* Looping var */
5526 char temp
[1024], /* Temporary value string */
5527 *valptr
; /* Pointer into value */
5531 match
= (flags
& _CUPS_WITH_ALL
) ? 1 : 0;
5534 * NULL matches everything.
5537 if (!value
|| !*value
)
5541 * Compare the value string to the attribute value.
5544 switch (attr
->value_tag
)
5546 case IPP_TAG_INTEGER
:
5548 for (i
= 0; i
< attr
->num_values
; i
++)
5550 char op
, /* Comparison operator */
5551 *nextptr
; /* Next pointer */
5552 int intvalue
, /* Integer value */
5553 valmatch
= 0; /* Does the current value match? */
5557 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5558 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5559 *valptr
== '=' || *valptr
== '>')
5562 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5564 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5572 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5573 if (nextptr
== valptr
)
5577 if ((op
== '=' && attr
->values
[i
].integer
== intvalue
) ||
5578 (op
== '<' && attr
->values
[i
].integer
< intvalue
) ||
5579 (op
== '>' && attr
->values
[i
].integer
> intvalue
))
5582 snprintf(matchbuf
, matchlen
, "%d", attr
->values
[i
].integer
);
5589 if (flags
& _CUPS_WITH_ALL
)
5604 if (!match
&& errors
)
5606 for (i
= 0; i
< attr
->num_values
; i
++)
5607 add_stringf(errors
, "GOT: %s=%d", attr
->name
,
5608 attr
->values
[i
].integer
);
5612 case IPP_TAG_RANGE
:
5613 for (i
= 0; i
< attr
->num_values
; i
++)
5615 char op
, /* Comparison operator */
5616 *nextptr
; /* Next pointer */
5617 int intvalue
, /* Integer value */
5618 valmatch
= 0; /* Does the current value match? */
5622 while (isspace(*valptr
& 255) || isdigit(*valptr
& 255) ||
5623 *valptr
== '-' || *valptr
== ',' || *valptr
== '<' ||
5624 *valptr
== '=' || *valptr
== '>')
5627 while (*valptr
&& !isdigit(*valptr
& 255) && *valptr
!= '-')
5629 if (*valptr
== '<' || *valptr
== '>' || *valptr
== '=')
5637 intvalue
= (int)strtol(valptr
, &nextptr
, 0);
5638 if (nextptr
== valptr
)
5642 if ((op
== '=' && (attr
->values
[i
].range
.lower
== intvalue
||
5643 attr
->values
[i
].range
.upper
== intvalue
)) ||
5644 (op
== '<' && attr
->values
[i
].range
.upper
< intvalue
) ||
5645 (op
== '>' && attr
->values
[i
].range
.upper
> intvalue
))
5648 snprintf(matchbuf
, matchlen
, "%d-%d",
5649 attr
->values
[0].range
.lower
,
5650 attr
->values
[0].range
.upper
);
5657 if (flags
& _CUPS_WITH_ALL
)
5672 if (!match
&& errors
)
5674 for (i
= 0; i
< attr
->num_values
; i
++)
5675 add_stringf(errors
, "GOT: %s=%d-%d", attr
->name
,
5676 attr
->values
[i
].range
.lower
,
5677 attr
->values
[i
].range
.upper
);
5681 case IPP_TAG_BOOLEAN
:
5682 for (i
= 0; i
< attr
->num_values
; i
++)
5684 if (!strcmp(value
, "true") == attr
->values
[i
].boolean
)
5687 strlcpy(matchbuf
, value
, matchlen
);
5689 if (!(flags
& _CUPS_WITH_ALL
))
5695 else if (flags
& _CUPS_WITH_ALL
)
5702 if (!match
&& errors
)
5704 for (i
= 0; i
< attr
->num_values
; i
++)
5705 add_stringf(errors
, "GOT: %s=%s", attr
->name
,
5706 attr
->values
[i
].boolean
? "true" : "false");
5710 case IPP_TAG_RESOLUTION
:
5711 for (i
= 0; i
< attr
->num_values
; i
++)
5713 if (attr
->values
[i
].resolution
.xres
==
5714 attr
->values
[i
].resolution
.yres
)
5715 snprintf(temp
, sizeof(temp
), "%d%s",
5716 attr
->values
[i
].resolution
.xres
,
5717 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5720 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5721 attr
->values
[i
].resolution
.xres
,
5722 attr
->values
[i
].resolution
.yres
,
5723 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5726 if (!strcmp(value
, temp
))
5729 strlcpy(matchbuf
, value
, matchlen
);
5731 if (!(flags
& _CUPS_WITH_ALL
))
5737 else if (flags
& _CUPS_WITH_ALL
)
5744 if (!match
&& errors
)
5746 for (i
= 0; i
< attr
->num_values
; i
++)
5748 if (attr
->values
[i
].resolution
.xres
==
5749 attr
->values
[i
].resolution
.yres
)
5750 snprintf(temp
, sizeof(temp
), "%d%s",
5751 attr
->values
[i
].resolution
.xres
,
5752 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5755 snprintf(temp
, sizeof(temp
), "%dx%d%s",
5756 attr
->values
[i
].resolution
.xres
,
5757 attr
->values
[i
].resolution
.yres
,
5758 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
5761 if (strcmp(value
, temp
))
5762 add_stringf(errors
, "GOT: %s=%s", attr
->name
, temp
);
5767 case IPP_TAG_NOVALUE
:
5768 case IPP_TAG_UNKNOWN
:
5771 case IPP_TAG_CHARSET
:
5772 case IPP_TAG_KEYWORD
:
5773 case IPP_TAG_LANGUAGE
:
5774 case IPP_TAG_MIMETYPE
:
5776 case IPP_TAG_NAMELANG
:
5778 case IPP_TAG_TEXTLANG
:
5780 case IPP_TAG_URISCHEME
:
5781 if (flags
& _CUPS_WITH_REGEX
)
5784 * Value is an extended, case-sensitive POSIX regular expression...
5787 regex_t re
; /* Regular expression */
5789 if ((i
= regcomp(&re
, value
, REG_EXTENDED
| REG_NOSUB
)) != 0)
5791 regerror(i
, &re
, temp
, sizeof(temp
));
5793 print_fatal_error(outfile
, "Unable to compile WITH-VALUE regular expression "
5794 "\"%s\" - %s", value
, temp
);
5799 * See if ALL of the values match the given regular expression.
5802 for (i
= 0; i
< attr
->num_values
; i
++)
5804 if (!regexec(&re
, get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5809 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5812 if (!(flags
& _CUPS_WITH_ALL
))
5818 else if (flags
& _CUPS_WITH_ALL
)
5830 * Value is a literal string, see if the value(s) match...
5833 for (i
= 0; i
< attr
->num_values
; i
++)
5835 if (!strcmp(value
, get_string(attr
, i
, flags
, temp
, sizeof(temp
))))
5839 get_string(attr
, i
, flags
, temp
, sizeof(temp
)),
5842 if (!(flags
& _CUPS_WITH_ALL
))
5848 else if (flags
& _CUPS_WITH_ALL
)
5856 if (!match
&& errors
)
5858 for (i
= 0; i
< attr
->num_values
; i
++)
5859 add_stringf(errors
, "GOT: %s=\"%s\"", attr
->name
,
5860 attr
->values
[i
].string
.text
);