]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ipptool.c
Import CUPS v2.0b1
[thirdparty/cups.git] / test / ipptool.c
CommitLineData
ef416fc2 1/*
1a18c85c 2 * "$Id: ipptool.c 12031 2014-07-15 19:57:59Z msweet $"
ef416fc2 3 *
e5d9b5e1 4 * ipptool command for CUPS.
ef416fc2 5 *
71f63681 6 * Copyright 2007-2014 by Apple Inc.
e5d9b5e1 7 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 8 *
e5d9b5e1
MS
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/".
ef416fc2 14 *
e5d9b5e1 15 * This file is subject to the Apple OS-Developed Software exception.
ef416fc2 16 */
17
18/*
19 * Include necessary headers...
20 */
21
71e16022 22#include <cups/cups-private.h>
5180a04c 23#include <cups/file-private.h>
f8b3a85b 24#include <regex.h>
85dda01c 25#include <sys/stat.h>
db8b865d 26#ifdef WIN32
1a18c85c
MS
27# include <windows.h>
28# ifndef R_OK
29# define R_OK 0
30# endif /* !R_OK */
db8b865d 31#else
83e08001 32# include <signal.h>
1a18c85c 33# include <termios.h>
83e08001 34#endif /* WIN32 */
5a662dc0
MS
35#ifndef O_BINARY
36# define O_BINARY 0
37#endif /* !O_BINARY */
38
39
40/*
41 * Types...
42 */
43
aaf19ab0 44typedef enum _cups_transfer_e /**** How to send request data ****/
f8b3a85b
MS
45{
46 _CUPS_TRANSFER_AUTO, /* Chunk for files, length for static */
47 _CUPS_TRANSFER_CHUNKED, /* Chunk always */
48 _CUPS_TRANSFER_LENGTH /* Length always */
49} _cups_transfer_t;
50
aaf19ab0
MS
51typedef enum _cups_output_e /**** Output mode ****/
52{
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 */
58} _cups_output_t;
59
82cc1f9a
MS
60typedef enum _cups_with_e /**** WITH flags ****/
61{
62 _CUPS_WITH_LITERAL = 0, /* Match string is a literal value */
63 _CUPS_WITH_ALL = 1, /* Must match all values */
890a10b7
MS
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 */
82cc1f9a
MS
68} _cups_with_t;
69
5a662dc0
MS
70typedef struct _cups_expect_s /**** Expected attribute info ****/
71{
f8b3a85b
MS
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 */
7cf5915e 78 *if_not_defined, /* Only required if variable is not defined */
030ae6a1
MS
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 */
a29fd7dd
MS
83 int repeat_limit, /* Maximum number of times to repeat */
84 repeat_match, /* Repeat test on match */
83e08001 85 repeat_no_match, /* Repeat test on no match */
82cc1f9a 86 with_flags, /* WITH flags */
f8b3a85b
MS
87 count; /* Expected count if > 0 */
88 ipp_tag_t in_group; /* IN-GROUP value */
5a662dc0 89} _cups_expect_t;
ef416fc2 90
f8b3a85b
MS
91typedef struct _cups_status_s /**** Status info ****/
92{
93 ipp_status_t status; /* Expected status code */
94 char *if_defined, /* Only if variable is defined */
0fa6c7fa
MS
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 */
a29fd7dd
MS
99 int repeat_limit, /* Maximum number of times to repeat */
100 repeat_match, /* Repeat the test when it does not match */
83e08001 101 repeat_no_match; /* Repeat the test when it matches */
f8b3a85b
MS
102} _cups_status_t;
103
104typedef struct _cups_var_s /**** Variable ****/
105{
106 char *name, /* Name of variable */
107 *value; /* Value of variable */
108} _cups_var_t;
109
110typedef struct _cups_vars_s /**** Set of variables ****/
111{
83e08001
MS
112 char *uri, /* URI for printer */
113 *filename, /* Filename */
114 scheme[64], /* Scheme from URI */
f8b3a85b
MS
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? */
10d09e33 120 double timeout; /* Timeout for connection */
1106b00e 121 int family; /* Address family */
f8b3a85b
MS
122 cups_array_t *vars; /* Array of variables */
123} _cups_vars_t;
124
ef416fc2 125
e00b005a 126/*
127 * Globals...
128 */
129
1a18c85c 130static _cups_transfer_t Transfer = _CUPS_TRANSFER_AUTO;
f8b3a85b 131 /* How to transfer requests */
1a18c85c 132static _cups_output_t Output = _CUPS_OUTPUT_LIST;
aaf19ab0 133 /* Output mode */
1a18c85c 134static int Cancel = 0, /* Cancel test? */
85dda01c 135 IgnoreErrors = 0, /* Ignore errors? */
9475ec92
MS
136 StopAfterIncludeError = 0,
137 /* Stop after include errors? */
030ae6a1 138 Verbosity = 0, /* Show all attributes? */
f8b3a85b 139 Version = 11, /* Default IPP version */
9b66acc5
MS
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 */
71f63681
MS
145static char *Username = NULL, /* Username from URI */
146 *Password = NULL; /* Password from URI */
147static int PasswordTries = 0; /* Number of tries with password */
e00b005a 148
149
ef416fc2 150/*
151 * Local functions...
152 */
153
dcb445bc
MS
154static void add_stringf(cups_array_t *a, const char *s, ...)
155 __attribute__ ((__format__ (__printf__, 2, 3)));
f8b3a85b 156static int compare_vars(_cups_var_t *a, _cups_var_t *b);
1a18c85c 157static int do_tests(FILE *outfile, _cups_vars_t *vars, const char *testfile);
f8b3a85b 158static void expand_variables(_cups_vars_t *vars, char *dst, const char *src,
83e08001 159 size_t dstsize) __attribute__((nonnull(1,2,3)));
5a662dc0 160static int expect_matches(_cups_expect_t *expect, ipp_tag_t value_tag);
1a18c85c 161static ipp_t *get_collection(FILE *outfile, _cups_vars_t *vars, FILE *fp, int *linenum);
f8b3a85b
MS
162static char *get_filename(const char *testfile, char *dst, const char *src,
163 size_t dstsize);
890a10b7
MS
164static char *get_string(ipp_attribute_t *attr, int element, int flags,
165 char *buffer, size_t bufsize);
5a662dc0
MS
166static char *get_token(FILE *fp, char *buf, int buflen,
167 int *linenum);
f8b3a85b
MS
168static char *get_variable(_cups_vars_t *vars, const char *name);
169static char *iso_date(ipp_uchar_t *date);
7cf5915e 170static const char *password_cb(const char *prompt);
1a18c85c
MS
171static void pause_message(const char *message);
172static void print_attr(FILE *outfile, int format, ipp_attribute_t *attr, ipp_tag_t *group);
173static void print_csv(FILE *outfile, ipp_attribute_t *attr, int num_displayed,
aaf19ab0 174 char **displayed, size_t *widths);
1a18c85c
MS
175static void print_fatal_error(FILE *outfile, const char *s, ...)
176 __attribute__ ((__format__ (__printf__, 2, 3)));
177static void print_line(FILE *outfile, ipp_attribute_t *attr, int num_displayed,
aaf19ab0 178 char **displayed, size_t *widths);
1a18c85c
MS
179static void print_xml_header(FILE *outfile);
180static void print_xml_string(FILE *outfile, const char *element, const char *s);
181static void print_xml_trailer(FILE *outfile, int success, const char *message);
182static void set_variable(FILE *outfile, _cups_vars_t *vars, const char *name, const char *value);
83e08001 183#ifndef WIN32
85dda01c 184static void sigterm_handler(int sig);
83e08001 185#endif /* WIN32 */
10d09e33 186static int timeout_cb(http_t *http, void *user_data);
85dda01c 187static void usage(void) __attribute__((noreturn));
1a18c85c
MS
188static int validate_attr(FILE *outfile, cups_array_t *errors, ipp_attribute_t *attr);
189static int with_value(FILE *outfile, cups_array_t *errors, char *value, int flags,
dcb445bc
MS
190 ipp_attribute_t *attr, char *matchbuf,
191 size_t matchlen);
ef416fc2 192
193
194/*
195 * 'main()' - Parse options and do tests.
196 */
197
198int /* O - Exit status */
5a662dc0 199main(int argc, /* I - Number of command-line args */
ef416fc2 200 char *argv[]) /* I - Command-line arguments */
201{
f8b3a85b
MS
202 int i; /* Looping var */
203 int status; /* Status of tests... */
1a18c85c
MS
204 FILE *outfile = stdout;
205 /* Output file */
f8b3a85b
MS
206 char *opt, /* Current option */
207 name[1024], /* Name/value buffer */
aaf19ab0
MS
208 *value, /* Pointer to value */
209 filename[1024], /* Real filename */
83e08001
MS
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 */
10d09e33 214 int interval, /* Test interval in microseconds */
aaf19ab0 215 repeat; /* Repeat count */
f8b3a85b
MS
216 _cups_vars_t vars; /* Variables */
217 http_uri_status_t uri_status; /* URI separation status */
aaf19ab0
MS
218 _cups_globals_t *cg = _cupsGlobals();
219 /* Global data */
220
f8b3a85b 221
83e08001 222#ifndef WIN32
85dda01c
MS
223 /*
224 * Catch SIGINT and SIGTERM...
225 */
226
227 signal(SIGINT, sigterm_handler);
228 signal(SIGTERM, sigterm_handler);
83e08001 229#endif /* !WIN32 */
f8b3a85b
MS
230
231 /*
232 * Initialize the locale and variables...
233 */
234
235 _cupsSetLocale(argv);
ef416fc2 236
f8b3a85b 237 memset(&vars, 0, sizeof(vars));
1106b00e
MS
238 vars.family = AF_UNSPEC;
239 vars.vars = cupsArrayNew((cups_array_func_t)compare_vars, NULL);
ef416fc2 240
241 /*
242 * We need at least:
243 *
aaf19ab0 244 * ipptool URI testfile
ef416fc2 245 */
246
8ca02f3c 247 interval = 0;
aaf19ab0
MS
248 repeat = 0;
249 status = 0;
250 testfile = NULL;
e00b005a 251
252 for (i = 1; i < argc; i ++)
ef416fc2 253 {
9475ec92
MS
254 if (!strcmp(argv[i], "--help"))
255 {
256 usage();
257 }
258 else if (!strcmp(argv[i], "--stop-after-include-error"))
259 {
260 StopAfterIncludeError = 1;
261 }
262 else if (!strcmp(argv[i], "--version"))
263 {
264 puts(CUPS_SVERSION);
265 return (0);
266 }
267 else if (argv[i][0] == '-')
e00b005a 268 {
5a662dc0 269 for (opt = argv[i] + 1; *opt; opt ++)
b94498cf 270 {
5a662dc0
MS
271 switch (*opt)
272 {
1106b00e
MS
273 case '4' : /* Connect using IPv4 only */
274 vars.family = AF_INET;
275 break;
276
277#ifdef AF_INET6
278 case '6' : /* Connect using IPv6 only */
279 vars.family = AF_INET6;
280 break;
281#endif /* AF_INET6 */
282
aaf19ab0
MS
283 case 'C' : /* Enable HTTP chunking */
284 Transfer = _CUPS_TRANSFER_CHUNKED;
285 break;
286
287 case 'E' : /* Encrypt with TLS */
f8b3a85b
MS
288#ifdef HAVE_SSL
289 vars.encryption = HTTP_ENCRYPT_REQUIRED;
290#else
0837b7e8 291 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
f8b3a85b
MS
292 argv[0]);
293#endif /* HAVE_SSL */
294 break;
295
030ae6a1
MS
296 case 'I' : /* Ignore errors */
297 IgnoreErrors = 1;
298 break;
299
aaf19ab0
MS
300 case 'L' : /* Disable HTTP chunking */
301 Transfer = _CUPS_TRANSFER_LENGTH;
302 break;
303
1a18c85c
MS
304 case 'P' : /* Output to plist file */
305 i ++;
306
307 if (i >= argc)
308 {
309 _cupsLangPrintf(stderr, _("%s: Missing filename for \"-P\"."), "ipptool");
310 usage();
311 }
312
313 if (outfile != stdout)
314 usage();
315
316 if ((outfile = fopen(argv[i], "w")) == NULL)
317 {
318 _cupsLangPrintf(stderr, _("%s: Unable to open \"%s\": %s"), "ipptool", argv[i], strerror(errno));
319 exit(1);
320 }
321
322 Output = _CUPS_OUTPUT_PLIST;
323
324 if (interval || repeat)
325 {
326 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
327 usage();
328 }
329 break;
330
aaf19ab0
MS
331 case 'S' : /* Encrypt with SSL */
332#ifdef HAVE_SSL
333 vars.encryption = HTTP_ENCRYPT_ALWAYS;
334#else
0837b7e8 335 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
aaf19ab0
MS
336 argv[0]);
337#endif /* HAVE_SSL */
338 break;
339
10d09e33
MS
340 case 'T' : /* Set timeout */
341 i ++;
342
343 if (i >= argc)
344 {
70752071
MS
345 _cupsLangPrintf(stderr,
346 _("%s: Missing timeout for \"-T\"."),
347 "ipptool");
10d09e33
MS
348 usage();
349 }
350
351 vars.timeout = _cupsStrScand(argv[i], NULL, localeconv());
352 break;
353
f8b3a85b
MS
354 case 'V' : /* Set IPP version */
355 i ++;
356
357 if (i >= argc)
358 {
70752071
MS
359 _cupsLangPrintf(stderr,
360 _("%s: Missing version for \"-V\"."),
361 "ipptool");
f8b3a85b
MS
362 usage();
363 }
364
365 if (!strcmp(argv[i], "1.0"))
366 Version = 10;
367 else if (!strcmp(argv[i], "1.1"))
368 Version = 11;
369 else if (!strcmp(argv[i], "2.0"))
370 Version = 20;
371 else if (!strcmp(argv[i], "2.1"))
372 Version = 21;
373 else if (!strcmp(argv[i], "2.2"))
374 Version = 22;
375 else
376 {
377 _cupsLangPrintf(stderr,
70752071
MS
378 _("%s: Bad version %s for \"-V\"."),
379 "ipptool", argv[i]);
f8b3a85b
MS
380 usage();
381 }
382 break;
383
384 case 'X' : /* Produce XML output */
aaf19ab0 385 Output = _CUPS_OUTPUT_PLIST;
f8b3a85b 386
aaf19ab0 387 if (interval || repeat)
f8b3a85b 388 {
1a18c85c 389 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
f8b3a85b
MS
390 usage();
391 }
392 break;
393
aaf19ab0
MS
394 case 'c' : /* CSV output */
395 Output = _CUPS_OUTPUT_CSV;
5a662dc0 396 break;
b94498cf 397
5a662dc0
MS
398 case 'd' : /* Define a variable */
399 i ++;
8ca02f3c 400
5a662dc0
MS
401 if (i >= argc)
402 {
f8b3a85b 403 _cupsLangPuts(stderr,
0837b7e8 404 _("ipptool: Missing name=value for \"-d\"."));
5a662dc0
MS
405 usage();
406 }
f8b3a85b
MS
407
408 strlcpy(name, argv[i], sizeof(name));
409 if ((value = strchr(name, '=')) != NULL)
410 *value++ = '\0';
5a662dc0 411 else
f8b3a85b
MS
412 value = name + strlen(name);
413
1a18c85c 414 set_variable(outfile, &vars, name, value);
f8b3a85b
MS
415 break;
416
417 case 'f' : /* Set the default test filename */
418 i ++;
419
420 if (i >= argc)
421 {
422 _cupsLangPuts(stderr,
0837b7e8 423 _("ipptool: Missing filename for \"-f\"."));
f8b3a85b
MS
424 usage();
425 }
426
83e08001 427 if (vars.filename)
db8b865d 428 {
83e08001 429 free(vars.filename);
db8b865d
MS
430 vars.filename = NULL;
431 }
83e08001 432
9b66acc5 433 if (access(argv[i], 0))
aaf19ab0 434 {
9b66acc5
MS
435 /*
436 * Try filename.gz...
437 */
438
439 snprintf(filename, sizeof(filename), "%s.gz", argv[i]);
12f89d24
MS
440 if (access(filename, 0) && filename[0] != '/'
441#ifdef WIN32
442 && (!isalpha(filename[0] & 255) || filename[1] != ':')
443#endif /* WIN32 */
444 )
9b66acc5
MS
445 {
446 snprintf(filename, sizeof(filename), "%s/ipptool/%s",
447 cg->cups_datadir, argv[i]);
448 if (access(filename, 0))
449 {
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]);
db8b865d
MS
454 else
455 vars.filename = strdup(filename);
9b66acc5
MS
456 }
457 else
458 vars.filename = strdup(filename);
459 }
460 else
461 vars.filename = strdup(filename);
462 }
83e08001
MS
463 else
464 vars.filename = strdup(argv[i]);
465
466 if ((ext = strrchr(vars.filename, '.')) != NULL)
467 {
468 /*
469 * Guess the MIME media type based on the extension...
470 */
471
472 if (!_cups_strcasecmp(ext, ".gif"))
1a18c85c 473 set_variable(outfile, &vars, "filetype", "image/gif");
83e08001 474 else if (!_cups_strcasecmp(ext, ".htm") ||
9b66acc5
MS
475 !_cups_strcasecmp(ext, ".htm.gz") ||
476 !_cups_strcasecmp(ext, ".html") ||
477 !_cups_strcasecmp(ext, ".html.gz"))
1a18c85c
MS
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");
a29fd7dd
MS
482 else if (!_cups_strcasecmp(ext, ".pcl") ||
483 !_cups_strcasecmp(ext, ".pcl.gz"))
1a18c85c 484 set_variable(outfile, &vars, "filetype", "application/vnd.hp-PCL");
83e08001 485 else if (!_cups_strcasecmp(ext, ".pdf"))
1a18c85c 486 set_variable(outfile, &vars, "filetype", "application/pdf");
83e08001 487 else if (!_cups_strcasecmp(ext, ".png"))
1a18c85c 488 set_variable(outfile, &vars, "filetype", "image/png");
9b66acc5
MS
489 else if (!_cups_strcasecmp(ext, ".ps") ||
490 !_cups_strcasecmp(ext, ".ps.gz"))
1a18c85c 491 set_variable(outfile, &vars, "filetype", "application/postscript");
db8b865d
MS
492 else if (!_cups_strcasecmp(ext, ".pwg") ||
493 !_cups_strcasecmp(ext, ".pwg.gz") ||
494 !_cups_strcasecmp(ext, ".ras") ||
9b66acc5 495 !_cups_strcasecmp(ext, ".ras.gz"))
1a18c85c
MS
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");
9b66acc5
MS
500 else if (!_cups_strcasecmp(ext, ".txt") ||
501 !_cups_strcasecmp(ext, ".txt.gz"))
1a18c85c
MS
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");
83e08001 506 else if (!_cups_strcasecmp(ext, ".xps"))
1a18c85c 507 set_variable(outfile, &vars, "filetype", "application/openxps");
aaf19ab0 508 else
1a18c85c 509 set_variable(outfile, &vars, "filetype", "application/octet-stream");
aaf19ab0
MS
510 }
511 else
83e08001
MS
512 {
513 /*
514 * Use the "auto-type" MIME media type...
515 */
516
1a18c85c 517 set_variable(outfile, &vars, "filetype", "application/octet-stream");
83e08001 518 }
5a662dc0
MS
519 break;
520
521 case 'i' : /* Test every N seconds */
aaf19ab0 522 i ++;
5a662dc0
MS
523
524 if (i >= argc)
525 {
f8b3a85b 526 _cupsLangPuts(stderr,
0837b7e8 527 _("ipptool: Missing seconds for \"-i\"."));
5a662dc0
MS
528 usage();
529 }
530 else
10d09e33
MS
531 {
532 interval = (int)(_cupsStrScand(argv[i], NULL, localeconv()) *
533 1000000.0);
534 if (interval <= 0)
535 {
536 _cupsLangPuts(stderr,
0837b7e8 537 _("ipptool: Invalid seconds for \"-i\"."));
10d09e33
MS
538 usage();
539 }
540 }
f8b3a85b 541
aaf19ab0 542 if (Output == _CUPS_OUTPUT_PLIST && interval)
f8b3a85b 543 {
1a18c85c 544 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
f8b3a85b
MS
545 usage();
546 }
5a662dc0
MS
547 break;
548
aaf19ab0
MS
549 case 'l' : /* List as a table */
550 Output = _CUPS_OUTPUT_LIST;
551 break;
552
553 case 'n' : /* Repeat count */
554 i ++;
555
556 if (i >= argc)
557 {
558 _cupsLangPuts(stderr,
0837b7e8 559 _("ipptool: Missing count for \"-n\"."));
aaf19ab0
MS
560 usage();
561 }
562 else
563 repeat = atoi(argv[i]);
564
565 if (Output == _CUPS_OUTPUT_PLIST && repeat)
566 {
1a18c85c 567 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are incompatible with \"-P\" and \"-X\"."));
aaf19ab0
MS
568 usage();
569 }
570 break;
571
572 case 'q' : /* Be quiet */
573 Output = _CUPS_OUTPUT_QUIET;
574 break;
575
576 case 't' : /* CUPS test output */
577 Output = _CUPS_OUTPUT_TEST;
f8b3a85b
MS
578 break;
579
5a662dc0
MS
580 case 'v' : /* Be verbose */
581 Verbosity ++;
582 break;
583
584 default :
0837b7e8 585 _cupsLangPrintf(stderr, _("ipptool: Unknown option \"-%c\"."),
f8b3a85b 586 *opt);
5a662dc0 587 usage();
5a662dc0 588 }
8ca02f3c 589 }
e00b005a 590 }
7cf5915e
MS
591 else if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "http://", 7)
592#ifdef HAVE_SSL
593 || !strncmp(argv[i], "ipps://", 7)
594 || !strncmp(argv[i], "https://", 8)
595#endif /* HAVE_SSL */
596 )
e00b005a 597 {
598 /*
599 * Set URI...
600 */
ef416fc2 601
f8b3a85b 602 if (vars.uri)
5a662dc0 603 {
0837b7e8 604 _cupsLangPuts(stderr, _("ipptool: May only specify a single URI."));
5a662dc0
MS
605 usage();
606 }
e00b005a 607
7cf5915e
MS
608#ifdef HAVE_SSL
609 if (!strncmp(argv[i], "ipps://", 7) || !strncmp(argv[i], "https://", 8))
610 vars.encryption = HTTP_ENCRYPT_ALWAYS;
611#endif /* HAVE_SSL */
612
f8b3a85b
MS
613 vars.uri = argv[i];
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),
618 &(vars.port),
619 vars.resource, sizeof(vars.resource));
620
621 if (uri_status != HTTP_URI_OK)
622 {
1a18c85c 623 _cupsLangPrintf(stderr, _("ipptool: Bad URI - %s."), httpURIStatusString(uri_status));
f8b3a85b
MS
624 return (1);
625 }
626
7cf5915e 627 if (vars.userpass[0])
f8b3a85b 628 {
7cf5915e
MS
629 if ((Password = strchr(vars.userpass, ':')) != NULL)
630 *Password++ = '\0';
631
71f63681 632 Username = vars.userpass;
7cf5915e 633 cupsSetPasswordCB(password_cb);
1a18c85c 634 set_variable(outfile, &vars, "uriuser", vars.userpass);
f8b3a85b 635 }
83e08001
MS
636
637 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), vars.scheme, NULL,
638 vars.hostname, vars.port, vars.resource);
639 vars.uri = uri;
e00b005a 640 }
641 else
642 {
643 /*
644 * Run test...
645 */
646
f8b3a85b
MS
647 if (!vars.uri)
648 {
aaf19ab0 649 _cupsLangPuts(stderr, _("ipptool: URI required before test file."));
f8b3a85b
MS
650 usage();
651 }
652
12f89d24
MS
653 if (access(argv[i], 0) && argv[i][0] != '/'
654#ifdef WIN32
655 && (!isalpha(argv[i][0] & 255) || argv[i][1] != ':')
656#endif /* WIN32 */
657 )
aaf19ab0
MS
658 {
659 snprintf(testname, sizeof(testname), "%s/ipptool/%s", cg->cups_datadir,
660 argv[i]);
661 if (access(testname, 0))
662 testfile = argv[i];
663 else
664 testfile = testname;
665 }
666 else
667 testfile = argv[i];
e00b005a 668
1a18c85c 669 if (!do_tests(outfile, &vars, testfile))
f8b3a85b 670 status = 1;
e00b005a 671 }
672 }
ef416fc2 673
f8b3a85b 674 if (!vars.uri || !testfile)
5a662dc0 675 usage();
ef416fc2 676
8ca02f3c 677 /*
678 * Loop if the interval is set...
679 */
680
aaf19ab0 681 if (Output == _CUPS_OUTPUT_PLIST)
1a18c85c 682 print_xml_trailer(outfile, !status, NULL);
10d09e33 683 else if (interval > 0 && repeat > 0)
aaf19ab0
MS
684 {
685 while (repeat > 1)
686 {
1a18c85c
MS
687 usleep((useconds_t)interval);
688 do_tests(outfile, &vars, testfile);
aaf19ab0
MS
689 repeat --;
690 }
691 }
10d09e33 692 else if (interval > 0)
8ca02f3c 693 {
694 for (;;)
695 {
1a18c85c
MS
696 usleep((useconds_t)interval);
697 do_tests(outfile, &vars, testfile);
8ca02f3c 698 }
699 }
1a18c85c
MS
700
701 if ((Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile)) && TestCount > 1)
9b66acc5
MS
702 {
703 /*
704 * Show a summary report if there were multiple tests...
705 */
706
707 printf("\nSummary: %d tests, %d passed, %d failed, %d skipped\n"
708 "Score: %d%%\n", TestCount, PassCount, FailCount, SkipCount,
709 100 * (PassCount + SkipCount) / TestCount);
710 }
8ca02f3c 711
ef416fc2 712 /*
713 * Exit...
714 */
715
e00b005a 716 return (status);
ef416fc2 717}
718
f8b3a85b 719
dcb445bc
MS
720/*
721 * 'add_stringf()' - Add a formatted string to an array.
722 */
723
724static void
725add_stringf(cups_array_t *a, /* I - Array */
726 const char *s, /* I - Printf-style format string */
727 ...) /* I - Additional args as needed */
728{
729 char buffer[10240]; /* Format buffer */
730 va_list ap; /* Argument pointer */
731
732
733 /*
734 * Don't bother is the array is NULL...
735 */
736
737 if (!a)
738 return;
739
740 /*
741 * Format the message...
742 */
743
744 va_start(ap, s);
745 vsnprintf(buffer, sizeof(buffer), s, ap);
746 va_end(ap);
747
748 /*
749 * Add it to the array...
750 */
751
752 cupsArrayAdd(a, buffer);
753}
754
755
f8b3a85b
MS
756/*
757 * 'compare_vars()' - Compare two variables.
758 */
759
760static int /* O - Result of comparison */
761compare_vars(_cups_var_t *a, /* I - First variable */
762 _cups_var_t *b) /* I - Second variable */
763{
88f9aafc 764 return (_cups_strcasecmp(a->name, b->name));
f8b3a85b
MS
765}
766
767
ef416fc2 768/*
769 * 'do_tests()' - Do tests as specified in the test file.
770 */
771
5a662dc0 772static int /* 1 = success, 0 = failure */
1a18c85c
MS
773do_tests(FILE *outfile, /* I - Output file */
774 _cups_vars_t *vars, /* I - Variables */
f8b3a85b 775 const char *testfile) /* I - Test file to use */
ef416fc2 776{
f8b3a85b
MS
777 int i, /* Looping var */
778 linenum, /* Current line number */
779 pass, /* Did we pass the test? */
030ae6a1 780 prev_pass = 1, /* Did we pass the previous test? */
cc754834 781 request_id, /* Current request ID */
030ae6a1
MS
782 show_header = 1, /* Show the test header? */
783 ignore_errors, /* Ignore test failures? */
83e08001 784 skip_previous = 0, /* Skip on previous test failure? */
a29fd7dd
MS
785 repeat_count, /* Repeat count */
786 repeat_interval, /* Repeat interval */
787 repeat_prev, /* Previous repeat interval */
83e08001 788 repeat_test; /* Repeat a test? */
f8b3a85b
MS
789 http_t *http = NULL; /* HTTP connection to server */
790 FILE *fp = NULL; /* Test file */
791 char resource[512], /* Resource for request */
792 token[1024], /* Token from file */
ef416fc2 793 *tokenptr, /* Pointer into token */
85dda01c 794 temp[1024], /* Temporary string */
c41769ff
MS
795 buffer[8192], /* Copy buffer */
796 compression[16]; /* COMPRESSION value */
85dda01c
MS
797 ipp_t *request = NULL, /* IPP request */
798 *response = NULL; /* IPP response */
799 size_t length; /* Length of IPP request */
800 http_status_t status; /* HTTP status */
9b66acc5 801 cups_file_t *reqfile; /* File to send */
85dda01c 802 ssize_t bytes; /* Bytes read/written */
f8b3a85b 803 char attr[128]; /* Attribute name */
ef416fc2 804 ipp_op_t op; /* Operation */
805 ipp_tag_t group; /* Current group */
806 ipp_tag_t value; /* Current value type */
5a662dc0 807 ipp_attribute_t *attrptr, /* Attribute pointer */
f8b3a85b
MS
808 *found, /* Found attribute */
809 *lastcol = NULL; /* Last collection attribute */
1d47b929
MS
810 char name[1024], /* Name of test */
811 file_id[1024], /* File identifier */
812 test_id[1024]; /* Test identifier */
f8b3a85b
MS
813 char filename[1024]; /* Filename */
814 _cups_transfer_t transfer; /* To chunk or not to chunk */
030ae6a1
MS
815 int version, /* IPP version number to use */
816 skip_test; /* Skip this test? */
f8b3a85b
MS
817 int num_statuses = 0; /* Number of valid status codes */
818 _cups_status_t statuses[100], /* Valid status codes */
819 *last_status; /* Last STATUS (for predicates) */
820 int num_expects = 0; /* Number of expected attributes */
821 _cups_expect_t expects[200], /* Expected attributes */
5a662dc0
MS
822 *expect, /* Current expected attribute */
823 *last_expect; /* Last EXPECT (for predicates) */
f8b3a85b 824 int num_displayed = 0; /* Number of displayed attributes */
aaf19ab0
MS
825 char *displayed[200]; /* Displayed attributes */
826 size_t widths[200]; /* Width of columns */
dcb445bc
MS
827 cups_array_t *a, /* Duplicate attribute array */
828 *errors = NULL; /* Errors array */
829 const char *error; /* Current error */
ef416fc2 830
831
832 /*
833 * Open the test file...
834 */
835
836 if ((fp = fopen(testfile, "r")) == NULL)
837 {
1a18c85c 838 print_fatal_error(outfile, "Unable to open test file %s - %s", testfile,
f8b3a85b 839 strerror(errno));
030ae6a1
MS
840 pass = 0;
841 goto test_exit;
ef416fc2 842 }
843
844 /*
845 * Connect to the server...
846 */
847
c1420c87
MS
848 if ((http = httpConnect2(vars->hostname, vars->port, NULL, vars->family,
849 vars->encryption, 1, 30000, NULL)) == NULL)
1106b00e 850 {
1a18c85c 851 print_fatal_error(outfile, "Unable to connect to %s on port %d - %s", vars->hostname,
a4845881 852 vars->port, cupsLastErrorString());
1106b00e
MS
853 pass = 0;
854 goto test_exit;
855 }
856
c1420c87
MS
857#ifdef HAVE_LIBZ
858 httpSetDefaultField(http, HTTP_FIELD_ACCEPT_ENCODING,
859 "deflate, gzip, identity");
860#else
861 httpSetDefaultField(http, HTTP_FIELD_ACCEPT_ENCODING, "identity");
862#endif /* HAVE_LIBZ */
ef416fc2 863
10d09e33 864 if (vars->timeout > 0.0)
f228370c 865 httpSetTimeout(http, vars->timeout, timeout_cb, NULL);
10d09e33 866
ef416fc2 867 /*
868 * Loop on tests...
869 */
870
f8b3a85b
MS
871 CUPS_SRAND(time(NULL));
872
dcb445bc
MS
873 errors = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup,
874 (cups_afree_func_t)free);
1d47b929 875 file_id[0] = '\0';
f8b3a85b
MS
876 pass = 1;
877 linenum = 1;
878 request_id = (CUPS_RAND() % 1000) * 137 + 1;
ef416fc2 879
85dda01c 880 while (!Cancel && get_token(fp, token, sizeof(token), &linenum) != NULL)
ef416fc2 881 {
882 /*
883 * Expect an open brace...
884 */
885
f8b3a85b
MS
886 if (!strcmp(token, "DEFINE"))
887 {
888 /*
889 * DEFINE name value
890 */
891
892 if (get_token(fp, attr, sizeof(attr), &linenum) &&
893 get_token(fp, temp, sizeof(temp), &linenum))
894 {
895 expand_variables(vars, token, temp, sizeof(token));
1a18c85c 896 set_variable(outfile, vars, attr, token);
f8b3a85b
MS
897 }
898 else
899 {
1a18c85c 900 print_fatal_error(outfile, "Missing DEFINE name and/or value on line %d.",
f8b3a85b 901 linenum);
030ae6a1
MS
902 pass = 0;
903 goto test_exit;
904 }
905
906 continue;
907 }
321d8d57
MS
908 else if (!strcmp(token, "DEFINE-DEFAULT"))
909 {
910 /*
911 * DEFINE-DEFAULT name value
912 */
913
914 if (get_token(fp, attr, sizeof(attr), &linenum) &&
915 get_token(fp, temp, sizeof(temp), &linenum))
916 {
917 expand_variables(vars, token, temp, sizeof(token));
918 if (!get_variable(vars, attr))
1a18c85c 919 set_variable(outfile, vars, attr, token);
321d8d57
MS
920 }
921 else
922 {
1a18c85c 923 print_fatal_error(outfile, "Missing DEFINE-DEFAULT name and/or value on line "
321d8d57
MS
924 "%d.", linenum);
925 pass = 0;
926 goto test_exit;
927 }
928
929 continue;
930 }
1d47b929
MS
931 else if (!strcmp(token, "FILE-ID"))
932 {
933 /*
934 * FILE-ID "string"
935 */
936
937 if (get_token(fp, temp, sizeof(temp), &linenum))
938 {
939 expand_variables(vars, file_id, temp, sizeof(file_id));
940 }
941 else
942 {
1a18c85c 943 print_fatal_error(outfile, "Missing FILE-ID value on line %d.", linenum);
1d47b929
MS
944 pass = 0;
945 goto test_exit;
946 }
947
948 continue;
949 }
030ae6a1
MS
950 else if (!strcmp(token, "IGNORE-ERRORS"))
951 {
952 /*
953 * IGNORE-ERRORS yes
954 * IGNORE-ERRORS no
955 */
956
957 if (get_token(fp, temp, sizeof(temp), &linenum) &&
88f9aafc 958 (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
030ae6a1 959 {
88f9aafc 960 IgnoreErrors = !_cups_strcasecmp(temp, "yes");
030ae6a1
MS
961 }
962 else
963 {
1a18c85c 964 print_fatal_error(outfile, "Missing IGNORE-ERRORS value on line %d.", linenum);
030ae6a1
MS
965 pass = 0;
966 goto test_exit;
f8b3a85b
MS
967 }
968
969 continue;
970 }
971 else if (!strcmp(token, "INCLUDE"))
972 {
973 /*
974 * INCLUDE "filename"
975 * INCLUDE <filename>
976 */
977
978 if (get_token(fp, temp, sizeof(temp), &linenum))
979 {
980 /*
981 * Map the filename to and then run the tests...
982 */
983
1a18c85c 984 if (!do_tests(outfile, vars, get_filename(testfile, filename, temp, sizeof(filename))))
030ae6a1
MS
985 {
986 pass = 0;
987
9475ec92 988 if (StopAfterIncludeError)
030ae6a1
MS
989 goto test_exit;
990 }
f8b3a85b
MS
991 }
992 else
993 {
1a18c85c 994 print_fatal_error(outfile, "Missing INCLUDE filename on line %d.", linenum);
030ae6a1
MS
995 pass = 0;
996 goto test_exit;
f8b3a85b
MS
997 }
998
cc754834 999 show_header = 1;
f8b3a85b
MS
1000 continue;
1001 }
321d8d57
MS
1002 else if (!strcmp(token, "INCLUDE-IF-DEFINED"))
1003 {
1004 /*
1005 * INCLUDE-IF-DEFINED name "filename"
1006 * INCLUDE-IF-DEFINED name <filename>
1007 */
1008
1009 if (get_token(fp, attr, sizeof(attr), &linenum) &&
1010 get_token(fp, temp, sizeof(temp), &linenum))
1011 {
1012 /*
1013 * Map the filename to and then run the tests...
1014 */
1015
1016 if (get_variable(vars, attr) &&
1a18c85c 1017 !do_tests(outfile, vars, get_filename(testfile, filename, temp, sizeof(filename))))
321d8d57
MS
1018 {
1019 pass = 0;
1020
9475ec92 1021 if (StopAfterIncludeError)
321d8d57
MS
1022 goto test_exit;
1023 }
1024 }
1025 else
1026 {
1a18c85c 1027 print_fatal_error(outfile, "Missing INCLUDE-IF-DEFINED name or filename on line "
321d8d57
MS
1028 "%d.", linenum);
1029 pass = 0;
1030 goto test_exit;
1031 }
1032
1033 show_header = 1;
1034 continue;
1035 }
1036 else if (!strcmp(token, "INCLUDE-IF-NOT-DEFINED"))
1037 {
1038 /*
1039 * INCLUDE-IF-NOT-DEFINED name "filename"
1040 * INCLUDE-IF-NOT-DEFINED name <filename>
1041 */
1042
1043 if (get_token(fp, attr, sizeof(attr), &linenum) &&
1044 get_token(fp, temp, sizeof(temp), &linenum))
1045 {
1046 /*
1047 * Map the filename to and then run the tests...
1048 */
1049
1050 if (!get_variable(vars, attr) &&
1a18c85c 1051 !do_tests(outfile, vars, get_filename(testfile, filename, temp, sizeof(filename))))
321d8d57
MS
1052 {
1053 pass = 0;
1054
9475ec92 1055 if (StopAfterIncludeError)
321d8d57
MS
1056 goto test_exit;
1057 }
1058 }
1059 else
1060 {
1a18c85c 1061 print_fatal_error(outfile, "Missing INCLUDE-IF-NOT-DEFINED name or filename on "
321d8d57
MS
1062 "line %d.", linenum);
1063 pass = 0;
1064 goto test_exit;
1065 }
1066
1067 show_header = 1;
1068 continue;
1069 }
030ae6a1
MS
1070 else if (!strcmp(token, "SKIP-IF-DEFINED"))
1071 {
1072 /*
1073 * SKIP-IF-DEFINED variable
1074 */
1075
1076 if (get_token(fp, temp, sizeof(temp), &linenum))
1077 {
1078 if (get_variable(vars, temp))
1079 goto test_exit;
1080 }
1081 else
1082 {
1a18c85c 1083 print_fatal_error(outfile, "Missing SKIP-IF-DEFINED variable on line %d.",
321d8d57 1084 linenum);
030ae6a1
MS
1085 pass = 0;
1086 goto test_exit;
1087 }
1088 }
1089 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
1090 {
1091 /*
1092 * SKIP-IF-NOT-DEFINED variable
1093 */
1094
1095 if (get_token(fp, temp, sizeof(temp), &linenum))
1096 {
1097 if (!get_variable(vars, temp))
1098 goto test_exit;
1099 }
1100 else
1101 {
1a18c85c 1102 print_fatal_error(outfile, "Missing SKIP-IF-NOT-DEFINED variable on line %d.",
030ae6a1
MS
1103 linenum);
1104 pass = 0;
1105 goto test_exit;
1106 }
1107 }
9475ec92
MS
1108 else if (!strcmp(token, "STOP-AFTER-INCLUDE-ERROR"))
1109 {
1110 /*
1111 * STOP-AFTER-INCLUDE-ERROR yes
1112 * STOP-AFTER-INCLUDE-ERROR no
1113 */
1114
1115 if (get_token(fp, temp, sizeof(temp), &linenum) &&
1116 (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
1117 {
1118 StopAfterIncludeError = !_cups_strcasecmp(temp, "yes");
1119 }
1120 else
1121 {
1a18c85c 1122 print_fatal_error(outfile, "Missing STOP-AFTER-INCLUDE-ERROR value on line %d.",
9475ec92
MS
1123 linenum);
1124 pass = 0;
1125 goto test_exit;
1126 }
1127
1128 continue;
1129 }
f8b3a85b
MS
1130 else if (!strcmp(token, "TRANSFER"))
1131 {
1132 /*
1133 * TRANSFER auto
1134 * TRANSFER chunked
1135 * TRANSFER length
1136 */
1137
1138 if (get_token(fp, temp, sizeof(temp), &linenum))
1139 {
1140 if (!strcmp(temp, "auto"))
1141 Transfer = _CUPS_TRANSFER_AUTO;
1142 else if (!strcmp(temp, "chunked"))
1143 Transfer = _CUPS_TRANSFER_CHUNKED;
1144 else if (!strcmp(temp, "length"))
1145 Transfer = _CUPS_TRANSFER_LENGTH;
1146 else
1147 {
1a18c85c 1148 print_fatal_error(outfile, "Bad TRANSFER value \"%s\" on line %d.", temp,
f8b3a85b 1149 linenum);
030ae6a1
MS
1150 pass = 0;
1151 goto test_exit;
f8b3a85b
MS
1152 }
1153 }
1154 else
1155 {
1a18c85c 1156 print_fatal_error(outfile, "Missing TRANSFER value on line %d.", linenum);
030ae6a1
MS
1157 pass = 0;
1158 goto test_exit;
f8b3a85b
MS
1159 }
1160
1161 continue;
1162 }
1163 else if (!strcmp(token, "VERSION"))
1164 {
1165 if (get_token(fp, temp, sizeof(temp), &linenum))
1166 {
1167 if (!strcmp(temp, "1.0"))
1168 Version = 10;
1169 else if (!strcmp(temp, "1.1"))
1170 Version = 11;
1171 else if (!strcmp(temp, "2.0"))
1172 Version = 20;
1173 else if (!strcmp(temp, "2.1"))
1174 Version = 21;
1175 else if (!strcmp(temp, "2.2"))
1176 Version = 22;
1177 else
1178 {
1a18c85c 1179 print_fatal_error(outfile, "Bad VERSION \"%s\" on line %d.", temp, linenum);
030ae6a1
MS
1180 pass = 0;
1181 goto test_exit;
f8b3a85b
MS
1182 }
1183 }
1184 else
1185 {
1a18c85c 1186 print_fatal_error(outfile, "Missing VERSION number on line %d.", linenum);
030ae6a1
MS
1187 pass = 0;
1188 goto test_exit;
f8b3a85b
MS
1189 }
1190
1191 continue;
1192 }
1193 else if (strcmp(token, "{"))
ef416fc2 1194 {
1a18c85c 1195 print_fatal_error(outfile, "Unexpected token %s seen on line %d.", token, linenum);
030ae6a1
MS
1196 pass = 0;
1197 goto test_exit;
ef416fc2 1198 }
1199
1200 /*
1201 * Initialize things...
1202 */
1203
cc754834
MS
1204 if (show_header)
1205 {
1206 if (Output == _CUPS_OUTPUT_PLIST)
1a18c85c
MS
1207 print_xml_header(outfile);
1208 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
cc754834
MS
1209 printf("\"%s\":\n", testfile);
1210
1211 show_header = 0;
1212 }
1213
f8b3a85b 1214 strlcpy(resource, vars->resource, sizeof(resource));
ef416fc2 1215
f8b3a85b 1216 request_id ++;
c41769ff
MS
1217 request = ippNew();
1218 op = (ipp_op_t)0;
1219 group = IPP_TAG_ZERO;
1220 ignore_errors = IgnoreErrors;
1221 last_expect = NULL;
1222 last_status = NULL;
1223 filename[0] = '\0';
1224 skip_previous = 0;
1225 skip_test = 0;
1d47b929 1226 test_id[0] = '\0';
c41769ff
MS
1227 version = Version;
1228 transfer = Transfer;
1229 compression[0] = '\0';
ef416fc2 1230
f8b3a85b 1231 strlcpy(name, testfile, sizeof(name));
ef416fc2 1232 if (strrchr(name, '.') != NULL)
1233 *strrchr(name, '.') = '\0';
1234
1235 /*
1236 * Parse until we see a close brace...
1237 */
1238
1239 while (get_token(fp, token, sizeof(token), &linenum) != NULL)
1240 {
88f9aafc
MS
1241 if (_cups_strcasecmp(token, "COUNT") &&
1242 _cups_strcasecmp(token, "DEFINE-MATCH") &&
1243 _cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
1244 _cups_strcasecmp(token, "DEFINE-VALUE") &&
1245 _cups_strcasecmp(token, "IF-DEFINED") &&
1246 _cups_strcasecmp(token, "IF-NOT-DEFINED") &&
1247 _cups_strcasecmp(token, "IN-GROUP") &&
1248 _cups_strcasecmp(token, "OF-TYPE") &&
a29fd7dd 1249 _cups_strcasecmp(token, "REPEAT-LIMIT") &&
83e08001
MS
1250 _cups_strcasecmp(token, "REPEAT-MATCH") &&
1251 _cups_strcasecmp(token, "REPEAT-NO-MATCH") &&
88f9aafc 1252 _cups_strcasecmp(token, "SAME-COUNT-AS") &&
82cc1f9a 1253 _cups_strcasecmp(token, "WITH-ALL-VALUES") &&
890a10b7
MS
1254 _cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") &&
1255 _cups_strcasecmp(token, "WITH-ALL-RESOURCES") &&
1256 _cups_strcasecmp(token, "WITH-ALL-SCHEMES") &&
1257 _cups_strcasecmp(token, "WITH-HOSTNAME") &&
1258 _cups_strcasecmp(token, "WITH-RESOURCE") &&
1259 _cups_strcasecmp(token, "WITH-SCHEME") &&
88f9aafc 1260 _cups_strcasecmp(token, "WITH-VALUE"))
5a662dc0
MS
1261 last_expect = NULL;
1262
0fa6c7fa
MS
1263 if (_cups_strcasecmp(token, "DEFINE-MATCH") &&
1264 _cups_strcasecmp(token, "DEFINE-NO-MATCH") &&
1265 _cups_strcasecmp(token, "IF-DEFINED") &&
83e08001 1266 _cups_strcasecmp(token, "IF-NOT-DEFINED") &&
a29fd7dd 1267 _cups_strcasecmp(token, "REPEAT-LIMIT") &&
83e08001
MS
1268 _cups_strcasecmp(token, "REPEAT-MATCH") &&
1269 _cups_strcasecmp(token, "REPEAT-NO-MATCH"))
f8b3a85b
MS
1270 last_status = NULL;
1271
ef416fc2 1272 if (!strcmp(token, "}"))
1273 break;
f8b3a85b
MS
1274 else if (!strcmp(token, "{") && lastcol)
1275 {
1276 /*
1277 * Another collection value
1278 */
1279
1a18c85c 1280 ipp_t *col = get_collection(outfile, vars, fp, &linenum);
f8b3a85b
MS
1281 /* Collection value */
1282
1283 if (col)
1284 {
e5d9b5e1
MS
1285 ippSetCollection(request, &lastcol, ippGetCount(lastcol), col);
1286 }
f8b3a85b 1287 else
030ae6a1
MS
1288 {
1289 pass = 0;
1290 goto test_exit;
1291 }
f8b3a85b 1292 }
c41769ff
MS
1293 else if (!strcmp(token, "COMPRESSION"))
1294 {
1295 /*
1296 * COMPRESSION none
1297 * COMPRESSION deflate
1298 * COMPRESSION gzip
1299 */
1300
1301 if (get_token(fp, temp, sizeof(temp), &linenum))
1302 {
1303 expand_variables(vars, compression, temp, sizeof(compression));
1304#ifdef HAVE_LIBZ
1305 if (strcmp(compression, "none") && strcmp(compression, "deflate") &&
1306 strcmp(compression, "gzip"))
1307#else
1308 if (strcmp(compression, "none"))
1309#endif /* HAVE_LIBZ */
1310 {
1a18c85c 1311 print_fatal_error(outfile, "Unsupported COMPRESSION value '%s' on line %d.",
c41769ff
MS
1312 compression, linenum);
1313 pass = 0;
1314 goto test_exit;
1315 }
1316
1317 if (!strcmp(compression, "none"))
1318 compression[0] = '\0';
1319 }
1320 else
1321 {
1a18c85c 1322 print_fatal_error(outfile, "Missing COMPRESSION value on line %d.", linenum);
c41769ff
MS
1323 pass = 0;
1324 goto test_exit;
1325 }
1326 }
f8b3a85b
MS
1327 else if (!strcmp(token, "DEFINE"))
1328 {
1329 /*
1330 * DEFINE name value
1331 */
1332
1333 if (get_token(fp, attr, sizeof(attr), &linenum) &&
1334 get_token(fp, temp, sizeof(temp), &linenum))
1335 {
1336 expand_variables(vars, token, temp, sizeof(token));
1a18c85c 1337 set_variable(outfile, vars, attr, token);
f8b3a85b
MS
1338 }
1339 else
1340 {
1a18c85c 1341 print_fatal_error(outfile, "Missing DEFINE name and/or value on line %d.",
f8b3a85b 1342 linenum);
030ae6a1
MS
1343 pass = 0;
1344 goto test_exit;
f8b3a85b
MS
1345 }
1346 }
030ae6a1
MS
1347 else if (!strcmp(token, "IGNORE-ERRORS"))
1348 {
1349 /*
1350 * IGNORE-ERRORS yes
1351 * IGNORE-ERRORS no
1352 */
1353
1354 if (get_token(fp, temp, sizeof(temp), &linenum) &&
88f9aafc 1355 (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
030ae6a1 1356 {
88f9aafc 1357 ignore_errors = !_cups_strcasecmp(temp, "yes");
030ae6a1
MS
1358 }
1359 else
1360 {
1a18c85c 1361 print_fatal_error(outfile, "Missing IGNORE-ERRORS value on line %d.", linenum);
030ae6a1
MS
1362 pass = 0;
1363 goto test_exit;
1364 }
1365
1366 continue;
1367 }
88f9aafc 1368 else if (!_cups_strcasecmp(token, "NAME"))
ef416fc2 1369 {
1370 /*
1371 * Name of test...
1372 */
1373
1374 get_token(fp, name, sizeof(name), &linenum);
1375 }
1a18c85c
MS
1376 else if (!_cups_strcasecmp(token, "PAUSE"))
1377 {
1378 /*
1379 * Pause with a message...
1380 */
1381
1382 get_token(fp, token, sizeof(token), &linenum);
1383 pause_message(token);
1384 }
f8b3a85b 1385 else if (!strcmp(token, "REQUEST-ID"))
ef416fc2 1386 {
1387 /*
f8b3a85b
MS
1388 * REQUEST-ID #
1389 * REQUEST-ID random
ef416fc2 1390 */
1391
f8b3a85b
MS
1392 if (get_token(fp, temp, sizeof(temp), &linenum))
1393 {
1394 if (isdigit(temp[0] & 255))
1395 request_id = atoi(temp);
88f9aafc 1396 else if (!_cups_strcasecmp(temp, "random"))
f8b3a85b
MS
1397 request_id = (CUPS_RAND() % 1000) * 137 + 1;
1398 else
1399 {
1a18c85c 1400 print_fatal_error(outfile, "Bad REQUEST-ID value \"%s\" on line %d.", temp,
f8b3a85b 1401 linenum);
030ae6a1
MS
1402 pass = 0;
1403 goto test_exit;
f8b3a85b
MS
1404 }
1405 }
1406 else
1407 {
1a18c85c 1408 print_fatal_error(outfile, "Missing REQUEST-ID value on line %d.", linenum);
030ae6a1
MS
1409 pass = 0;
1410 goto test_exit;
1411 }
1412 }
1413 else if (!strcmp(token, "SKIP-IF-DEFINED"))
1414 {
1415 /*
1416 * SKIP-IF-DEFINED variable
1417 */
1418
1419 if (get_token(fp, temp, sizeof(temp), &linenum))
1420 {
1421 if (get_variable(vars, temp))
1422 skip_test = 1;
1423 }
1424 else
1425 {
1a18c85c 1426 print_fatal_error(outfile, "Missing SKIP-IF-DEFINED value on line %d.",
030ae6a1
MS
1427 linenum);
1428 pass = 0;
1429 goto test_exit;
1430 }
1431 }
db8b865d
MS
1432 else if (!strcmp(token, "SKIP-IF-MISSING"))
1433 {
1434 /*
1435 * SKIP-IF-MISSING filename
1436 */
1437
1438 if (get_token(fp, temp, sizeof(temp), &linenum))
1439 {
1440 expand_variables(vars, token, temp, sizeof(token));
1441 get_filename(testfile, filename, token, sizeof(filename));
1442
1443 if (access(filename, R_OK))
1444 skip_test = 1;
1445 }
1446 else
1447 {
1a18c85c 1448 print_fatal_error(outfile, "Missing SKIP-IF-MISSING filename on line %d.",
db8b865d
MS
1449 linenum);
1450 pass = 0;
1451 goto test_exit;
1452 }
1453 }
030ae6a1
MS
1454 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
1455 {
1456 /*
1457 * SKIP-IF-NOT-DEFINED variable
1458 */
1459
1460 if (get_token(fp, temp, sizeof(temp), &linenum))
1461 {
1462 if (!get_variable(vars, temp))
1463 skip_test = 1;
1464 }
1465 else
1466 {
1a18c85c 1467 print_fatal_error(outfile, "Missing SKIP-IF-NOT-DEFINED value on line %d.",
030ae6a1
MS
1468 linenum);
1469 pass = 0;
1470 goto test_exit;
1471 }
1472 }
1473 else if (!strcmp(token, "SKIP-PREVIOUS-ERROR"))
1474 {
1475 /*
1476 * SKIP-PREVIOUS-ERROR yes
1477 * SKIP-PREVIOUS-ERROR no
1478 */
1479
1480 if (get_token(fp, temp, sizeof(temp), &linenum) &&
88f9aafc 1481 (!_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "no")))
030ae6a1 1482 {
88f9aafc 1483 skip_previous = !_cups_strcasecmp(temp, "yes");
f8b3a85b 1484 }
030ae6a1
MS
1485 else
1486 {
1a18c85c 1487 print_fatal_error(outfile, "Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum);
030ae6a1
MS
1488 pass = 0;
1489 goto test_exit;
1490 }
1491
1492 continue;
f8b3a85b 1493 }
1d47b929
MS
1494 else if (!strcmp(token, "TEST-ID"))
1495 {
1496 /*
1497 * TEST-ID "string"
1498 */
1499
1500 if (get_token(fp, temp, sizeof(temp), &linenum))
1501 {
1502 expand_variables(vars, test_id, temp, sizeof(test_id));
1503 }
1504 else
1505 {
1a18c85c 1506 print_fatal_error(outfile, "Missing TEST-ID value on line %d.", linenum);
1d47b929
MS
1507 pass = 0;
1508 goto test_exit;
1509 }
1510
1511 continue;
1512 }
f8b3a85b
MS
1513 else if (!strcmp(token, "TRANSFER"))
1514 {
1515 /*
1516 * TRANSFER auto
1517 * TRANSFER chunked
1518 * TRANSFER length
1519 */
d2354e63 1520
f8b3a85b
MS
1521 if (get_token(fp, temp, sizeof(temp), &linenum))
1522 {
1523 if (!strcmp(temp, "auto"))
1524 transfer = _CUPS_TRANSFER_AUTO;
1525 else if (!strcmp(temp, "chunked"))
1526 transfer = _CUPS_TRANSFER_CHUNKED;
1527 else if (!strcmp(temp, "length"))
1528 transfer = _CUPS_TRANSFER_LENGTH;
1529 else
1530 {
1a18c85c 1531 print_fatal_error(outfile, "Bad TRANSFER value \"%s\" on line %d.", temp,
f8b3a85b 1532 linenum);
030ae6a1
MS
1533 pass = 0;
1534 goto test_exit;
f8b3a85b
MS
1535 }
1536 }
1537 else
1538 {
1a18c85c 1539 print_fatal_error(outfile, "Missing TRANSFER value on line %d.", linenum);
030ae6a1
MS
1540 pass = 0;
1541 goto test_exit;
f8b3a85b
MS
1542 }
1543 }
88f9aafc 1544 else if (!_cups_strcasecmp(token, "VERSION"))
f8b3a85b
MS
1545 {
1546 if (get_token(fp, temp, sizeof(temp), &linenum))
1547 {
1548 if (!strcmp(temp, "0.0"))
1549 version = 0;
1550 else if (!strcmp(temp, "1.0"))
1551 version = 10;
1552 else if (!strcmp(temp, "1.1"))
1553 version = 11;
1554 else if (!strcmp(temp, "2.0"))
1555 version = 20;
1556 else if (!strcmp(temp, "2.1"))
1557 version = 21;
1558 else if (!strcmp(temp, "2.2"))
1559 version = 22;
1560 else
1561 {
1a18c85c 1562 print_fatal_error(outfile, "Bad VERSION \"%s\" on line %d.", temp, linenum);
030ae6a1
MS
1563 pass = 0;
1564 goto test_exit;
f8b3a85b
MS
1565 }
1566 }
d2354e63
MS
1567 else
1568 {
1a18c85c 1569 print_fatal_error(outfile, "Missing VERSION number on line %d.", linenum);
030ae6a1
MS
1570 pass = 0;
1571 goto test_exit;
d2354e63 1572 }
ef416fc2 1573 }
88f9aafc 1574 else if (!_cups_strcasecmp(token, "RESOURCE"))
ef416fc2 1575 {
1576 /*
1577 * Resource name...
1578 */
1579
f8b3a85b
MS
1580 if (!get_token(fp, resource, sizeof(resource), &linenum))
1581 {
1a18c85c 1582 print_fatal_error(outfile, "Missing RESOURCE path on line %d.", linenum);
030ae6a1
MS
1583 pass = 0;
1584 goto test_exit;
f8b3a85b 1585 }
ef416fc2 1586 }
88f9aafc 1587 else if (!_cups_strcasecmp(token, "OPERATION"))
ef416fc2 1588 {
1589 /*
1590 * Operation...
1591 */
1592
0fa6c7fa 1593 if (!get_token(fp, temp, sizeof(temp), &linenum))
f8b3a85b 1594 {
1a18c85c 1595 print_fatal_error(outfile, "Missing OPERATION code on line %d.", linenum);
030ae6a1
MS
1596 pass = 0;
1597 goto test_exit;
f8b3a85b
MS
1598 }
1599
0fa6c7fa
MS
1600 expand_variables(vars, token, temp, sizeof(token));
1601
84315f46 1602 if ((op = ippOpValue(token)) == (ipp_op_t)-1 &&
1a18c85c 1603 (op = (ipp_op_t)strtol(token, NULL, 0)) == 0)
f8b3a85b 1604 {
1a18c85c 1605 print_fatal_error(outfile, "Bad OPERATION code \"%s\" on line %d.", token,
f8b3a85b 1606 linenum);
030ae6a1
MS
1607 pass = 0;
1608 goto test_exit;
f8b3a85b 1609 }
ef416fc2 1610 }
88f9aafc 1611 else if (!_cups_strcasecmp(token, "GROUP"))
ef416fc2 1612 {
1613 /*
1614 * Attribute group...
1615 */
1616
f8b3a85b
MS
1617 if (!get_token(fp, token, sizeof(token), &linenum))
1618 {
1a18c85c 1619 print_fatal_error(outfile, "Missing GROUP tag on line %d.", linenum);
030ae6a1
MS
1620 pass = 0;
1621 goto test_exit;
f8b3a85b
MS
1622 }
1623
1624 if ((value = ippTagValue(token)) < 0)
1625 {
1a18c85c 1626 print_fatal_error(outfile, "Bad GROUP tag \"%s\" on line %d.", token, linenum);
030ae6a1
MS
1627 pass = 0;
1628 goto test_exit;
f8b3a85b 1629 }
ef416fc2 1630
1631 if (value == group)
1632 ippAddSeparator(request);
1633
1634 group = value;
1635 }
88f9aafc 1636 else if (!_cups_strcasecmp(token, "DELAY"))
ef416fc2 1637 {
1638 /*
1639 * Delay before operation...
1640 */
1641
10d09e33 1642 double delay;
ef416fc2 1643
0fa6c7fa 1644 if (!get_token(fp, temp, sizeof(temp), &linenum))
f8b3a85b 1645 {
1a18c85c 1646 print_fatal_error(outfile, "Missing DELAY value on line %d.", linenum);
030ae6a1
MS
1647 pass = 0;
1648 goto test_exit;
f8b3a85b
MS
1649 }
1650
0fa6c7fa
MS
1651 expand_variables(vars, token, temp, sizeof(token));
1652
10d09e33 1653 if ((delay = _cupsStrScand(token, NULL, localeconv())) <= 0.0)
f8b3a85b 1654 {
1a18c85c 1655 print_fatal_error(outfile, "Bad DELAY value \"%s\" on line %d.", token,
f8b3a85b 1656 linenum);
030ae6a1
MS
1657 pass = 0;
1658 goto test_exit;
f8b3a85b
MS
1659 }
1660 else
7cf5915e
MS
1661 {
1662 if (Output == _CUPS_OUTPUT_TEST)
10d09e33 1663 printf(" [%g second delay]\n", delay);
7cf5915e 1664
1a18c85c 1665 usleep((useconds_t)(1000000.0 * delay));
7cf5915e 1666 }
ef416fc2 1667 }
88f9aafc 1668 else if (!_cups_strcasecmp(token, "ATTR"))
ef416fc2 1669 {
1670 /*
1671 * Attribute...
1672 */
1673
f8b3a85b
MS
1674 if (!get_token(fp, token, sizeof(token), &linenum))
1675 {
1a18c85c 1676 print_fatal_error(outfile, "Missing ATTR value tag on line %d.", linenum);
030ae6a1
MS
1677 pass = 0;
1678 goto test_exit;
f8b3a85b 1679 }
ef416fc2 1680
5a6b583a 1681 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
f8b3a85b 1682 {
1a18c85c 1683 print_fatal_error(outfile, "Bad ATTR value tag \"%s\" on line %d.", token,
f8b3a85b 1684 linenum);
030ae6a1
MS
1685 pass = 0;
1686 goto test_exit;
f8b3a85b 1687 }
ef416fc2 1688
f8b3a85b
MS
1689 if (!get_token(fp, attr, sizeof(attr), &linenum))
1690 {
1a18c85c 1691 print_fatal_error(outfile, "Missing ATTR name on line %d.", linenum);
030ae6a1
MS
1692 pass = 0;
1693 goto test_exit;
f8b3a85b 1694 }
b94498cf 1695
f8b3a85b
MS
1696 if (!get_token(fp, temp, sizeof(temp), &linenum))
1697 {
1a18c85c 1698 print_fatal_error(outfile, "Missing ATTR value on line %d.", linenum);
030ae6a1
MS
1699 pass = 0;
1700 goto test_exit;
f8b3a85b 1701 }
b94498cf 1702
f8b3a85b 1703 expand_variables(vars, token, temp, sizeof(token));
a2326b5b 1704 attrptr = NULL;
ef416fc2 1705
1706 switch (value)
1707 {
1708 case IPP_TAG_BOOLEAN :
88f9aafc 1709 if (!_cups_strcasecmp(token, "true"))
a2326b5b 1710 attrptr = ippAddBoolean(request, group, attr, 1);
ef416fc2 1711 else
1a18c85c 1712 attrptr = ippAddBoolean(request, group, attr, (char)atoi(token));
ef416fc2 1713 break;
1714
1715 case IPP_TAG_INTEGER :
1716 case IPP_TAG_ENUM :
e60ec91f 1717 if (!strchr(token, ','))
1a18c85c 1718 attrptr = ippAddInteger(request, group, value, attr, (int)strtol(token, &tokenptr, 0));
e60ec91f
MS
1719 else
1720 {
1721 int values[100], /* Values */
1722 num_values = 1; /* Number of values */
1723
1a18c85c 1724 values[0] = (int)strtol(token, &tokenptr, 10);
e60ec91f
MS
1725 while (tokenptr && *tokenptr &&
1726 num_values < (int)(sizeof(values) / sizeof(values[0])))
1727 {
1728 if (*tokenptr == ',')
1729 tokenptr ++;
1730 else if (!isdigit(*tokenptr & 255) && *tokenptr != '-')
1731 break;
1732
1a18c85c 1733 values[num_values] = (int)strtol(tokenptr, &tokenptr, 0);
e60ec91f
MS
1734 num_values ++;
1735 }
1736
a2326b5b 1737 attrptr = ippAddIntegers(request, group, value, attr, num_values, values);
e60ec91f
MS
1738 }
1739
1740 if (!tokenptr || *tokenptr)
1741 {
1a18c85c 1742 print_fatal_error(outfile, "Bad %s value \"%s\" on line %d.",
e60ec91f
MS
1743 ippTagString(value), token, linenum);
1744 pass = 0;
1745 goto test_exit;
1746 }
ef416fc2 1747 break;
1748
1749 case IPP_TAG_RESOLUTION :
f8b3a85b
MS
1750 {
1751 int xres, /* X resolution */
030ae6a1
MS
1752 yres; /* Y resolution */
1753 char *ptr; /* Pointer into value */
cc754834 1754
1a18c85c 1755 xres = yres = (int)strtol(token, (char **)&ptr, 10);
cc754834
MS
1756 if (ptr > token && xres > 0)
1757 {
1758 if (*ptr == 'x')
1a18c85c 1759 yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
cc754834
MS
1760 }
1761
1762 if (ptr <= token || xres <= 0 || yres <= 0 || !ptr ||
3e7fe0ca
MS
1763 (_cups_strcasecmp(ptr, "dpi") &&
1764 _cups_strcasecmp(ptr, "dpc") &&
1765 _cups_strcasecmp(ptr, "dpcm") &&
88f9aafc 1766 _cups_strcasecmp(ptr, "other")))
cc754834 1767 {
1a18c85c 1768 print_fatal_error(outfile, "Bad resolution value \"%s\" on line %d.",
f8b3a85b 1769 token, linenum);
030ae6a1
MS
1770 pass = 0;
1771 goto test_exit;
cc754834
MS
1772 }
1773
88f9aafc 1774 if (!_cups_strcasecmp(ptr, "dpi"))
1a18c85c 1775 attrptr = ippAddResolution(request, group, attr, IPP_RES_PER_INCH, xres, yres);
3e7fe0ca
MS
1776 else if (!_cups_strcasecmp(ptr, "dpc") ||
1777 !_cups_strcasecmp(ptr, "dpcm"))
1a18c85c 1778 attrptr = ippAddResolution(request, group, attr, IPP_RES_PER_CM, xres, yres);
cc754834 1779 else
1a18c85c 1780 attrptr = ippAddResolution(request, group, attr, (ipp_res_t)0, xres, yres);
f8b3a85b 1781 }
ef416fc2 1782 break;
1783
1784 case IPP_TAG_RANGE :
f8b3a85b
MS
1785 {
1786 int lowers[4], /* Lower value */
1787 uppers[4], /* Upper values */
1788 num_vals; /* Number of values */
1789
1790
1791 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
1792 lowers + 0, uppers + 0,
1793 lowers + 1, uppers + 1,
1794 lowers + 2, uppers + 2,
1795 lowers + 3, uppers + 3);
1796
1797 if ((num_vals & 1) || num_vals == 0)
1798 {
1a18c85c 1799 print_fatal_error(outfile, "Bad rangeOfInteger value \"%s\" on line "
f8b3a85b 1800 "%d.", token, linenum);
030ae6a1
MS
1801 pass = 0;
1802 goto test_exit;
f8b3a85b
MS
1803 }
1804
a2326b5b
MS
1805 attrptr = ippAddRanges(request, group, attr, num_vals / 2, lowers,
1806 uppers);
f8b3a85b
MS
1807 }
1808 break;
1809
1810 case IPP_TAG_BEGIN_COLLECTION :
1811 if (!strcmp(token, "{"))
1812 {
1a18c85c 1813 ipp_t *col = get_collection(outfile, vars, fp, &linenum);
f8b3a85b
MS
1814 /* Collection value */
1815
1816 if (col)
aaf19ab0 1817 {
a2326b5b 1818 attrptr = lastcol = ippAddCollection(request, group, attr, col);
aaf19ab0
MS
1819 ippDelete(col);
1820 }
f8b3a85b 1821 else
030ae6a1
MS
1822 {
1823 pass = 0;
1824 goto test_exit;
1825 }
f8b3a85b
MS
1826 }
1827 else
1828 {
1a18c85c 1829 print_fatal_error(outfile, "Bad ATTR collection value on line %d.",
f8b3a85b 1830 linenum);
030ae6a1
MS
1831 pass = 0;
1832 goto test_exit;
f8b3a85b 1833 }
1a18c85c
MS
1834
1835 do
1836 {
1837 ipp_t *col; /* Collection value */
1838 long pos = ftell(fp); /* Save position of file */
1839
1840 if (!get_token(fp, token, sizeof(token), &linenum))
1841 break;
1842
1843 if (strcmp(token, ","))
1844 {
1845 fseek(fp, pos, SEEK_SET);
1846 break;
1847 }
1848
1849 if (!get_token(fp, token, sizeof(token), &linenum) || strcmp(token, "{"))
1850 {
1851 print_fatal_error(outfile, "Unexpected \"%s\" on line %d.", token, linenum);
1852 pass = 0;
1853 goto test_exit;
1854 break;
1855 }
1856
1857 if ((col = get_collection(outfile, vars, fp, &linenum)) == NULL)
1858 break;
1859
1860 ippSetCollection(request, &attrptr, ippGetCount(attrptr), col);
1861 lastcol = attrptr;
1862 }
1863 while (!strcmp(token, "{"));
ef416fc2 1864 break;
1865
5a9febac 1866 case IPP_TAG_STRING :
1a18c85c 1867 attrptr = ippAddOctetString(request, group, attr, token, (int)strlen(token));
5a9febac
MS
1868 break;
1869
ef416fc2 1870 default :
1a18c85c 1871 print_fatal_error(outfile, "Unsupported ATTR value tag %s on line %d.",
5a6b583a 1872 ippTagString(value), linenum);
030ae6a1
MS
1873 pass = 0;
1874 goto test_exit;
5a6b583a
MS
1875
1876 case IPP_TAG_TEXTLANG :
1877 case IPP_TAG_NAMELANG :
1878 case IPP_TAG_TEXT :
1879 case IPP_TAG_NAME :
1880 case IPP_TAG_KEYWORD :
1881 case IPP_TAG_URI :
1882 case IPP_TAG_URISCHEME :
1883 case IPP_TAG_CHARSET :
1884 case IPP_TAG_LANGUAGE :
1885 case IPP_TAG_MIMETYPE :
ef416fc2 1886 if (!strchr(token, ','))
a2326b5b 1887 attrptr = ippAddString(request, group, value, attr, NULL, token);
ef416fc2 1888 else
1889 {
1890 /*
1891 * Multiple string values...
1892 */
1893
1894 int num_values; /* Number of values */
1895 char *values[100], /* Values */
1896 *ptr; /* Pointer to next value */
1897
1898
1899 values[0] = token;
1900 num_values = 1;
1901
1902 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
1903 {
a469f8a5
MS
1904 if (ptr > token && ptr[-1] == '\\')
1905 _cups_strcpy(ptr - 1, ptr);
1906 else
1907 {
1908 *ptr++ = '\0';
1909 values[num_values] = ptr;
1910 num_values ++;
1911 }
ef416fc2 1912 }
1913
a2326b5b
MS
1914 attrptr = ippAddStrings(request, group, value, attr, num_values,
1915 NULL, (const char **)values);
ef416fc2 1916 }
1917 break;
1918 }
a2326b5b
MS
1919
1920 if (!attrptr)
1921 {
1a18c85c 1922 print_fatal_error(outfile, "Unable to add attribute on line %d: %s", linenum,
a2326b5b
MS
1923 cupsLastErrorString());
1924 pass = 0;
1925 goto test_exit;
1926 }
ef416fc2 1927 }
88f9aafc 1928 else if (!_cups_strcasecmp(token, "FILE"))
ef416fc2 1929 {
1930 /*
1931 * File...
1932 */
1933
f8b3a85b
MS
1934 if (!get_token(fp, temp, sizeof(temp), &linenum))
1935 {
1a18c85c 1936 print_fatal_error(outfile, "Missing FILE filename on line %d.", linenum);
030ae6a1
MS
1937 pass = 0;
1938 goto test_exit;
f8b3a85b
MS
1939 }
1940
aaf19ab0 1941 expand_variables(vars, token, temp, sizeof(token));
f8b3a85b 1942 get_filename(testfile, filename, token, sizeof(filename));
db8b865d
MS
1943
1944 if (access(filename, R_OK))
1945 {
1a18c85c 1946 print_fatal_error(outfile, "Filename \"%s\" on line %d cannot be read.",
db8b865d 1947 temp, linenum);
1a18c85c 1948 print_fatal_error(outfile, "Filename mapped to \"%s\".", filename);
db8b865d
MS
1949 pass = 0;
1950 goto test_exit;
1951 }
ef416fc2 1952 }
88f9aafc 1953 else if (!_cups_strcasecmp(token, "STATUS"))
ef416fc2 1954 {
1955 /*
1956 * Status...
1957 */
1958
f8b3a85b
MS
1959 if (num_statuses >= (int)(sizeof(statuses) / sizeof(statuses[0])))
1960 {
1a18c85c 1961 print_fatal_error(outfile, "Too many STATUS's on line %d.", linenum);
030ae6a1
MS
1962 pass = 0;
1963 goto test_exit;
f8b3a85b
MS
1964 }
1965
1966 if (!get_token(fp, token, sizeof(token), &linenum))
1967 {
1a18c85c 1968 print_fatal_error(outfile, "Missing STATUS code on line %d.", linenum);
030ae6a1
MS
1969 pass = 0;
1970 goto test_exit;
f8b3a85b
MS
1971 }
1972
84315f46
MS
1973 if ((statuses[num_statuses].status = ippErrorValue(token))
1974 == (ipp_status_t)-1 &&
1a18c85c 1975 (statuses[num_statuses].status = (ipp_status_t)strtol(token, NULL, 0)) == 0)
f8b3a85b 1976 {
1a18c85c 1977 print_fatal_error(outfile, "Bad STATUS code \"%s\" on line %d.", token,
f8b3a85b 1978 linenum);
030ae6a1
MS
1979 pass = 0;
1980 goto test_exit;
f8b3a85b
MS
1981 }
1982
1983 last_status = statuses + num_statuses;
ef416fc2 1984 num_statuses ++;
f8b3a85b 1985
0cb67df3
MS
1986 last_status->define_match = NULL;
1987 last_status->define_no_match = NULL;
83e08001
MS
1988 last_status->if_defined = NULL;
1989 last_status->if_not_defined = NULL;
a29fd7dd 1990 last_status->repeat_limit = 1000;
83e08001
MS
1991 last_status->repeat_match = 0;
1992 last_status->repeat_no_match = 0;
ef416fc2 1993 }
88f9aafc 1994 else if (!_cups_strcasecmp(token, "EXPECT"))
ef416fc2 1995 {
1996 /*
1997 * Expected attributes...
1998 */
1999
5a662dc0
MS
2000 if (num_expects >= (int)(sizeof(expects) / sizeof(expects[0])))
2001 {
1a18c85c 2002 print_fatal_error(outfile, "Too many EXPECT's on line %d.", linenum);
030ae6a1
MS
2003 pass = 0;
2004 goto test_exit;
5a662dc0
MS
2005 }
2006
f8b3a85b
MS
2007 if (!get_token(fp, token, sizeof(token), &linenum))
2008 {
1a18c85c 2009 print_fatal_error(outfile, "Missing EXPECT name on line %d.", linenum);
030ae6a1
MS
2010 pass = 0;
2011 goto test_exit;
f8b3a85b 2012 }
5a662dc0
MS
2013
2014 last_expect = expects + num_expects;
ef416fc2 2015 num_expects ++;
5a662dc0 2016
f8b3a85b 2017 memset(last_expect, 0, sizeof(_cups_expect_t));
a29fd7dd 2018 last_expect->repeat_limit = 1000;
f8b3a85b 2019
5a662dc0
MS
2020 if (token[0] == '!')
2021 {
2022 last_expect->not_expect = 1;
2023 last_expect->name = strdup(token + 1);
2024 }
f8b3a85b 2025 else if (token[0] == '?')
5a662dc0 2026 {
f8b3a85b
MS
2027 last_expect->optional = 1;
2028 last_expect->name = strdup(token + 1);
2029 }
2030 else
2031 last_expect->name = strdup(token);
2032 }
88f9aafc 2033 else if (!_cups_strcasecmp(token, "COUNT"))
f8b3a85b
MS
2034 {
2035 if (!get_token(fp, token, sizeof(token), &linenum))
2036 {
1a18c85c 2037 print_fatal_error(outfile, "Missing COUNT number on line %d.", linenum);
030ae6a1
MS
2038 pass = 0;
2039 goto test_exit;
f8b3a85b
MS
2040 }
2041
2042 if ((i = atoi(token)) <= 0)
2043 {
1a18c85c 2044 print_fatal_error(outfile, "Bad COUNT \"%s\" on line %d.", token, linenum);
030ae6a1
MS
2045 pass = 0;
2046 goto test_exit;
5a662dc0
MS
2047 }
2048
f8b3a85b
MS
2049 if (last_expect)
2050 last_expect->count = i;
2051 else
2052 {
1a18c85c 2053 print_fatal_error(outfile, "COUNT without a preceding EXPECT on line %d.",
f8b3a85b 2054 linenum);
030ae6a1
MS
2055 pass = 0;
2056 goto test_exit;
2057 }
2058 }
88f9aafc 2059 else if (!_cups_strcasecmp(token, "DEFINE-MATCH"))
030ae6a1
MS
2060 {
2061 if (!get_token(fp, token, sizeof(token), &linenum))
2062 {
1a18c85c 2063 print_fatal_error(outfile, "Missing DEFINE-MATCH variable on line %d.",
030ae6a1
MS
2064 linenum);
2065 pass = 0;
2066 goto test_exit;
2067 }
2068
2069 if (last_expect)
2070 last_expect->define_match = strdup(token);
0fa6c7fa
MS
2071 else if (last_status)
2072 last_status->define_match = strdup(token);
030ae6a1
MS
2073 else
2074 {
1a18c85c 2075 print_fatal_error(outfile, "DEFINE-MATCH without a preceding EXPECT or STATUS "
0fa6c7fa 2076 "on line %d.", linenum);
030ae6a1
MS
2077 pass = 0;
2078 goto test_exit;
2079 }
2080 }
88f9aafc 2081 else if (!_cups_strcasecmp(token, "DEFINE-NO-MATCH"))
030ae6a1
MS
2082 {
2083 if (!get_token(fp, token, sizeof(token), &linenum))
2084 {
1a18c85c 2085 print_fatal_error(outfile, "Missing DEFINE-NO-MATCH variable on line %d.",
030ae6a1
MS
2086 linenum);
2087 pass = 0;
2088 goto test_exit;
2089 }
2090
2091 if (last_expect)
2092 last_expect->define_no_match = strdup(token);
0fa6c7fa
MS
2093 else if (last_status)
2094 last_status->define_no_match = strdup(token);
030ae6a1
MS
2095 else
2096 {
1a18c85c 2097 print_fatal_error(outfile, "DEFINE-NO-MATCH without a preceding EXPECT or "
0fa6c7fa 2098 "STATUS on line %d.", linenum);
030ae6a1
MS
2099 pass = 0;
2100 goto test_exit;
2101 }
2102 }
88f9aafc 2103 else if (!_cups_strcasecmp(token, "DEFINE-VALUE"))
030ae6a1
MS
2104 {
2105 if (!get_token(fp, token, sizeof(token), &linenum))
2106 {
1a18c85c 2107 print_fatal_error(outfile, "Missing DEFINE-VALUE variable on line %d.",
030ae6a1
MS
2108 linenum);
2109 pass = 0;
2110 goto test_exit;
2111 }
2112
2113 if (last_expect)
2114 last_expect->define_value = strdup(token);
2115 else
2116 {
1a18c85c 2117 print_fatal_error(outfile, "DEFINE-VALUE without a preceding EXPECT on "
0fa6c7fa 2118 "line %d.", linenum);
030ae6a1
MS
2119 pass = 0;
2120 goto test_exit;
f8b3a85b 2121 }
5a662dc0 2122 }
88f9aafc 2123 else if (!_cups_strcasecmp(token, "OF-TYPE"))
5a662dc0 2124 {
f8b3a85b
MS
2125 if (!get_token(fp, token, sizeof(token), &linenum))
2126 {
1a18c85c 2127 print_fatal_error(outfile, "Missing OF-TYPE value tag(s) on line %d.",
f8b3a85b 2128 linenum);
030ae6a1
MS
2129 pass = 0;
2130 goto test_exit;
f8b3a85b 2131 }
5a662dc0
MS
2132
2133 if (last_expect)
2134 last_expect->of_type = strdup(token);
2135 else
2136 {
1a18c85c 2137 print_fatal_error(outfile, "OF-TYPE without a preceding EXPECT on line %d.",
f8b3a85b 2138 linenum);
030ae6a1
MS
2139 pass = 0;
2140 goto test_exit;
f8b3a85b
MS
2141 }
2142 }
88f9aafc 2143 else if (!_cups_strcasecmp(token, "IN-GROUP"))
f8b3a85b
MS
2144 {
2145 ipp_tag_t in_group; /* IN-GROUP value */
2146
2147
2148 if (!get_token(fp, token, sizeof(token), &linenum))
2149 {
1a18c85c 2150 print_fatal_error(outfile, "Missing IN-GROUP group tag on line %d.", linenum);
030ae6a1
MS
2151 pass = 0;
2152 goto test_exit;
f8b3a85b
MS
2153 }
2154
2155 if ((in_group = ippTagValue(token)) == (ipp_tag_t)-1)
2156 {
2157 }
2158 else if (last_expect)
2159 last_expect->in_group = in_group;
2160 else
2161 {
1a18c85c 2162 print_fatal_error(outfile, "IN-GROUP without a preceding EXPECT on line %d.",
f8b3a85b 2163 linenum);
030ae6a1
MS
2164 pass = 0;
2165 goto test_exit;
5a662dc0
MS
2166 }
2167 }
a29fd7dd
MS
2168 else if (!_cups_strcasecmp(token, "REPEAT-LIMIT"))
2169 {
2170 if (!get_token(fp, token, sizeof(token), &linenum))
2171 {
1a18c85c 2172 print_fatal_error(outfile, "Missing REPEAT-LIMIT value on line %d.", linenum);
a29fd7dd
MS
2173 pass = 0;
2174 goto test_exit;
2175 }
2176 else if (atoi(token) <= 0)
2177 {
1a18c85c 2178 print_fatal_error(outfile, "Bad REPEAT-LIMIT value on line %d.", linenum);
a29fd7dd
MS
2179 pass = 0;
2180 goto test_exit;
2181 }
2182
2183 if (last_status)
2184 last_status->repeat_limit = atoi(token);
2185 else if (last_expect)
2186 last_expect->repeat_limit = atoi(token);
2187 else
2188 {
1a18c85c 2189 print_fatal_error(outfile, "REPEAT-LIMIT without a preceding EXPECT or STATUS "
a29fd7dd
MS
2190 "on line %d.", linenum);
2191 pass = 0;
2192 goto test_exit;
2193 }
2194 }
83e08001
MS
2195 else if (!_cups_strcasecmp(token, "REPEAT-MATCH"))
2196 {
2197 if (last_status)
2198 last_status->repeat_match = 1;
2199 else if (last_expect)
2200 last_expect->repeat_match = 1;
2201 else
2202 {
1a18c85c 2203 print_fatal_error(outfile, "REPEAT-MATCH without a preceding EXPECT or STATUS "
83e08001
MS
2204 "on line %d.", linenum);
2205 pass = 0;
2206 goto test_exit;
2207 }
2208 }
2209 else if (!_cups_strcasecmp(token, "REPEAT-NO-MATCH"))
2210 {
2211 if (last_status)
2212 last_status->repeat_no_match = 1;
2213 else if (last_expect)
2214 last_expect->repeat_no_match = 1;
2215 else
2216 {
1a18c85c 2217 print_fatal_error(outfile, "REPEAT-NO-MATCH without a preceding EXPECT or "
83e08001
MS
2218 "STATUS on ine %d.", linenum);
2219 pass = 0;
2220 goto test_exit;
2221 }
2222 }
88f9aafc 2223 else if (!_cups_strcasecmp(token, "SAME-COUNT-AS"))
5a662dc0 2224 {
f8b3a85b
MS
2225 if (!get_token(fp, token, sizeof(token), &linenum))
2226 {
1a18c85c 2227 print_fatal_error(outfile, "Missing SAME-COUNT-AS name on line %d.", linenum);
030ae6a1
MS
2228 pass = 0;
2229 goto test_exit;
f8b3a85b 2230 }
5a662dc0
MS
2231
2232 if (last_expect)
2233 last_expect->same_count_as = strdup(token);
2234 else
2235 {
1a18c85c 2236 print_fatal_error(outfile, "SAME-COUNT-AS without a preceding EXPECT on line "
f8b3a85b 2237 "%d.", linenum);
030ae6a1
MS
2238 pass = 0;
2239 goto test_exit;
5a662dc0
MS
2240 }
2241 }
88f9aafc 2242 else if (!_cups_strcasecmp(token, "IF-DEFINED"))
5a662dc0 2243 {
f8b3a85b
MS
2244 if (!get_token(fp, token, sizeof(token), &linenum))
2245 {
1a18c85c 2246 print_fatal_error(outfile, "Missing IF-DEFINED name on line %d.", linenum);
030ae6a1
MS
2247 pass = 0;
2248 goto test_exit;
f8b3a85b 2249 }
5a662dc0
MS
2250
2251 if (last_expect)
2252 last_expect->if_defined = strdup(token);
f8b3a85b
MS
2253 else if (last_status)
2254 last_status->if_defined = strdup(token);
2255 else
2256 {
1a18c85c 2257 print_fatal_error(outfile, "IF-DEFINED without a preceding EXPECT or STATUS "
f8b3a85b 2258 "on line %d.", linenum);
030ae6a1
MS
2259 pass = 0;
2260 goto test_exit;
f8b3a85b
MS
2261 }
2262 }
88f9aafc 2263 else if (!_cups_strcasecmp(token, "IF-NOT-DEFINED"))
f8b3a85b
MS
2264 {
2265 if (!get_token(fp, token, sizeof(token), &linenum))
2266 {
1a18c85c 2267 print_fatal_error(outfile, "Missing IF-NOT-DEFINED name on line %d.", linenum);
030ae6a1
MS
2268 pass = 0;
2269 goto test_exit;
f8b3a85b
MS
2270 }
2271
2272 if (last_expect)
7cf5915e 2273 last_expect->if_not_defined = strdup(token);
f8b3a85b 2274 else if (last_status)
7cf5915e 2275 last_status->if_not_defined = strdup(token);
f8b3a85b
MS
2276 else
2277 {
1a18c85c 2278 print_fatal_error(outfile, "IF-NOT-DEFINED without a preceding EXPECT or STATUS "
f8b3a85b 2279 "on line %d.", linenum);
030ae6a1
MS
2280 pass = 0;
2281 goto test_exit;
f8b3a85b
MS
2282 }
2283 }
82cc1f9a 2284 else if (!_cups_strcasecmp(token, "WITH-ALL-VALUES") ||
890a10b7
MS
2285 !_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") ||
2286 !_cups_strcasecmp(token, "WITH-ALL-RESOURCES") ||
2287 !_cups_strcasecmp(token, "WITH-ALL-SCHEMES") ||
2288 !_cups_strcasecmp(token, "WITH-HOSTNAME") ||
2289 !_cups_strcasecmp(token, "WITH-RESOURCE") ||
2290 !_cups_strcasecmp(token, "WITH-SCHEME") ||
82cc1f9a 2291 !_cups_strcasecmp(token, "WITH-VALUE"))
f8b3a85b 2292 {
890a10b7
MS
2293 if (last_expect)
2294 {
2295 if (!_cups_strcasecmp(token, "WITH-ALL-HOSTNAMES") ||
2296 !_cups_strcasecmp(token, "WITH-HOSTNAME"))
2297 last_expect->with_flags = _CUPS_WITH_HOSTNAME;
2298 else if (!_cups_strcasecmp(token, "WITH-ALL-RESOURCES") ||
2299 !_cups_strcasecmp(token, "WITH-RESOURCE"))
2300 last_expect->with_flags = _CUPS_WITH_RESOURCE;
2301 else if (!_cups_strcasecmp(token, "WITH-ALL-SCHEMES") ||
2302 !_cups_strcasecmp(token, "WITH-SCHEME"))
2303 last_expect->with_flags = _CUPS_WITH_SCHEME;
2304
2305 if (!_cups_strncasecmp(token, "WITH-ALL-", 9))
2306 last_expect->with_flags |= _CUPS_WITH_ALL;
2307 }
82cc1f9a 2308
7cf5915e 2309 if (!get_token(fp, temp, sizeof(temp), &linenum))
f8b3a85b 2310 {
1a18c85c 2311 print_fatal_error(outfile, "Missing %s value on line %d.", token, linenum);
030ae6a1
MS
2312 pass = 0;
2313 goto test_exit;
f8b3a85b
MS
2314 }
2315
2316 if (last_expect)
2317 {
7cf5915e
MS
2318 /*
2319 * Expand any variables in the value and then save it.
2320 */
2321
2322 expand_variables(vars, token, temp, sizeof(token));
2323
f8b3a85b 2324 tokenptr = token + strlen(token) - 1;
7cf5915e 2325
f8b3a85b
MS
2326 if (token[0] == '/' && tokenptr > token && *tokenptr == '/')
2327 {
2328 /*
2329 * WITH-VALUE is a POSIX extended regular expression.
2330 */
2331
1a18c85c 2332 last_expect->with_value = calloc(1, (size_t)(tokenptr - token));
82cc1f9a 2333 last_expect->with_flags |= _CUPS_WITH_REGEX;
f8b3a85b
MS
2334
2335 if (last_expect->with_value)
1a18c85c 2336 memcpy(last_expect->with_value, token + 1, (size_t)(tokenptr - token - 1));
f8b3a85b
MS
2337 }
2338 else
2339 {
2340 /*
2341 * WITH-VALUE is a literal value...
2342 */
2343
a469f8a5
MS
2344 char *ptr; /* Pointer into value */
2345
2346 for (ptr = token; *ptr; ptr ++)
2347 {
2348 if (*ptr == '\\' && ptr[1])
2349 {
2350 /*
2351 * Remove \ from \foo...
2352 */
2353
2354 _cups_strcpy(ptr, ptr + 1);
2355 }
2356 }
2357
f8b3a85b 2358 last_expect->with_value = strdup(token);
82cc1f9a 2359 last_expect->with_flags |= _CUPS_WITH_LITERAL;
f8b3a85b
MS
2360 }
2361 }
5a662dc0
MS
2362 else
2363 {
1a18c85c 2364 print_fatal_error(outfile, "%s without a preceding EXPECT on line %d.", token,
f8b3a85b 2365 linenum);
030ae6a1
MS
2366 pass = 0;
2367 goto test_exit;
5a662dc0 2368 }
ef416fc2 2369 }
88f9aafc 2370 else if (!_cups_strcasecmp(token, "DISPLAY"))
ef416fc2 2371 {
2372 /*
2373 * Display attributes...
2374 */
2375
f8b3a85b
MS
2376 if (num_displayed >= (int)(sizeof(displayed) / sizeof(displayed[0])))
2377 {
1a18c85c 2378 print_fatal_error(outfile, "Too many DISPLAY's on line %d", linenum);
030ae6a1
MS
2379 pass = 0;
2380 goto test_exit;
f8b3a85b
MS
2381 }
2382
2383 if (!get_token(fp, token, sizeof(token), &linenum))
2384 {
1a18c85c 2385 print_fatal_error(outfile, "Missing DISPLAY name on line %d.", linenum);
030ae6a1
MS
2386 pass = 0;
2387 goto test_exit;
f8b3a85b
MS
2388 }
2389
ef416fc2 2390 displayed[num_displayed] = strdup(token);
2391 num_displayed ++;
2392 }
2393 else
2394 {
1a18c85c 2395 print_fatal_error(outfile, "Unexpected token %s seen on line %d.", token,
f8b3a85b 2396 linenum);
030ae6a1
MS
2397 pass = 0;
2398 goto test_exit;
ef416fc2 2399 }
2400 }
2401
2402 /*
2403 * Submit the IPP request...
2404 */
2405
9b66acc5
MS
2406 TestCount ++;
2407
1a18c85c
MS
2408 ippSetVersion(request, version / 10, version % 10);
2409 ippSetOperation(request, op);
2410 ippSetRequestId(request, request_id);
ef416fc2 2411
aaf19ab0 2412 if (Output == _CUPS_OUTPUT_PLIST)
bd7854cb 2413 {
1a18c85c
MS
2414 fputs("<dict>\n", outfile);
2415 fputs("<key>Name</key>\n", outfile);
2416 print_xml_string(outfile, "string", name);
1d47b929
MS
2417 if (file_id[0])
2418 {
1a18c85c
MS
2419 fputs("<key>FileId</key>\n", outfile);
2420 print_xml_string(outfile, "string", file_id);
1d47b929
MS
2421 }
2422 if (test_id[0])
2423 {
1a18c85c
MS
2424 fputs("<key>TestId</key>\n", outfile);
2425 print_xml_string(outfile, "string", test_id);
1d47b929 2426 }
1a18c85c
MS
2427 fputs("<key>Version</key>\n", outfile);
2428 fprintf(outfile, "<string>%d.%d</string>\n", version / 10, version % 10);
2429 fputs("<key>Operation</key>\n", outfile);
2430 print_xml_string(outfile, "string", ippOpString(op));
2431 fputs("<key>RequestId</key>\n", outfile);
2432 fprintf(outfile, "<integer>%d</integer>\n", request_id);
2433 fputs("<key>RequestAttributes</key>\n", outfile);
2434 fputs("<array>\n", outfile);
c8fef167
MS
2435 if (request->attrs)
2436 {
1a18c85c 2437 fputs("<dict>\n", outfile);
db8b865d
MS
2438 for (attrptr = request->attrs,
2439 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
c8fef167
MS
2440 attrptr;
2441 attrptr = attrptr->next)
1a18c85c
MS
2442 print_attr(outfile, Output, attrptr, &group);
2443 fputs("</dict>\n", outfile);
c8fef167 2444 }
1a18c85c 2445 fputs("</array>\n", outfile);
bd7854cb 2446 }
1a18c85c
MS
2447
2448 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
f8b3a85b
MS
2449 {
2450 if (Verbosity)
2451 {
83e08001 2452 printf(" %s:\n", ippOpString(op));
f8b3a85b 2453
83e08001 2454 for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
1a18c85c 2455 print_attr(stdout, _CUPS_OUTPUT_TEST, attrptr, NULL);
f8b3a85b 2456 }
ef416fc2 2457
d7225fc2 2458 printf(" %-68.68s [", name);
f8b3a85b
MS
2459 fflush(stdout);
2460 }
ef416fc2 2461
030ae6a1
MS
2462 if ((skip_previous && !prev_pass) || skip_test)
2463 {
9b66acc5
MS
2464 SkipCount ++;
2465
030ae6a1
MS
2466 ippDelete(request);
2467 request = NULL;
2468
2469 if (Output == _CUPS_OUTPUT_PLIST)
2470 {
1a18c85c
MS
2471 fputs("<key>Successful</key>\n", outfile);
2472 fputs("<true />\n", outfile);
2473 fputs("<key>StatusCode</key>\n", outfile);
2474 print_xml_string(outfile, "string", "skip");
2475 fputs("<key>ResponseAttributes</key>\n", outfile);
2476 fputs("<dict />\n", outfile);
030ae6a1 2477 }
1a18c85c
MS
2478
2479 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
7cf5915e 2480 puts("SKIP]");
030ae6a1
MS
2481
2482 goto skip_error;
2483 }
2484
71f63681 2485 PasswordTries = 0;
a29fd7dd
MS
2486 repeat_count = 0;
2487 repeat_interval = 1;
2488 repeat_prev = 1;
2489
83e08001 2490 do
5a662dc0 2491 {
a29fd7dd
MS
2492 repeat_count ++;
2493
71f63681 2494 status = HTTP_STATUS_OK;
f8b3a85b 2495
83e08001
MS
2496 if (transfer == _CUPS_TRANSFER_CHUNKED ||
2497 (transfer == _CUPS_TRANSFER_AUTO && filename[0]))
5a662dc0 2498 {
83e08001
MS
2499 /*
2500 * Send request using chunking - a 0 length means "chunk".
2501 */
f8b3a85b 2502
83e08001 2503 length = 0;
5a662dc0 2504 }
83e08001 2505 else
85dda01c 2506 {
83e08001
MS
2507 /*
2508 * Send request using content length...
2509 */
85dda01c 2510
83e08001
MS
2511 length = ippLength(request);
2512
9b66acc5 2513 if (filename[0] && (reqfile = cupsFileOpen(filename, "r")) != NULL)
85dda01c 2514 {
9b66acc5
MS
2515 /*
2516 * Read the file to get the uncompressed file size...
2517 */
83e08001 2518
9b66acc5 2519 while ((bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
1a18c85c 2520 length += (size_t)bytes;
85dda01c 2521
9b66acc5 2522 cupsFileClose(reqfile);
85dda01c 2523 }
83e08001 2524 }
85dda01c 2525
83e08001
MS
2526 /*
2527 * Send the request...
2528 */
85dda01c 2529
9b66acc5
MS
2530 response = NULL;
2531 repeat_test = 0;
2532 prev_pass = 1;
85dda01c 2533
71f63681 2534 if (status != HTTP_STATUS_ERROR)
83e08001 2535 {
9b66acc5 2536 while (!response && !Cancel && prev_pass)
85dda01c 2537 {
83e08001
MS
2538 status = cupsSendRequest(http, request, resource, length);
2539
c41769ff
MS
2540#ifdef HAVE_LIBZ
2541 if (compression[0])
2542 httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
2543#endif /* HAVE_LIBZ */
2544
71f63681 2545 if (!Cancel && status == HTTP_STATUS_CONTINUE &&
83e08001
MS
2546 request->state == IPP_DATA && filename[0])
2547 {
9b66acc5 2548 if ((reqfile = cupsFileOpen(filename, "r")) != NULL)
83e08001 2549 {
9b66acc5 2550 while (!Cancel &&
1a18c85c
MS
2551 (bytes = cupsFileRead(reqfile, buffer, sizeof(buffer))) > 0)
2552 if ((status = cupsWriteRequestData(http, buffer, (size_t)bytes)) != HTTP_STATUS_CONTINUE)
83e08001 2553 break;
9b66acc5
MS
2554
2555 cupsFileClose(reqfile);
83e08001
MS
2556 }
2557 else
2558 {
2559 snprintf(buffer, sizeof(buffer), "%s: %s", filename,
2560 strerror(errno));
2561 _cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
2562
71f63681 2563 status = HTTP_STATUS_ERROR;
83e08001
MS
2564 }
2565 }
85dda01c 2566
85dda01c 2567 /*
83e08001 2568 * Get the server's response...
85dda01c
MS
2569 */
2570
71f63681 2571 if (!Cancel && status != HTTP_STATUS_ERROR)
83e08001
MS
2572 {
2573 response = cupsGetResponse(http, resource);
2574 status = httpGetStatus(http);
2575 }
2576
71f63681 2577 if (!Cancel && status == HTTP_STATUS_ERROR && http->error != EINVAL &&
83e08001
MS
2578#ifdef WIN32
2579 http->error != WSAETIMEDOUT)
2580#else
2581 http->error != ETIMEDOUT)
2582#endif /* WIN32 */
2583 {
2584 if (httpReconnect(http))
9b66acc5
MS
2585 prev_pass = 0;
2586 }
71f63681 2587 else if (status == HTTP_STATUS_ERROR || status == HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED)
9b66acc5 2588 {
9b66acc5 2589 prev_pass = 0;
12f89d24
MS
2590 break;
2591 }
71f63681 2592 else if (status != HTTP_STATUS_OK)
12f89d24
MS
2593 {
2594 httpFlush(http);
6961465f
MS
2595
2596 if (status == HTTP_STATUS_UNAUTHORIZED)
2597 continue;
2598
12f89d24 2599 break;
83e08001 2600 }
85dda01c
MS
2601 }
2602 }
85dda01c 2603
71f63681 2604 if (!Cancel && status == HTTP_STATUS_ERROR && http->error != EINVAL &&
12f89d24
MS
2605#ifdef WIN32
2606 http->error != WSAETIMEDOUT)
2607#else
2608 http->error != ETIMEDOUT)
2609#endif /* WIN32 */
2610 {
2611 if (httpReconnect(http))
2612 prev_pass = 0;
2613 }
71f63681 2614 else if (status == HTTP_STATUS_ERROR)
12f89d24
MS
2615 {
2616 if (!Cancel)
2617 httpReconnect(http);
2618
2619 prev_pass = 0;
2620 }
71f63681 2621 else if (status != HTTP_STATUS_OK)
12f89d24
MS
2622 {
2623 httpFlush(http);
2624 prev_pass = 0;
2625 }
2626
83e08001
MS
2627 /*
2628 * Check results of request...
2629 */
ef416fc2 2630
dcb445bc 2631 cupsArrayClear(errors);
5a662dc0 2632
dcb445bc
MS
2633 if (http->version != HTTP_1_1)
2634 add_stringf(errors, "Bad HTTP version (%d.%d)", http->version / 100,
2635 http->version % 100);
7cf5915e 2636
dcb445bc
MS
2637 if (!response)
2638 {
2639 /*
2640 * No response, log error...
2641 */
f8b3a85b 2642
dcb445bc
MS
2643 add_stringf(errors, "IPP request failed with status %s (%s)",
2644 ippErrorString(cupsLastError()),
2645 cupsLastErrorString());
2646 }
2647 else
2648 {
2649 /*
2650 * Collect common attribute values...
2651 */
f8b3a85b 2652
83e08001
MS
2653 if ((attrptr = ippFindAttribute(response, "job-id",
2654 IPP_TAG_INTEGER)) != NULL)
2655 {
2656 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1a18c85c 2657 set_variable(outfile, vars, "job-id", temp);
83e08001 2658 }
ef416fc2 2659
83e08001
MS
2660 if ((attrptr = ippFindAttribute(response, "job-uri",
2661 IPP_TAG_URI)) != NULL)
1a18c85c 2662 set_variable(outfile, vars, "job-uri", attrptr->values[0].string.text);
f8b3a85b 2663
83e08001
MS
2664 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
2665 IPP_TAG_INTEGER)) != NULL)
2666 {
2667 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1a18c85c 2668 set_variable(outfile, vars, "notify-subscription-id", temp);
83e08001 2669 }
f8b3a85b 2670
dcb445bc
MS
2671 /*
2672 * Check response, validating groups and attributes and logging errors
2673 * as needed...
2674 */
f8b3a85b 2675
dcb445bc
MS
2676 if (response->state != IPP_DATA)
2677 add_stringf(errors,
2678 "Missing end-of-attributes-tag in response "
2679 "(RFC 2910 section 3.5.1)");
2680
2681 if (version &&
2682 (response->request.status.version[0] != (version / 10) ||
2683 response->request.status.version[1] != (version % 10)))
2684 add_stringf(errors,
2685 "Bad version %d.%d in response - expected %d.%d "
2686 "(RFC 2911 section 3.1.8).",
2687 response->request.status.version[0],
2688 response->request.status.version[1],
2689 version / 10, version % 10);
2690
2691 if (response->request.status.request_id != request_id)
2692 add_stringf(errors,
2693 "Bad request ID %d in response - expected %d "
2694 "(RFC 2911 section 3.1.1)",
2695 response->request.status.request_id, request_id);
2696
2697 attrptr = response->attrs;
2698 if (!attrptr)
2699 add_stringf(errors,
2700 "Missing first attribute \"attributes-charset "
2701 "(charset)\" in group operation-attributes-tag "
2702 "(RFC 2911 section 3.1.4).");
2703 else
83e08001 2704 {
dcb445bc
MS
2705 if (!attrptr->name ||
2706 attrptr->value_tag != IPP_TAG_CHARSET ||
83e08001
MS
2707 attrptr->group_tag != IPP_TAG_OPERATION ||
2708 attrptr->num_values != 1 ||
dcb445bc
MS
2709 strcmp(attrptr->name, "attributes-charset"))
2710 add_stringf(errors,
2711 "Bad first attribute \"%s (%s%s)\" in group %s, "
2712 "expected \"attributes-charset (charset)\" in "
2713 "group operation-attributes-tag (RFC 2911 section "
2714 "3.1.4).",
2715 attrptr->name ? attrptr->name : "(null)",
2716 attrptr->num_values > 1 ? "1setOf " : "",
2717 ippTagString(attrptr->value_tag),
2718 ippTagString(attrptr->group_tag));
2719
2720 attrptr = attrptr->next;
2721 if (!attrptr)
2722 add_stringf(errors,
2723 "Missing second attribute \"attributes-natural-"
2724 "language (naturalLanguage)\" in group "
2725 "operation-attributes-tag (RFC 2911 section "
2726 "3.1.4).");
2727 else if (!attrptr->name ||
2728 attrptr->value_tag != IPP_TAG_LANGUAGE ||
2729 attrptr->group_tag != IPP_TAG_OPERATION ||
2730 attrptr->num_values != 1 ||
2731 strcmp(attrptr->name, "attributes-natural-language"))
2732 add_stringf(errors,
2733 "Bad first attribute \"%s (%s%s)\" in group %s, "
2734 "expected \"attributes-natural-language "
2735 "(naturalLanguage)\" in group "
2736 "operation-attributes-tag (RFC 2911 section "
2737 "3.1.4).",
2738 attrptr->name ? attrptr->name : "(null)",
2739 attrptr->num_values > 1 ? "1setOf " : "",
2740 ippTagString(attrptr->value_tag),
2741 ippTagString(attrptr->group_tag));
2742 }
f8b3a85b 2743
83e08001 2744 if ((attrptr = ippFindAttribute(response, "status-message",
dcb445bc
MS
2745 IPP_TAG_ZERO)) != NULL)
2746 {
2747 if (attrptr->value_tag != IPP_TAG_TEXT)
2748 add_stringf(errors,
2749 "status-message (text(255)) has wrong value tag "
2750 "%s (RFC 2911 section 3.1.6.2).",
2751 ippTagString(attrptr->value_tag));
2752 if (attrptr->group_tag != IPP_TAG_OPERATION)
2753 add_stringf(errors,
2754 "status-message (text(255)) has wrong group tag "
2755 "%s (RFC 2911 section 3.1.6.2).",
2756 ippTagString(attrptr->group_tag));
2757 if (attrptr->num_values != 1)
2758 add_stringf(errors,
2759 "status-message (text(255)) has %d values "
2760 "(RFC 2911 section 3.1.6.2).",
2761 attrptr->num_values);
2762 if (attrptr->value_tag == IPP_TAG_TEXT &&
2763 strlen(attrptr->values[0].string.text) > 255)
2764 add_stringf(errors,
2765 "status-message (text(255)) has bad length %d"
2766 " (RFC 2911 section 3.1.6.2).",
2767 (int)strlen(attrptr->values[0].string.text));
2768 }
f8b3a85b 2769
83e08001 2770 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
dcb445bc
MS
2771 IPP_TAG_ZERO)) != NULL)
2772 {
2773 if (attrptr->value_tag != IPP_TAG_TEXT)
2774 add_stringf(errors,
2775 "detailed-status-message (text(MAX)) has wrong "
2776 "value tag %s (RFC 2911 section 3.1.6.3).",
2777 ippTagString(attrptr->value_tag));
2778 if (attrptr->group_tag != IPP_TAG_OPERATION)
2779 add_stringf(errors,
2780 "detailed-status-message (text(MAX)) has wrong "
2781 "group tag %s (RFC 2911 section 3.1.6.3).",
2782 ippTagString(attrptr->group_tag));
2783 if (attrptr->num_values != 1)
2784 add_stringf(errors,
2785 "detailed-status-message (text(MAX)) has %d values"
2786 " (RFC 2911 section 3.1.6.3).",
2787 attrptr->num_values);
2788 if (attrptr->value_tag == IPP_TAG_TEXT &&
2789 strlen(attrptr->values[0].string.text) > 1023)
2790 add_stringf(errors,
2791 "detailed-status-message (text(MAX)) has bad "
2792 "length %d (RFC 2911 section 3.1.6.3).",
2793 (int)strlen(attrptr->values[0].string.text));
2794 }
f8b3a85b 2795
d7225fc2
MS
2796 a = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2797
db8b865d
MS
2798 for (attrptr = response->attrs,
2799 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
83e08001
MS
2800 attrptr;
2801 attrptr = attrptr->next)
f8b3a85b 2802 {
d7225fc2 2803 if (attrptr->group_tag != group)
83e08001 2804 {
dcb445bc 2805 int out_of_order = 0; /* Are attribute groups out-of-order? */
d7225fc2 2806 cupsArrayClear(a);
3e7fe0ca 2807
d7225fc2
MS
2808 switch (attrptr->group_tag)
2809 {
a2326b5b
MS
2810 case IPP_TAG_ZERO :
2811 break;
2812
d7225fc2 2813 case IPP_TAG_OPERATION :
dcb445bc 2814 out_of_order = 1;
d7225fc2
MS
2815 break;
2816
2817 case IPP_TAG_UNSUPPORTED_GROUP :
2818 if (group != IPP_TAG_OPERATION)
dcb445bc 2819 out_of_order = 1;
d7225fc2
MS
2820 break;
2821
2822 case IPP_TAG_JOB :
2823 case IPP_TAG_PRINTER :
2824 if (group != IPP_TAG_OPERATION &&
2825 group != IPP_TAG_UNSUPPORTED_GROUP)
dcb445bc 2826 out_of_order = 1;
d7225fc2
MS
2827 break;
2828
2829 case IPP_TAG_SUBSCRIPTION :
2830 if (group > attrptr->group_tag &&
2831 group != IPP_TAG_DOCUMENT)
dcb445bc 2832 out_of_order = 1;
d7225fc2
MS
2833 break;
2834
2835 default :
2836 if (group > attrptr->group_tag)
dcb445bc 2837 out_of_order = 1;
d7225fc2
MS
2838 break;
2839 }
2840
dcb445bc
MS
2841 if (out_of_order)
2842 add_stringf(errors, "Attribute groups out of order (%s < %s)",
2843 ippTagString(attrptr->group_tag),
2844 ippTagString(group));
d7225fc2 2845
a2326b5b
MS
2846 if (attrptr->group_tag != IPP_TAG_ZERO)
2847 group = attrptr->group_tag;
83e08001
MS
2848 }
2849
1a18c85c 2850 validate_attr(outfile, errors, attrptr);
d7225fc2
MS
2851
2852 if (attrptr->name)
2853 {
2854 if (cupsArrayFind(a, attrptr->name))
dcb445bc
MS
2855 add_stringf(errors, "Duplicate \"%s\" attribute in %s group",
2856 attrptr->name, ippTagString(group));
d7225fc2
MS
2857
2858 cupsArrayAdd(a, attrptr->name);
2859 }
f8b3a85b 2860 }
ef416fc2 2861
d7225fc2
MS
2862 cupsArrayDelete(a);
2863
dcb445bc
MS
2864 /*
2865 * Now check the test-defined expected status-code and attribute
2866 * values...
2867 */
2868
83e08001
MS
2869 for (i = 0; i < num_statuses; i ++)
2870 {
2871 if (statuses[i].if_defined &&
2872 !get_variable(vars, statuses[i].if_defined))
2873 continue;
f8b3a85b 2874
83e08001
MS
2875 if (statuses[i].if_not_defined &&
2876 get_variable(vars, statuses[i].if_not_defined))
2877 continue;
f8b3a85b 2878
83e08001
MS
2879 if (response->request.status.status_code == statuses[i].status)
2880 {
a29fd7dd
MS
2881 if (statuses[i].repeat_match &&
2882 repeat_count < statuses[i].repeat_limit)
83e08001 2883 repeat_test = 1;
ef416fc2 2884
0fa6c7fa 2885 if (statuses[i].define_match)
1a18c85c 2886 set_variable(outfile, vars, statuses[i].define_match, "1");
0fa6c7fa
MS
2887
2888 break;
83e08001 2889 }
0fa6c7fa
MS
2890 else
2891 {
2892 if (statuses[i].repeat_no_match &&
2893 repeat_count < statuses[i].repeat_limit)
2894 repeat_test = 1;
2895
2896 if (statuses[i].define_no_match)
2897 {
1a18c85c 2898 set_variable(outfile, vars, statuses[i].define_no_match, "1");
0fa6c7fa
MS
2899 break;
2900 }
2901 }
83e08001 2902 }
f8b3a85b 2903
83e08001 2904 if (i == num_statuses && num_statuses > 0)
dcb445bc
MS
2905 {
2906 for (i = 0; i < num_statuses; i ++)
2907 {
2908 if (statuses[i].if_defined &&
2909 !get_variable(vars, statuses[i].if_defined))
2910 continue;
2911
2912 if (statuses[i].if_not_defined &&
2913 get_variable(vars, statuses[i].if_not_defined))
2914 continue;
2915
37e7e6e0
MS
2916 if (!statuses[i].repeat_match)
2917 add_stringf(errors, "EXPECTED: STATUS %s (got %s)",
2918 ippErrorString(statuses[i].status),
2919 ippErrorString(cupsLastError()));
dcb445bc
MS
2920 }
2921
2922 if ((attrptr = ippFindAttribute(response, "status-message",
2923 IPP_TAG_TEXT)) != NULL)
2924 add_stringf(errors, "status-message=\"%s\"",
2925 attrptr->values[0].string.text);
2926 }
d7225fc2
MS
2927
2928 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
83e08001 2929 {
d7225fc2
MS
2930 if (expect->if_defined && !get_variable(vars, expect->if_defined))
2931 continue;
2932
2933 if (expect->if_not_defined &&
2934 get_variable(vars, expect->if_not_defined))
2935 continue;
2936
2937 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
2938
2939 if ((found && expect->not_expect) ||
2940 (!found && !(expect->not_expect || expect->optional)) ||
2941 (found && !expect_matches(expect, found->value_tag)) ||
2942 (found && expect->in_group &&
2943 found->group_tag != expect->in_group))
83e08001 2944 {
d7225fc2 2945 if (expect->define_no_match)
1a18c85c 2946 set_variable(outfile, vars, expect->define_no_match, "1");
d7225fc2 2947 else if (!expect->define_match && !expect->define_value)
dcb445bc
MS
2948 {
2949 if (found && expect->not_expect)
2950 add_stringf(errors, "NOT EXPECTED: %s", expect->name);
2951 else if (!found && !(expect->not_expect || expect->optional))
2952 add_stringf(errors, "EXPECTED: %s", expect->name);
2953 else if (found)
2954 {
2955 if (!expect_matches(expect, found->value_tag))
2956 add_stringf(errors, "EXPECTED: %s OF-TYPE %s (got %s)",
2957 expect->name, expect->of_type,
2958 ippTagString(found->value_tag));
3e7fe0ca 2959
dcb445bc
MS
2960 if (expect->in_group && found->group_tag != expect->in_group)
2961 add_stringf(errors, "EXPECTED: %s IN-GROUP %s (got %s).",
2962 expect->name, ippTagString(expect->in_group),
2963 ippTagString(found->group_tag));
2964 }
2965 }
5a662dc0 2966
a29fd7dd
MS
2967 if (expect->repeat_no_match &&
2968 repeat_count < expect->repeat_limit)
d7225fc2 2969 repeat_test = 1;
5a662dc0 2970
d7225fc2
MS
2971 continue;
2972 }
030ae6a1 2973
d7225fc2 2974 if (found)
a2326b5b 2975 ippAttributeString(found, buffer, sizeof(buffer));
5a662dc0 2976
d7225fc2 2977 if (found &&
1a18c85c 2978 !with_value(outfile, NULL, expect->with_value, expect->with_flags, found,
d7225fc2
MS
2979 buffer, sizeof(buffer)))
2980 {
2981 if (expect->define_no_match)
1a18c85c 2982 set_variable(outfile, vars, expect->define_no_match, "1");
37e7e6e0
MS
2983 else if (!expect->define_match && !expect->define_value &&
2984 !expect->repeat_match && !expect->repeat_no_match)
dcb445bc 2985 {
82cc1f9a
MS
2986 if (expect->with_flags & _CUPS_WITH_REGEX)
2987 add_stringf(errors, "EXPECTED: %s %s /%s/",
2988 expect->name,
2989 (expect->with_flags & _CUPS_WITH_ALL) ?
2990 "WITH-ALL-VALUES" : "WITH-VALUE",
2991 expect->with_value);
dcb445bc 2992 else
82cc1f9a
MS
2993 add_stringf(errors, "EXPECTED: %s %s \"%s\"",
2994 expect->name,
2995 (expect->with_flags & _CUPS_WITH_ALL) ?
2996 "WITH-ALL-VALUES" : "WITH-VALUE",
2997 expect->with_value);
dcb445bc 2998
1a18c85c 2999 with_value(outfile, errors, expect->with_value, expect->with_flags, found,
dcb445bc
MS
3000 buffer, sizeof(buffer));
3001 }
030ae6a1 3002
a29fd7dd
MS
3003 if (expect->repeat_no_match &&
3004 repeat_count < expect->repeat_limit)
d7225fc2 3005 repeat_test = 1;
f8b3a85b 3006
d7225fc2
MS
3007 continue;
3008 }
9b66acc5 3009
d7225fc2
MS
3010 if (found && expect->count > 0 &&
3011 found->num_values != expect->count)
3012 {
3013 if (expect->define_no_match)
1a18c85c 3014 set_variable(outfile, vars, expect->define_no_match, "1");
d7225fc2 3015 else if (!expect->define_match && !expect->define_value)
dcb445bc
MS
3016 {
3017 add_stringf(errors, "EXPECTED: %s COUNT %d (got %d)", expect->name,
3018 expect->count, found->num_values);
3019 }
030ae6a1 3020
a29fd7dd
MS
3021 if (expect->repeat_no_match &&
3022 repeat_count < expect->repeat_limit)
d7225fc2 3023 repeat_test = 1;
f8b3a85b 3024
d7225fc2
MS
3025 continue;
3026 }
3027
3028 if (found && expect->same_count_as)
3029 {
3030 attrptr = ippFindAttribute(response, expect->same_count_as,
3031 IPP_TAG_ZERO);
5a662dc0 3032
d7225fc2 3033 if (!attrptr || attrptr->num_values != found->num_values)
83e08001 3034 {
030ae6a1 3035 if (expect->define_no_match)
1a18c85c 3036 set_variable(outfile, vars, expect->define_no_match, "1");
9b66acc5 3037 else if (!expect->define_match && !expect->define_value)
dcb445bc
MS
3038 {
3039 if (!attrptr)
3040 add_stringf(errors,
3041 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3042 "(not returned)", expect->name,
3043 found->num_values, expect->same_count_as);
3044 else if (attrptr->num_values != found->num_values)
3045 add_stringf(errors,
3046 "EXPECTED: %s (%d values) SAME-COUNT-AS %s "
3047 "(%d values)", expect->name, found->num_values,
3048 expect->same_count_as, attrptr->num_values);
3049 }
030ae6a1 3050
a29fd7dd
MS
3051 if (expect->repeat_no_match &&
3052 repeat_count < expect->repeat_limit)
d7225fc2 3053 repeat_test = 1;
030ae6a1 3054
83e08001
MS
3055 continue;
3056 }
d7225fc2 3057 }
030ae6a1 3058
d7225fc2 3059 if (found && expect->define_match)
1a18c85c 3060 set_variable(outfile, vars, expect->define_match, "1");
83e08001 3061
d7225fc2 3062 if (found && expect->define_value)
1a18c85c 3063 set_variable(outfile, vars, expect->define_value, buffer);
83e08001 3064
a29fd7dd
MS
3065 if (found && expect->repeat_match &&
3066 repeat_count < expect->repeat_limit)
d7225fc2 3067 repeat_test = 1;
83e08001 3068 }
ef416fc2 3069 }
83e08001
MS
3070
3071 /*
3072 * If we are going to repeat this test, sleep 1 second so we don't flood
3073 * the printer with requests...
3074 */
3075
3076 if (repeat_test)
a29fd7dd 3077 {
1a18c85c 3078 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
a29fd7dd
MS
3079 {
3080 printf("%04d]\n", repeat_count);
3081 fflush(stdout);
3082 }
3083
1a18c85c 3084 sleep((unsigned)repeat_interval);
a29fd7dd
MS
3085 repeat_interval = _cupsNextDelay(repeat_interval, &repeat_prev);
3086
1a18c85c 3087 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
a29fd7dd
MS
3088 {
3089 printf(" %-68.68s [", name);
3090 fflush(stdout);
3091 }
3092 }
f8b3a85b 3093 }
83e08001
MS
3094 while (repeat_test);
3095
3096 ippDelete(request);
3097
9b66acc5
MS
3098 request = NULL;
3099
dcb445bc
MS
3100 if (cupsArrayCount(errors) > 0)
3101 prev_pass = pass = 0;
3102
9b66acc5
MS
3103 if (prev_pass)
3104 PassCount ++;
3105 else
3106 FailCount ++;
f8b3a85b 3107
aaf19ab0 3108 if (Output == _CUPS_OUTPUT_PLIST)
f8b3a85b 3109 {
1a18c85c
MS
3110 fputs("<key>Successful</key>\n", outfile);
3111 fputs(prev_pass ? "<true />\n" : "<false />\n", outfile);
3112 fputs("<key>StatusCode</key>\n", outfile);
3113 print_xml_string(outfile, "string", ippErrorString(cupsLastError()));
3114 fputs("<key>ResponseAttributes</key>\n", outfile);
3115 fputs("<array>\n", outfile);
3116 fputs("<dict>\n", outfile);
e60ec91f
MS
3117 for (attrptr = response ? response->attrs : NULL,
3118 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
f8b3a85b
MS
3119 attrptr;
3120 attrptr = attrptr->next)
1a18c85c
MS
3121 print_attr(outfile, Output, attrptr, &group);
3122 fputs("</dict>\n", outfile);
3123 fputs("</array>\n", outfile);
f8b3a85b 3124 }
1a18c85c
MS
3125
3126 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
f8b3a85b 3127 {
030ae6a1 3128 puts(prev_pass ? "PASS]" : "FAIL]");
ef416fc2 3129
a2326b5b 3130 if (!prev_pass || (Verbosity && response))
ef416fc2 3131 {
ef416fc2 3132 printf(" RECEIVED: %lu bytes in response\n",
3133 (unsigned long)ippLength(response));
a2326b5b
MS
3134 printf(" status-code = %s (%s)\n", ippErrorString(cupsLastError()),
3135 cupsLastErrorString());
ef416fc2 3136
a2326b5b
MS
3137 if (response)
3138 {
3139 for (attrptr = response->attrs;
3140 attrptr != NULL;
3141 attrptr = attrptr->next)
1a18c85c 3142 print_attr(stdout, _CUPS_OUTPUT_TEST, attrptr, NULL);
f8b3a85b
MS
3143 }
3144 }
3145 }
030ae6a1 3146 else if (!prev_pass)
aaf19ab0 3147 fprintf(stderr, "%s\n", cupsLastErrorString());
f8b3a85b 3148
d7225fc2
MS
3149 if (prev_pass && Output >= _CUPS_OUTPUT_LIST && !Verbosity &&
3150 num_displayed > 0)
f8b3a85b 3151 {
d7225fc2
MS
3152 size_t width; /* Length of value */
3153
3154 for (i = 0; i < num_displayed; i ++)
aaf19ab0 3155 {
d7225fc2 3156 widths[i] = strlen(displayed[i]);
aaf19ab0 3157
d7225fc2
MS
3158 for (attrptr = ippFindAttribute(response, displayed[i], IPP_TAG_ZERO);
3159 attrptr;
3160 attrptr = ippFindNextAttribute(response, displayed[i],
3161 IPP_TAG_ZERO))
3162 {
a2326b5b 3163 width = ippAttributeString(attrptr, NULL, 0);
d7225fc2
MS
3164 if (width > widths[i])
3165 widths[i] = width;
3166 }
3167 }
aaf19ab0 3168
d7225fc2 3169 if (Output == _CUPS_OUTPUT_CSV)
1a18c85c 3170 print_csv(outfile, NULL, num_displayed, displayed, widths);
d7225fc2 3171 else
1a18c85c 3172 print_line(outfile, NULL, num_displayed, displayed, widths);
aaf19ab0 3173
d7225fc2 3174 attrptr = response->attrs;
aaf19ab0 3175
d7225fc2
MS
3176 while (attrptr)
3177 {
3178 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
3179 attrptr = attrptr->next;
aaf19ab0 3180
d7225fc2
MS
3181 if (attrptr)
3182 {
3183 if (Output == _CUPS_OUTPUT_CSV)
1a18c85c 3184 print_csv(outfile, attrptr, num_displayed, displayed, widths);
d7225fc2 3185 else
1a18c85c 3186 print_line(outfile, attrptr, num_displayed, displayed, widths);
aaf19ab0 3187
d7225fc2 3188 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
aaf19ab0 3189 attrptr = attrptr->next;
aaf19ab0
MS
3190 }
3191 }
f8b3a85b 3192 }
030ae6a1 3193 else if (!prev_pass)
f8b3a85b 3194 {
aaf19ab0 3195 if (Output == _CUPS_OUTPUT_PLIST)
f8b3a85b 3196 {
1a18c85c
MS
3197 fputs("<key>Errors</key>\n", outfile);
3198 fputs("<array>\n", outfile);
f8b3a85b 3199
dcb445bc
MS
3200 for (error = (char *)cupsArrayFirst(errors);
3201 error;
3202 error = (char *)cupsArrayNext(errors))
1a18c85c 3203 print_xml_string(outfile, "string", error);
f8b3a85b 3204
1a18c85c 3205 fputs("</array>\n", outfile);
dcb445bc 3206 }
1a18c85c
MS
3207
3208 if (Output == _CUPS_OUTPUT_TEST || (Output == _CUPS_OUTPUT_PLIST && outfile != stdout))
ef416fc2 3209 {
dcb445bc
MS
3210 for (error = (char *)cupsArrayFirst(errors);
3211 error;
3212 error = (char *)cupsArrayNext(errors))
3213 printf(" %s\n", error);
ef416fc2 3214 }
f8b3a85b
MS
3215 }
3216
1a18c85c 3217 if (num_displayed > 0 && !Verbosity && response && Output == _CUPS_OUTPUT_TEST)
d7225fc2
MS
3218 {
3219 for (attrptr = response->attrs;
3220 attrptr != NULL;
3221 attrptr = attrptr->next)
3222 {
3223 if (attrptr->name)
3224 {
3225 for (i = 0; i < num_displayed; i ++)
3226 {
3227 if (!strcmp(displayed[i], attrptr->name))
3228 {
1a18c85c 3229 print_attr(outfile, Output, attrptr, NULL);
d7225fc2
MS
3230 break;
3231 }
3232 }
3233 }
3234 }
3235 }
3236
f99f3698
MS
3237 skip_error:
3238
aaf19ab0 3239 if (Output == _CUPS_OUTPUT_PLIST)
1a18c85c 3240 fputs("</dict>\n", outfile);
f8b3a85b 3241
d7225fc2
MS
3242 fflush(stdout);
3243
f8b3a85b
MS
3244 ippDelete(response);
3245 response = NULL;
3246
3247 for (i = 0; i < num_statuses; i ++)
3248 {
3249 if (statuses[i].if_defined)
3250 free(statuses[i].if_defined);
7cf5915e
MS
3251 if (statuses[i].if_not_defined)
3252 free(statuses[i].if_not_defined);
0fa6c7fa
MS
3253 if (statuses[i].define_match)
3254 free(statuses[i].define_match);
3255 if (statuses[i].define_no_match)
3256 free(statuses[i].define_no_match);
ef416fc2 3257 }
f8b3a85b 3258 num_statuses = 0;
ef416fc2 3259
5a662dc0
MS
3260 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
3261 {
3262 free(expect->name);
3263 if (expect->of_type)
3264 free(expect->of_type);
3265 if (expect->same_count_as)
3266 free(expect->same_count_as);
3267 if (expect->if_defined)
3268 free(expect->if_defined);
7cf5915e
MS
3269 if (expect->if_not_defined)
3270 free(expect->if_not_defined);
f8b3a85b
MS
3271 if (expect->with_value)
3272 free(expect->with_value);
030ae6a1
MS
3273 if (expect->define_match)
3274 free(expect->define_match);
3275 if (expect->define_no_match)
3276 free(expect->define_no_match);
3277 if (expect->define_value)
3278 free(expect->define_value);
5a662dc0 3279 }
f8b3a85b
MS
3280 num_expects = 0;
3281
3282 for (i = 0; i < num_displayed; i ++)
3283 free(displayed[i]);
3284 num_displayed = 0;
3285
030ae6a1 3286 if (!ignore_errors && !prev_pass)
ef416fc2 3287 break;
3288 }
3289
030ae6a1 3290 test_exit:
f8b3a85b 3291
dcb445bc
MS
3292 cupsArrayDelete(errors);
3293
f8b3a85b
MS
3294 if (fp)
3295 fclose(fp);
3296
3297 httpClose(http);
3298 ippDelete(request);
3299 ippDelete(response);
3300
3301 for (i = 0; i < num_statuses; i ++)
3302 {
3303 if (statuses[i].if_defined)
3304 free(statuses[i].if_defined);
7cf5915e
MS
3305 if (statuses[i].if_not_defined)
3306 free(statuses[i].if_not_defined);
0cb67df3
MS
3307 if (statuses[i].define_match)
3308 free(statuses[i].define_match);
3309 if (statuses[i].define_no_match)
3310 free(statuses[i].define_no_match);
f8b3a85b
MS
3311 }
3312
3313 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
3314 {
3315 free(expect->name);
3316 if (expect->of_type)
3317 free(expect->of_type);
3318 if (expect->same_count_as)
3319 free(expect->same_count_as);
3320 if (expect->if_defined)
3321 free(expect->if_defined);
7cf5915e
MS
3322 if (expect->if_not_defined)
3323 free(expect->if_not_defined);
f8b3a85b
MS
3324 if (expect->with_value)
3325 free(expect->with_value);
030ae6a1
MS
3326 if (expect->define_match)
3327 free(expect->define_match);
3328 if (expect->define_no_match)
3329 free(expect->define_no_match);
3330 if (expect->define_value)
3331 free(expect->define_value);
f8b3a85b
MS
3332 }
3333
3334 for (i = 0; i < num_displayed; i ++)
3335 free(displayed[i]);
3336
030ae6a1 3337 return (pass);
f8b3a85b
MS
3338}
3339
3340
3341/*
3342 * 'expand_variables()' - Expand variables in a string.
3343 */
3344
3345static void
3346expand_variables(_cups_vars_t *vars, /* I - Variables */
3347 char *dst, /* I - Destination string buffer */
3348 const char *src, /* I - Source string */
3349 size_t dstsize) /* I - Size of destination buffer */
3350{
3351 char *dstptr, /* Pointer into destination */
3352 *dstend, /* End of destination */
3353 temp[256], /* Temporary string */
3354 *tempptr; /* Pointer into temporary string */
3355 const char *value; /* Value to substitute */
3356
3357
3358 dstptr = dst;
3359 dstend = dst + dstsize - 1;
3360
3361 while (*src && dstptr < dstend)
3362 {
3363 if (*src == '$')
3364 {
3365 /*
3366 * Substitute a string/number...
3367 */
3368
3369 if (!strncmp(src, "$$", 2))
3370 {
3371 value = "$";
3372 src += 2;
3373 }
3374 else if (!strncmp(src, "$ENV[", 5))
3375 {
3376 strlcpy(temp, src + 5, sizeof(temp));
3377
3378 for (tempptr = temp; *tempptr; tempptr ++)
3379 if (*tempptr == ']')
3380 break;
3381
3382 if (*tempptr)
3383 *tempptr++ = '\0';
3384
3385 value = getenv(temp);
3386 src += tempptr - temp + 5;
3387 }
3388 else if (vars)
3389 {
3390 strlcpy(temp, src + 1, sizeof(temp));
3391
3392 for (tempptr = temp; *tempptr; tempptr ++)
3393 if (!isalnum(*tempptr & 255) && *tempptr != '-' && *tempptr != '_')
3394 break;
3395
3396 if (*tempptr)
3397 *tempptr = '\0';
3398
3399 if (!strcmp(temp, "uri"))
3400 value = vars->uri;
3401 else if (!strcmp(temp, "filename"))
3402 value = vars->filename;
3403 else if (!strcmp(temp, "scheme") || !strcmp(temp, "method"))
3404 value = vars->scheme;
3405 else if (!strcmp(temp, "username"))
3406 value = vars->userpass;
3407 else if (!strcmp(temp, "hostname"))
3408 value = vars->hostname;
3409 else if (!strcmp(temp, "port"))
3410 {
3411 snprintf(temp, sizeof(temp), "%d", vars->port);
3412 value = temp;
3413 }
3414 else if (!strcmp(temp, "resource"))
3415 value = vars->resource;
3416 else if (!strcmp(temp, "user"))
3417 value = cupsUser();
3418 else
3419 value = get_variable(vars, temp);
3420
3421 src += tempptr - temp + 1;
3422 }
3423 else
3424 {
3425 value = "$";
3426 src ++;
3427 }
3428
3429 if (value)
3430 {
1a18c85c 3431 strlcpy(dstptr, value, (size_t)(dstend - dstptr + 1));
f8b3a85b
MS
3432 dstptr += strlen(dstptr);
3433 }
3434 }
3435 else
3436 *dstptr++ = *src++;
3437 }
3438
3439 *dstptr = '\0';
3440}
3441
3442
3443/*
3444 * 'expect_matches()' - Return true if the tag matches the specification.
3445 */
aaf19ab0 3446
5a662dc0
MS
3447static int /* O - 1 if matches, 0 otherwise */
3448expect_matches(
3449 _cups_expect_t *expect, /* I - Expected attribute */
3450 ipp_tag_t value_tag) /* I - Value tag for attribute */
3451{
3452 int match; /* Match? */
3453 char *of_type, /* Type name to match */
f8b3a85b
MS
3454 *next, /* Next name to match */
3455 sep; /* Separator character */
5a662dc0
MS
3456
3457
3458 /*
3459 * If we don't expect a particular type, return immediately...
3460 */
3461
3462 if (!expect->of_type)
3463 return (1);
3464
3465 /*
3466 * Parse the "of_type" value since the string can contain multiple attribute
f8b3a85b 3467 * types separated by "," or "|"...
5a662dc0
MS
3468 */
3469
5a6b583a 3470 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
5a662dc0
MS
3471 {
3472 /*
3473 * Find the next separator, and set it (temporarily) to nul if present.
3474 */
3475
f8b3a85b
MS
3476 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
3477
3478 if ((sep = *next) != '\0')
5a662dc0 3479 *next = '\0';
aaf19ab0 3480
5a662dc0
MS
3481 /*
3482 * Support some meta-types to make it easier to write the test file.
3483 */
3484
3485 if (!strcmp(of_type, "text"))
aaf19ab0 3486 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
5a662dc0 3487 else if (!strcmp(of_type, "name"))
aaf19ab0 3488 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
5a662dc0 3489 else if (!strcmp(of_type, "collection"))
aaf19ab0 3490 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
5a662dc0
MS
3491 else
3492 match = value_tag == ippTagValue(of_type);
3493
3494 /*
3495 * Restore the separator if we have one...
3496 */
3497
f8b3a85b
MS
3498 if (sep)
3499 *next++ = sep;
5a662dc0
MS
3500 }
3501
3502 return (match);
3503}
3504
3505
f8b3a85b
MS
3506/*
3507 * 'get_collection()' - Get a collection value from the current test file.
3508 */
3509
3510static ipp_t * /* O - Collection value */
1a18c85c
MS
3511get_collection(FILE *outfile, /* I - Output file */
3512 _cups_vars_t *vars, /* I - Variables */
f8b3a85b
MS
3513 FILE *fp, /* I - File to read from */
3514 int *linenum) /* IO - Line number */
3515{
3516 char token[1024], /* Token from file */
3517 temp[1024], /* Temporary string */
3518 attr[128]; /* Attribute name */
3519 ipp_tag_t value; /* Current value type */
3520 ipp_t *col = ippNew(); /* Collection value */
3521 ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
3522
3523
3524 while (get_token(fp, token, sizeof(token), linenum) != NULL)
3525 {
3526 if (!strcmp(token, "}"))
3527 break;
3528 else if (!strcmp(token, "{") && lastcol)
3529 {
3530 /*
3531 * Another collection value
3532 */
3533
1a18c85c 3534 ipp_t *subcol = get_collection(outfile, vars, fp, linenum);
f8b3a85b
MS
3535 /* Collection value */
3536
3537 if (subcol)
e5d9b5e1 3538 ippSetCollection(col, &lastcol, ippGetCount(lastcol), subcol);
f8b3a85b
MS
3539 else
3540 goto col_error;
3541 }
88f9aafc 3542 else if (!_cups_strcasecmp(token, "MEMBER"))
f8b3a85b
MS
3543 {
3544 /*
3545 * Attribute...
3546 */
3547
3548 lastcol = NULL;
3549
3550 if (!get_token(fp, token, sizeof(token), linenum))
3551 {
1a18c85c 3552 print_fatal_error(outfile, "Missing MEMBER value tag on line %d.", *linenum);
f8b3a85b
MS
3553 goto col_error;
3554 }
3555
cc754834 3556 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
f8b3a85b 3557 {
1a18c85c 3558 print_fatal_error(outfile, "Bad MEMBER value tag \"%s\" on line %d.", token,
f8b3a85b
MS
3559 *linenum);
3560 goto col_error;
3561 }
3562
3563 if (!get_token(fp, attr, sizeof(attr), linenum))
3564 {
1a18c85c 3565 print_fatal_error(outfile, "Missing MEMBER name on line %d.", *linenum);
f8b3a85b
MS
3566 goto col_error;
3567 }
3568
3569 if (!get_token(fp, temp, sizeof(temp), linenum))
3570 {
1a18c85c 3571 print_fatal_error(outfile, "Missing MEMBER value on line %d.", *linenum);
f8b3a85b
MS
3572 goto col_error;
3573 }
3574
3575 expand_variables(vars, token, temp, sizeof(token));
3576
3577 switch (value)
3578 {
3579 case IPP_TAG_BOOLEAN :
88f9aafc 3580 if (!_cups_strcasecmp(token, "true"))
f8b3a85b
MS
3581 ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
3582 else
1a18c85c 3583 ippAddBoolean(col, IPP_TAG_ZERO, attr, (char)atoi(token));
f8b3a85b
MS
3584 break;
3585
3586 case IPP_TAG_INTEGER :
3587 case IPP_TAG_ENUM :
3588 ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
3589 break;
3590
3591 case IPP_TAG_RESOLUTION :
3592 {
3593 int xres, /* X resolution */
3594 yres; /* Y resolution */
3595 char units[6]; /* Units */
3596
3597 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
3e7fe0ca
MS
3598 (_cups_strcasecmp(units, "dpi") &&
3599 _cups_strcasecmp(units, "dpc") &&
3600 _cups_strcasecmp(units, "dpcm") &&
88f9aafc 3601 _cups_strcasecmp(units, "other")))
f8b3a85b 3602 {
1a18c85c 3603 print_fatal_error(outfile, "Bad resolution value \"%s\" on line %d.",
f8b3a85b
MS
3604 token, *linenum);
3605 goto col_error;
3606 }
3607
88f9aafc 3608 if (!_cups_strcasecmp(units, "dpi"))
1a18c85c 3609 ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_INCH, xres, yres);
3e7fe0ca
MS
3610 else if (!_cups_strcasecmp(units, "dpc") ||
3611 !_cups_strcasecmp(units, "dpcm"))
1a18c85c 3612 ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_CM, xres, yres);
f8b3a85b 3613 else
1a18c85c 3614 ippAddResolution(col, IPP_TAG_ZERO, attr, (ipp_res_t)0, xres, yres);
f8b3a85b
MS
3615 }
3616 break;
3617
3618 case IPP_TAG_RANGE :
3619 {
3620 int lowers[4], /* Lower value */
3621 uppers[4], /* Upper values */
3622 num_vals; /* Number of values */
3623
3624
3625 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
3626 lowers + 0, uppers + 0,
3627 lowers + 1, uppers + 1,
3628 lowers + 2, uppers + 2,
3629 lowers + 3, uppers + 3);
3630
3631 if ((num_vals & 1) || num_vals == 0)
3632 {
1a18c85c 3633 print_fatal_error(outfile, "Bad rangeOfInteger value \"%s\" on line %d.",
f8b3a85b
MS
3634 token, *linenum);
3635 goto col_error;
3636 }
3637
3638 ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
3639 uppers);
3640 }
3641 break;
3642
3643 case IPP_TAG_BEGIN_COLLECTION :
3644 if (!strcmp(token, "{"))
3645 {
1a18c85c 3646 ipp_t *subcol = get_collection(outfile, vars, fp, linenum);
f8b3a85b
MS
3647 /* Collection value */
3648
3649 if (subcol)
aaf19ab0 3650 {
f8b3a85b 3651 lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
aaf19ab0
MS
3652 ippDelete(subcol);
3653 }
f8b3a85b
MS
3654 else
3655 goto col_error;
3656 }
3657 else
3658 {
1a18c85c 3659 print_fatal_error(outfile, "Bad collection value on line %d.", *linenum);
f8b3a85b
MS
3660 goto col_error;
3661 }
3662 break;
a469f8a5 3663 case IPP_TAG_STRING :
1a18c85c 3664 ippAddOctetString(col, IPP_TAG_ZERO, attr, token, (int)strlen(token));
a469f8a5 3665 break;
f8b3a85b
MS
3666
3667 default :
3668 if (!strchr(token, ','))
3669 ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
3670 else
3671 {
3672 /*
3673 * Multiple string values...
3674 */
3675
3676 int num_values; /* Number of values */
3677 char *values[100], /* Values */
3678 *ptr; /* Pointer to next value */
3679
3680
3681 values[0] = token;
3682 num_values = 1;
3683
3684 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
3685 {
3686 *ptr++ = '\0';
3687 values[num_values] = ptr;
3688 num_values ++;
3689 }
3690
3691 ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
3692 NULL, (const char **)values);
3693 }
3694 break;
3695 }
3696 }
3697 }
3698
3699 return (col);
3700
3701 /*
3702 * If we get here there was a parse error; free memory and return.
3703 */
3704
3705 col_error:
3706
3707 ippDelete(col);
3708
3709 return (NULL);
3710}
3711
3712
3713/*
3714 * 'get_filename()' - Get a filename based on the current test file.
3715 */
3716
3717static char * /* O - Filename */
3718get_filename(const char *testfile, /* I - Current test file */
3719 char *dst, /* I - Destination filename */
3720 const char *src, /* I - Source filename */
3721 size_t dstsize) /* I - Size of destination buffer */
3722{
3723 char *dstptr; /* Pointer into destination */
3724 _cups_globals_t *cg = _cupsGlobals();
3725 /* Global data */
3726
3727
3728 if (*src == '<' && src[strlen(src) - 1] == '>')
3729 {
3730 /*
aaf19ab0 3731 * Map <filename> to CUPS_DATADIR/ipptool/filename...
f8b3a85b
MS
3732 */
3733
aaf19ab0 3734 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
f8b3a85b
MS
3735 dstptr = dst + strlen(dst) - 1;
3736 if (*dstptr == '>')
3737 *dstptr = '\0';
3738 }
12f89d24
MS
3739 else if (*src == '/' || !strchr(testfile, '/')
3740#ifdef WIN32
3741 || (isalpha(*src & 255) && src[1] == ':')
3742#endif /* WIN32 */
3743 )
f8b3a85b
MS
3744 {
3745 /*
3746 * Use the path as-is...
3747 */
3748
3749 strlcpy(dst, src, dstsize);
3750 }
3751 else
3752 {
3753 /*
3754 * Make path relative to testfile...
3755 */
3756
3757 strlcpy(dst, testfile, dstsize);
3758 if ((dstptr = strrchr(dst, '/')) != NULL)
3759 dstptr ++;
3760 else
3761 dstptr = dst; /* Should never happen */
3762
1a18c85c 3763 strlcpy(dstptr, src, dstsize - (size_t)(dstptr - dst));
f8b3a85b
MS
3764 }
3765
3766 return (dst);
3767}
3768
3769
890a10b7
MS
3770/*
3771 * 'get_string()' - Get a pointer to a string value or the portion of interest.
3772 */
3773
3774static char * /* O - Pointer to string */
3775get_string(ipp_attribute_t *attr, /* I - IPP attribute */
3776 int element, /* I - Element to fetch */
3777 int flags, /* I - Value ("with") flags */
3778 char *buffer, /* I - Temporary buffer */
3779 size_t bufsize) /* I - Size of temporary buffer */
3780{
3781 char *ptr, /* Value */
3782 scheme[256], /* URI scheme */
3783 userpass[256], /* Username/password */
3784 hostname[256], /* Hostname */
3785 resource[1024]; /* Resource */
3786 int port; /* Port number */
3787
3788
3789 ptr = attr->values[element].string.text;
3790
3791 if (flags & _CUPS_WITH_HOSTNAME)
3792 {
1a18c85c
MS
3793 if (httpSeparateURI(HTTP_URI_CODING_ALL, ptr, scheme, sizeof(scheme), userpass, sizeof(userpass), buffer, (int)bufsize, &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
3794 buffer[0] = '\0';
3795
3796 return (buffer);
890a10b7
MS
3797 }
3798 else if (flags & _CUPS_WITH_RESOURCE)
3799 {
1a18c85c
MS
3800 if (httpSeparateURI(HTTP_URI_CODING_ALL, ptr, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, buffer, (int)bufsize) < HTTP_URI_STATUS_OK)
3801 buffer[0] = '\0';
3802
3803 return (buffer);
890a10b7
MS
3804 }
3805 else if (flags & _CUPS_WITH_SCHEME)
3806 {
1a18c85c
MS
3807 if (httpSeparateURI(HTTP_URI_CODING_ALL, ptr, buffer, (int)bufsize, userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
3808 buffer[0] = '\0';
3809
3810 return (buffer);
890a10b7
MS
3811 }
3812 else
3813 return (ptr);
3814}
3815
3816
ef416fc2 3817/*
3818 * 'get_token()' - Get a token from a file.
3819 */
3820
5a662dc0 3821static char * /* O - Token from file or NULL on EOF */
ef416fc2 3822get_token(FILE *fp, /* I - File to read from */
3823 char *buf, /* I - Buffer to read into */
3824 int buflen, /* I - Length of buffer */
3825 int *linenum) /* IO - Current line number */
3826{
3827 int ch, /* Character from file */
3828 quote; /* Quoting character */
3829 char *bufptr, /* Pointer into buffer */
3830 *bufend; /* End of buffer */
3831
3832
3833 for (;;)
3834 {
3835 /*
3836 * Skip whitespace...
3837 */
3838
3839 while (isspace(ch = getc(fp)))
3840 {
3841 if (ch == '\n')
3842 (*linenum) ++;
3843 }
3844
3845 /*
3846 * Read a token...
3847 */
3848
3849 if (ch == EOF)
3850 return (NULL);
3851 else if (ch == '\'' || ch == '\"')
3852 {
3853 /*
f8b3a85b 3854 * Quoted text or regular expression...
ef416fc2 3855 */
3856
3857 quote = ch;
3858 bufptr = buf;
3859 bufend = buf + buflen - 1;
3860
3861 while ((ch = getc(fp)) != EOF)
f8b3a85b
MS
3862 {
3863 if (ch == '\\')
3864 {
3865 /*
3866 * Escape next character...
3867 */
3868
3869 if (bufptr < bufend)
1a18c85c 3870 *bufptr++ = (char)ch;
f8b3a85b
MS
3871
3872 if ((ch = getc(fp)) != EOF && bufptr < bufend)
1a18c85c 3873 *bufptr++ = (char)ch;
f8b3a85b
MS
3874 }
3875 else if (ch == quote)
ef416fc2 3876 break;
3877 else if (bufptr < bufend)
1a18c85c 3878 *bufptr++ = (char)ch;
f8b3a85b 3879 }
ef416fc2 3880
3881 *bufptr = '\0';
f8b3a85b 3882
ef416fc2 3883 return (buf);
3884 }
3885 else if (ch == '#')
3886 {
3887 /*
3888 * Comment...
3889 */
3890
3891 while ((ch = getc(fp)) != EOF)
3892 if (ch == '\n')
3893 break;
3894
3895 (*linenum) ++;
3896 }
1a18c85c
MS
3897 else if (ch == '{' || ch == '}' || ch == ',')
3898 {
3899 buf[0] = (char)ch;
3900 buf[1] = '\0';
3901
3902 return (buf);
3903 }
ef416fc2 3904 else
3905 {
3906 /*
3907 * Whitespace delimited text...
3908 */
3909
3910 ungetc(ch, fp);
3911
3912 bufptr = buf;
3913 bufend = buf + buflen - 1;
3914
3915 while ((ch = getc(fp)) != EOF)
3916 if (isspace(ch) || ch == '#')
3917 break;
3918 else if (bufptr < bufend)
1a18c85c 3919 *bufptr++ = (char)ch;
ef416fc2 3920
3921 if (ch == '#')
3922 ungetc(ch, fp);
f8b3a85b
MS
3923 else if (ch == '\n')
3924 (*linenum) ++;
aaf19ab0 3925
ef416fc2 3926 *bufptr = '\0';
f8b3a85b 3927
ef416fc2 3928 return (buf);
3929 }
3930 }
3931}
3932
3933
f8b3a85b
MS
3934/*
3935 * 'get_variable()' - Get the value of a variable.
3936 */
3937
3938static char * /* O - Value or NULL */
3939get_variable(_cups_vars_t *vars, /* I - Variables */
3940 const char *name) /* I - Variable name */
3941{
3942 _cups_var_t key, /* Search key */
3943 *match; /* Matching variable, if any */
3944
3945
3946 key.name = (char *)name;
3947 match = cupsArrayFind(vars->vars, &key);
3948
3949 return (match ? match->value : NULL);
3950}
3951
3952
3953/*
3954 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3955 * value.
3956 */
3957
3958static char * /* O - ISO 8601 date/time string */
3959iso_date(ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
3960{
a4845881
MS
3961 time_t utctime; /* UTC time since 1970 */
3962 struct tm *utcdate; /* UTC date/time */
f8b3a85b
MS
3963 static char buffer[255]; /* String buffer */
3964
3965
a4845881
MS
3966 utctime = ippDateToTime(date);
3967 utcdate = gmtime(&utctime);
3968
3969 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02dZ",
3970 utcdate->tm_year + 1900, utcdate->tm_mon + 1, utcdate->tm_mday,
3971 utcdate->tm_hour, utcdate->tm_min, utcdate->tm_sec);
f8b3a85b
MS
3972
3973 return (buffer);
3974}
3975
3976
7cf5915e
MS
3977/*
3978 * 'password_cb()' - Password callback for authenticated tests.
3979 */
3980
3981static const char * /* O - Password */
3982password_cb(const char *prompt) /* I - Prompt (unused) */
3983{
3984 (void)prompt;
3985
71f63681
MS
3986 if (PasswordTries < 3)
3987 {
3988 PasswordTries ++;
3989
3990 cupsSetUser(Username);
3991
3992 return (Password);
3993 }
3994 else
3995 return (NULL);
7cf5915e
MS
3996}
3997
3998
1a18c85c
MS
3999/*
4000 * 'pause_message()' - Display the message and pause until the user presses a key.
4001 */
4002
4003static void
4004pause_message(const char *message) /* I - Message */
4005{
4006#ifdef WIN32
4007 HANDLE tty; /* Console handle */
4008 DWORD mode; /* Console mode */
4009 char key; /* Key press */
4010 DWORD bytes; /* Bytes read for key press */
4011
4012
4013 /*
4014 * Disable input echo and set raw input...
4015 */
4016
4017 if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
4018 return;
4019
4020 if (!GetConsoleMode(tty, &mode))
4021 return;
4022
4023 if (!SetConsoleMode(tty, 0))
4024 return;
4025
4026#else
4027 int tty; /* /dev/tty - never read from stdin */
4028 struct termios original, /* Original input mode */
4029 noecho; /* No echo input mode */
4030 char key; /* Current key press */
4031
4032
4033 /*
4034 * Disable input echo and set raw input...
4035 */
4036
4037 if ((tty = open("/dev/tty", O_RDONLY)) < 0)
4038 return;
4039
4040 if (tcgetattr(tty, &original))
4041 {
4042 close(tty);
4043 return;
4044 }
4045
4046 noecho = original;
4047 noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
4048
4049 if (tcsetattr(tty, TCSAFLUSH, &noecho))
4050 {
4051 close(tty);
4052 return;
4053 }
4054#endif /* WIN32 */
4055
4056 /*
4057 * Display the prompt...
4058 */
4059
4060 printf("%s\n---- PRESS ANY KEY ----", message);
4061 fflush(stdout);
4062
4063#ifdef WIN32
4064 /*
4065 * Read a key...
4066 */
4067
4068 ReadFile(tty, &key, 1, &bytes, NULL);
4069
4070 /*
4071 * Cleanup...
4072 */
4073
4074 SetConsoleMode(tty, mode);
4075
4076#else
4077 /*
4078 * Read a key...
4079 */
4080
4081 read(tty, &key, 1);
4082
4083 /*
4084 * Cleanup...
4085 */
4086
4087 tcsetattr(tty, TCSAFLUSH, &original);
4088 close(tty);
4089#endif /* WIN32 */
4090
4091 /*
4092 * Erase the "press any key" prompt...
4093 */
4094
4095 fputs("\r \r", stdout);
4096 fflush(stdout);
4097}
4098
4099
ef416fc2 4100/*
4101 * 'print_attr()' - Print an attribute on the screen.
4102 */
4103
5a662dc0 4104static void
1a18c85c
MS
4105print_attr(FILE *outfile, /* I - Output file */
4106 int format, /* I - Output format */
4107 ipp_attribute_t *attr, /* I - Attribute to print */
e60ec91f 4108 ipp_tag_t *group) /* IO - Current group */
ef416fc2 4109{
f8b3a85b
MS
4110 int i; /* Looping var */
4111 ipp_attribute_t *colattr; /* Collection attribute */
ef416fc2 4112
4113
1a18c85c 4114 if (format == _CUPS_OUTPUT_PLIST)
ef416fc2 4115 {
e60ec91f 4116 if (!attr->name || (group && *group != attr->group_tag))
f8b3a85b 4117 {
c1420c87
MS
4118 if (attr->group_tag != IPP_TAG_ZERO)
4119 {
1a18c85c
MS
4120 fputs("</dict>\n", outfile);
4121 fputs("<dict>\n", outfile);
c1420c87 4122 }
e60ec91f
MS
4123
4124 if (group)
4125 *group = attr->group_tag;
f8b3a85b
MS
4126 }
4127
e60ec91f
MS
4128 if (!attr->name)
4129 return;
4130
1a18c85c 4131 print_xml_string(outfile, "key", attr->name);
f8b3a85b 4132 if (attr->num_values > 1)
1a18c85c 4133 fputs("<array>\n", outfile);
ef416fc2 4134
9b66acc5
MS
4135 switch (attr->value_tag)
4136 {
4137 case IPP_TAG_INTEGER :
4138 case IPP_TAG_ENUM :
4139 for (i = 0; i < attr->num_values; i ++)
1a18c85c 4140 fprintf(outfile, "<integer>%d</integer>\n", attr->values[i].integer);
9b66acc5 4141 break;
ef416fc2 4142
9b66acc5
MS
4143 case IPP_TAG_BOOLEAN :
4144 for (i = 0; i < attr->num_values; i ++)
1a18c85c 4145 fputs(attr->values[i].boolean ? "<true />\n" : "<false />\n", outfile);
9b66acc5 4146 break;
ef416fc2 4147
9b66acc5
MS
4148 case IPP_TAG_RANGE :
4149 for (i = 0; i < attr->num_values; i ++)
1a18c85c
MS
4150 fprintf(outfile, "<dict><key>lower</key><integer>%d</integer>"
4151 "<key>upper</key><integer>%d</integer></dict>\n",
4152 attr->values[i].range.lower, attr->values[i].range.upper);
9b66acc5 4153 break;
ef416fc2 4154
9b66acc5
MS
4155 case IPP_TAG_RESOLUTION :
4156 for (i = 0; i < attr->num_values; i ++)
1a18c85c
MS
4157 fprintf(outfile, "<dict><key>xres</key><integer>%d</integer>"
4158 "<key>yres</key><integer>%d</integer>"
4159 "<key>units</key><string>%s</string></dict>\n",
4160 attr->values[i].resolution.xres,
4161 attr->values[i].resolution.yres,
4162 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
4163 "dpi" : "dpcm");
9b66acc5 4164 break;
ef416fc2 4165
9b66acc5
MS
4166 case IPP_TAG_DATE :
4167 for (i = 0; i < attr->num_values; i ++)
1a18c85c 4168 fprintf(outfile, "<date>%s</date>\n", iso_date(attr->values[i].date));
9b66acc5 4169 break;
f8b3a85b 4170
9b66acc5 4171 case IPP_TAG_STRING :
5a9febac
MS
4172 for (i = 0; i < attr->num_values; i ++)
4173 {
1a18c85c 4174 char buffer[IPP_MAX_LENGTH * 5 / 4 + 1];
5a9febac
MS
4175 /* Output buffer */
4176
1a18c85c
MS
4177 fprintf(outfile, "<data>%s</data>\n",
4178 httpEncode64_2(buffer, sizeof(buffer),
4179 attr->values[i].unknown.data,
4180 attr->values[i].unknown.length));
5a9febac
MS
4181 }
4182 break;
4183
9b66acc5
MS
4184 case IPP_TAG_TEXT :
4185 case IPP_TAG_NAME :
4186 case IPP_TAG_KEYWORD :
4187 case IPP_TAG_CHARSET :
4188 case IPP_TAG_URI :
4189 case IPP_TAG_MIMETYPE :
4190 case IPP_TAG_LANGUAGE :
4191 for (i = 0; i < attr->num_values; i ++)
1a18c85c 4192 print_xml_string(outfile, "string", attr->values[i].string.text);
9b66acc5 4193 break;
f8b3a85b 4194
9b66acc5
MS
4195 case IPP_TAG_TEXTLANG :
4196 case IPP_TAG_NAMELANG :
4197 for (i = 0; i < attr->num_values; i ++)
1a18c85c
MS
4198 {
4199 fputs("<dict><key>language</key><string>", outfile);
4200 print_xml_string(outfile, NULL, attr->values[i].string.language);
4201 fputs("</string><key>string</key><string>", outfile);
4202 print_xml_string(outfile, NULL, attr->values[i].string.text);
4203 fputs("</string></dict>\n", outfile);
4204 }
9b66acc5 4205 break;
ef416fc2 4206
9b66acc5
MS
4207 case IPP_TAG_BEGIN_COLLECTION :
4208 for (i = 0; i < attr->num_values; i ++)
f8b3a85b 4209 {
1a18c85c
MS
4210 fputs("<dict>\n", outfile);
4211 for (colattr = attr->values[i].collection->attrs;
4212 colattr;
4213 colattr = colattr->next)
4214 print_attr(outfile, format, colattr, NULL);
4215 fputs("</dict>\n", outfile);
f8b3a85b 4216 }
9b66acc5 4217 break;
ef416fc2 4218
9b66acc5 4219 default :
1a18c85c 4220 fprintf(outfile, "<string>&lt;&lt;%s&gt;&gt;</string>\n", ippTagString(attr->value_tag));
9b66acc5
MS
4221 break;
4222 }
ef416fc2 4223
f8b3a85b 4224 if (attr->num_values > 1)
1a18c85c 4225 fputs("</array>\n", outfile);
f8b3a85b
MS
4226 }
4227 else
9b66acc5
MS
4228 {
4229 char buffer[8192]; /* Value buffer */
4230
1a18c85c 4231 if (format == _CUPS_OUTPUT_TEST)
9b66acc5
MS
4232 {
4233 if (!attr->name)
4234 {
1a18c85c 4235 fputs(" -- separator --\n", outfile);
9b66acc5
MS
4236 return;
4237 }
4238
1a18c85c 4239 fprintf(outfile, " %s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag));
9b66acc5
MS
4240 }
4241
a2326b5b 4242 ippAttributeString(attr, buffer, sizeof(buffer));
1a18c85c 4243 fprintf(outfile, "%s\n", buffer);
9b66acc5 4244 }
ef416fc2 4245}
4246
4247
aaf19ab0
MS
4248/*
4249 * 'print_csv()' - Print a line of CSV text.
4250 */
4251
4252static void
4253print_csv(
1a18c85c 4254 FILE *outfile, /* I - Output file */
aaf19ab0
MS
4255 ipp_attribute_t *attr, /* I - First attribute for line */
4256 int num_displayed, /* I - Number of attributes to display */
4257 char **displayed, /* I - Attributes to display */
4258 size_t *widths) /* I - Column widths */
4259{
4260 int i; /* Looping var */
4261 size_t maxlength; /* Max length of all columns */
4262 char *buffer, /* String buffer */
4263 *bufptr; /* Pointer into buffer */
4264 ipp_attribute_t *current; /* Current attribute */
4265
4266
4267 /*
4268 * Get the maximum string length we have to show and allocate...
4269 */
4270
4271 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
4272 if (widths[i] > maxlength)
4273 maxlength = widths[i];
4274
4275 maxlength += 2;
4276
4277 if ((buffer = malloc(maxlength)) == NULL)
4278 return;
4279
4280 /*
4281 * Loop through the attributes to display...
4282 */
4283
4284 if (attr)
4285 {
4286 for (i = 0; i < num_displayed; i ++)
4287 {
4288 if (i)
1a18c85c 4289 fputc(',', outfile);
aaf19ab0
MS
4290
4291 buffer[0] = '\0';
4292
4293 for (current = attr; current; current = current->next)
4294 {
4295 if (!current->name)
4296 break;
4297 else if (!strcmp(current->name, displayed[i]))
4298 {
a2326b5b 4299 ippAttributeString(current, buffer, maxlength);
aaf19ab0
MS
4300 break;
4301 }
4302 }
4303
4304 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
4305 strchr(buffer, '\\') != NULL)
4306 {
1a18c85c 4307 putc('\"', outfile);
aaf19ab0
MS
4308 for (bufptr = buffer; *bufptr; bufptr ++)
4309 {
4310 if (*bufptr == '\\' || *bufptr == '\"')
1a18c85c
MS
4311 putc('\\', outfile);
4312 putc(*bufptr, outfile);
aaf19ab0 4313 }
1a18c85c 4314 putc('\"', outfile);
aaf19ab0
MS
4315 }
4316 else
1a18c85c 4317 fputs(buffer, outfile);
aaf19ab0 4318 }
1a18c85c 4319 putc('\n', outfile);
aaf19ab0
MS
4320 }
4321 else
4322 {
4323 for (i = 0; i < num_displayed; i ++)
4324 {
4325 if (i)
1a18c85c 4326 putc(',', outfile);
aaf19ab0 4327
1a18c85c 4328 fputs(displayed[i], outfile);
aaf19ab0 4329 }
1a18c85c 4330 putc('\n', outfile);
aaf19ab0
MS
4331 }
4332
4333 free(buffer);
4334}
4335
4336
f8b3a85b
MS
4337/*
4338 * 'print_fatal_error()' - Print a fatal error message.
4339 */
4340
4341static void
1a18c85c
MS
4342print_fatal_error(FILE *outfile, /* I - Output file */
4343 const char *s, /* I - Printf-style format string */
f8b3a85b
MS
4344 ...) /* I - Additional arguments as needed */
4345{
4346 char buffer[10240]; /* Format buffer */
4347 va_list ap; /* Pointer to arguments */
4348
4349
4350 /*
4351 * Format the error message...
4352 */
4353
4354 va_start(ap, s);
4355 vsnprintf(buffer, sizeof(buffer), s, ap);
4356 va_end(ap);
4357
4358 /*
4359 * Then output it...
4360 */
4361
aaf19ab0 4362 if (Output == _CUPS_OUTPUT_PLIST)
f8b3a85b 4363 {
1a18c85c
MS
4364 print_xml_header(outfile);
4365 print_xml_trailer(outfile, 0, buffer);
f8b3a85b 4366 }
1a18c85c
MS
4367
4368 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
aaf19ab0
MS
4369}
4370
4371
4372/*
4373 * 'print_line()' - Print a line of formatted or CSV text.
4374 */
4375
4376static void
4377print_line(
1a18c85c 4378 FILE *outfile, /* I - Output file */
aaf19ab0
MS
4379 ipp_attribute_t *attr, /* I - First attribute for line */
4380 int num_displayed, /* I - Number of attributes to display */
4381 char **displayed, /* I - Attributes to display */
4382 size_t *widths) /* I - Column widths */
4383{
4384 int i; /* Looping var */
4385 size_t maxlength; /* Max length of all columns */
4386 char *buffer; /* String buffer */
4387 ipp_attribute_t *current; /* Current attribute */
4388
4389
4390 /*
4391 * Get the maximum string length we have to show and allocate...
4392 */
4393
4394 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
4395 if (widths[i] > maxlength)
4396 maxlength = widths[i];
4397
4398 maxlength += 2;
4399
4400 if ((buffer = malloc(maxlength)) == NULL)
4401 return;
4402
4403 /*
4404 * Loop through the attributes to display...
4405 */
4406
4407 if (attr)
4408 {
4409 for (i = 0; i < num_displayed; i ++)
4410 {
4411 if (i)
1a18c85c 4412 putc(' ', outfile);
aaf19ab0
MS
4413
4414 buffer[0] = '\0';
4415
4416 for (current = attr; current; current = current->next)
4417 {
4418 if (!current->name)
4419 break;
4420 else if (!strcmp(current->name, displayed[i]))
4421 {
a2326b5b 4422 ippAttributeString(current, buffer, maxlength);
aaf19ab0
MS
4423 break;
4424 }
4425 }
4426
1a18c85c 4427 fprintf(outfile, "%*s", (int)-widths[i], buffer);
aaf19ab0 4428 }
1a18c85c 4429 putc('\n', outfile);
aaf19ab0
MS
4430 }
4431 else
4432 {
4433 for (i = 0; i < num_displayed; i ++)
4434 {
4435 if (i)
1a18c85c 4436 putc(' ', outfile);
aaf19ab0 4437
1a18c85c 4438 fprintf(outfile, "%*s", (int)-widths[i], displayed[i]);
aaf19ab0 4439 }
1a18c85c 4440 putc('\n', outfile);
aaf19ab0
MS
4441
4442 for (i = 0; i < num_displayed; i ++)
4443 {
4444 if (i)
1a18c85c 4445 putc(' ', outfile);
aaf19ab0
MS
4446
4447 memset(buffer, '-', widths[i]);
4448 buffer[widths[i]] = '\0';
1a18c85c 4449 fputs(buffer, outfile);
aaf19ab0 4450 }
1a18c85c 4451 putc('\n', outfile);
aaf19ab0
MS
4452 }
4453
4454 free(buffer);
f8b3a85b
MS
4455}
4456
4457
f8b3a85b
MS
4458/*
4459 * 'print_xml_header()' - Print a standard XML plist header.
4460 */
4461
4462static void
1a18c85c 4463print_xml_header(FILE *outfile) /* I - Output file */
f8b3a85b
MS
4464{
4465 if (!XMLHeader)
4466 {
1a18c85c
MS
4467 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outfile);
4468 fputs("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
4469 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n", outfile);
4470 fputs("<plist version=\"1.0\">\n", outfile);
4471 fputs("<dict>\n", outfile);
4472 fputs("<key>ipptoolVersion</key>\n", outfile);
4473 fputs("<string>" CUPS_SVERSION "</string>\n", outfile);
4474 fputs("<key>Transfer</key>\n", outfile);
4475 fprintf(outfile, "<string>%s</string>\n",
4476 Transfer == _CUPS_TRANSFER_AUTO ? "auto" :
4477 Transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
4478 fputs("<key>Tests</key>\n", outfile);
4479 fputs("<array>\n", outfile);
f8b3a85b
MS
4480
4481 XMLHeader = 1;
4482 }
4483}
4484
4485
4486/*
4487 * 'print_xml_string()' - Print an XML string with escaping.
4488 */
4489
4490static void
1a18c85c
MS
4491print_xml_string(FILE *outfile, /* I - Output file */
4492 const char *element, /* I - Element name or NULL */
f8b3a85b
MS
4493 const char *s) /* I - String to print */
4494{
4495 if (element)
1a18c85c 4496 fprintf(outfile, "<%s>", element);
f8b3a85b
MS
4497
4498 while (*s)
4499 {
4500 if (*s == '&')
1a18c85c 4501 fputs("&amp;", outfile);
f8b3a85b 4502 else if (*s == '<')
1a18c85c 4503 fputs("&lt;", outfile);
f8b3a85b 4504 else if (*s == '>')
1a18c85c 4505 fputs("&gt;", outfile);
a4845881
MS
4506 else if ((*s & 0xe0) == 0xc0)
4507 {
4508 /*
4509 * Validate UTF-8 two-byte sequence...
4510 */
4511
4512 if ((s[1] & 0xc0) != 0x80)
4513 {
1a18c85c 4514 putc('?', outfile);
a4845881
MS
4515 s ++;
4516 }
4517 else
4518 {
1a18c85c
MS
4519 putc(*s++, outfile);
4520 putc(*s, outfile);
a4845881
MS
4521 }
4522 }
4523 else if ((*s & 0xf0) == 0xe0)
4524 {
4525 /*
4526 * Validate UTF-8 three-byte sequence...
4527 */
4528
4529 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80)
4530 {
1a18c85c 4531 putc('?', outfile);
a4845881
MS
4532 s += 2;
4533 }
4534 else
4535 {
1a18c85c
MS
4536 putc(*s++, outfile);
4537 putc(*s++, outfile);
4538 putc(*s, outfile);
a4845881
MS
4539 }
4540 }
4541 else if ((*s & 0xf8) == 0xf0)
4542 {
4543 /*
4544 * Validate UTF-8 four-byte sequence...
4545 */
4546
4547 if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
4548 (s[3] & 0xc0) != 0x80)
4549 {
1a18c85c 4550 putc('?', outfile);
a4845881
MS
4551 s += 3;
4552 }
4553 else
4554 {
1a18c85c
MS
4555 putc(*s++, outfile);
4556 putc(*s++, outfile);
4557 putc(*s++, outfile);
4558 putc(*s, outfile);
a4845881
MS
4559 }
4560 }
4561 else if ((*s & 0x80) || (*s < ' ' && !isspace(*s & 255)))
4562 {
4563 /*
4564 * Invalid control character...
4565 */
4566
1a18c85c 4567 putc('?', outfile);
a4845881 4568 }
f8b3a85b 4569 else
1a18c85c 4570 putc(*s, outfile);
f8b3a85b
MS
4571
4572 s ++;
4573 }
4574
4575 if (element)
1a18c85c 4576 fprintf(outfile, "</%s>\n", element);
f8b3a85b
MS
4577}
4578
4579
4580/*
4581 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
4582 */
4583
4584static void
1a18c85c
MS
4585print_xml_trailer(FILE *outfile, /* I - Output file */
4586 int success, /* I - 1 on success, 0 on failure */
f8b3a85b
MS
4587 const char *message) /* I - Error message or NULL */
4588{
4589 if (XMLHeader)
4590 {
1a18c85c
MS
4591 fputs("</array>\n", outfile);
4592 fputs("<key>Successful</key>\n", outfile);
4593 fputs(success ? "<true />\n" : "<false />\n", outfile);
f8b3a85b
MS
4594 if (message)
4595 {
1a18c85c
MS
4596 fputs("<key>ErrorMessage</key>\n", outfile);
4597 print_xml_string(outfile, "string", message);
f8b3a85b 4598 }
1a18c85c
MS
4599 fputs("</dict>\n", outfile);
4600 fputs("</plist>\n", outfile);
f8b3a85b
MS
4601
4602 XMLHeader = 0;
4603 }
4604}
4605
4606
4607/*
4608 * 'set_variable()' - Set a variable value.
4609 */
4610
4611static void
1a18c85c
MS
4612set_variable(FILE *outfile, /* I - Output file */
4613 _cups_vars_t *vars, /* I - Variables */
f8b3a85b
MS
4614 const char *name, /* I - Variable name */
4615 const char *value) /* I - Value string */
4616{
4617 _cups_var_t key, /* Search key */
4618 *var; /* New variable */
4619
4620
83e08001
MS
4621 if (!_cups_strcasecmp(name, "filename"))
4622 {
4623 if (vars->filename)
4624 free(vars->filename);
4625
4626 vars->filename = strdup(value);
4627 return;
4628 }
4629
f8b3a85b
MS
4630 key.name = (char *)name;
4631 if ((var = cupsArrayFind(vars->vars, &key)) != NULL)
4632 {
4633 free(var->value);
4634 var->value = strdup(value);
4635 }
4636 else if ((var = malloc(sizeof(_cups_var_t))) == NULL)
4637 {
1a18c85c 4638 print_fatal_error(outfile, "Unable to allocate memory for variable \"%s\".", name);
f8b3a85b
MS
4639 exit(1);
4640 }
4641 else
4642 {
4643 var->name = strdup(name);
4644 var->value = strdup(value);
4645
4646 cupsArrayAdd(vars->vars, var);
4647 }
4648}
4649
4650
83e08001 4651#ifndef WIN32
85dda01c
MS
4652/*
4653 * 'sigterm_handler()' - Handle SIGINT and SIGTERM.
4654 */
4655
4656static void
4657sigterm_handler(int sig) /* I - Signal number (unused) */
4658{
4659 (void)sig;
4660
4661 Cancel = 1;
9b66acc5
MS
4662
4663 signal(SIGINT, SIG_DFL);
4664 signal(SIGTERM, SIG_DFL);
85dda01c 4665}
83e08001 4666#endif /* !WIN32 */
85dda01c
MS
4667
4668
10d09e33
MS
4669/*
4670 * 'timeout_cb()' - Handle HTTP timeouts.
4671 */
4672
4673static int /* O - 1 to continue, 0 to cancel */
26340b35 4674timeout_cb(http_t *http, /* I - Connection to server */
10d09e33
MS
4675 void *user_data) /* I - User data (unused) */
4676{
26340b35
MS
4677 int buffered = 0; /* Bytes buffered but not yet sent */
4678
4679
1a18c85c
MS
4680 (void)user_data;
4681
4682 /*
26340b35
MS
4683 * If the socket still have data waiting to be sent to the printer (as can
4684 * happen if the printer runs out of paper), continue to wait until the output
4685 * buffer is empty...
4686 */
4687
4688#ifdef SO_NWRITE /* OS X and some versions of Linux */
4689 socklen_t len = sizeof(buffered); /* Size of return value */
4690
4691 if (getsockopt(httpGetFd(http), SOL_SOCKET, SO_NWRITE, &buffered, &len))
4692 buffered = 0;
4693
4694#elif defined(SIOCOUTQ) /* Others except Windows */
4695 if (ioctl(httpGetFd(http), SIOCOUTQ, &buffered))
4696 buffered = 0;
4697
4698#else /* Windows (not possible) */
10d09e33 4699 (void)http;
26340b35 4700#endif /* SO_NWRITE */
10d09e33 4701
26340b35 4702 return (buffered > 0);
10d09e33
MS
4703}
4704
4705
ef416fc2 4706/*
e00b005a 4707 * 'usage()' - Show program usage.
4708 */
4709
5a662dc0
MS
4710static void
4711usage(void)
e00b005a 4712{
0837b7e8
MS
4713 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... "
4714 "filenameN ]"));
4715 _cupsLangPuts(stderr, _("Options:"));
84315f46
MS
4716 _cupsLangPuts(stderr, _(" -4 Connect using IPv4."));
4717 _cupsLangPuts(stderr, _(" -6 Connect using IPv6."));
4718 _cupsLangPuts(stderr, _(" -C Send requests using "
4719 "chunking (default)."));
f3c17241
MS
4720 _cupsLangPuts(stdout, _(" -E Test with HTTP Upgrade to "
4721 "TLS."));
84315f46
MS
4722 _cupsLangPuts(stderr, _(" -I Ignore errors."));
4723 _cupsLangPuts(stderr, _(" -L Send requests using "
0837b7e8 4724 "content-length."));
84315f46
MS
4725 _cupsLangPuts(stderr, _(" -S Test with SSL "
4726 "encryption."));
12f89d24 4727 _cupsLangPuts(stderr, _(" -T seconds Set the receive/send "
84315f46
MS
4728 "timeout in seconds."));
4729 _cupsLangPuts(stderr, _(" -V version Set default IPP "
4730 "version."));
4731 _cupsLangPuts(stderr, _(" -X Produce XML plist instead "
4732 "of plain text."));
4733 _cupsLangPuts(stderr, _(" -d name=value Set named variable to "
4734 "value."));
4735 _cupsLangPuts(stderr, _(" -f filename Set default request "
4736 "filename."));
4737 _cupsLangPuts(stderr, _(" -i seconds Repeat the last file with "
4738 "the given time interval."));
4739 _cupsLangPuts(stderr, _(" -n count Repeat the last file the "
4740 "given number of times."));
f3c17241 4741 _cupsLangPuts(stderr, _(" -q Run silently."));
84315f46 4742 _cupsLangPuts(stderr, _(" -t Produce a test report."));
f3c17241 4743 _cupsLangPuts(stderr, _(" -v Be verbose."));
e00b005a 4744
4745 exit(1);
4746}
4747
4748
4749/*
f8b3a85b
MS
4750 * 'validate_attr()' - Determine whether an attribute is valid.
4751 */
4752
4753static int /* O - 1 if valid, 0 otherwise */
1a18c85c
MS
4754validate_attr(FILE *outfile, /* I - Output file */
4755 cups_array_t *errors, /* I - Errors array */
dcb445bc 4756 ipp_attribute_t *attr) /* I - Attribute to validate */
f8b3a85b
MS
4757{
4758 int i; /* Looping var */
4759 char scheme[64], /* Scheme from URI */
4760 userpass[256], /* Username/password from URI */
4761 hostname[256], /* Hostname from URI */
4762 resource[1024]; /* Resource from URI */
4763 int port, /* Port number from URI */
4764 uri_status, /* URI separation status */
4765 valid = 1; /* Is the attribute valid? */
4766 const char *ptr; /* Pointer into string */
4767 ipp_attribute_t *colattr; /* Collection attribute */
4768 regex_t re; /* Regular expression */
4769 ipp_uchar_t *date; /* Current date value */
4770
4771
4772 /*
4773 * Skip separators.
4774 */
4775
4776 if (!attr->name)
4777 return (1);
4778
4779 /*
4780 * Validate the attribute name.
4781 */
4782
4783 for (ptr = attr->name; *ptr; ptr ++)
4784 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
4785 break;
4786
4787 if (*ptr || ptr == attr->name)
4788 {
4789 valid = 0;
4790
dcb445bc
MS
4791 add_stringf(errors,
4792 "\"%s\": Bad attribute name - invalid character "
4793 "(RFC 2911 section 4.1.3).", attr->name);
f8b3a85b
MS
4794 }
4795
4796 if ((ptr - attr->name) > 255)
4797 {
4798 valid = 0;
4799
dcb445bc
MS
4800 add_stringf(errors,
4801 "\"%s\": Bad attribute name - bad length "
4802 "(RFC 2911 section 4.1.3).", attr->name);
f8b3a85b
MS
4803 }
4804
4805 switch (attr->value_tag)
4806 {
4807 case IPP_TAG_INTEGER :
4808 break;
4809
4810 case IPP_TAG_BOOLEAN :
4811 for (i = 0; i < attr->num_values; i ++)
4812 {
4813 if (attr->values[i].boolean != 0 &&
4814 attr->values[i].boolean != 1)
4815 {
4816 valid = 0;
4817
dcb445bc
MS
4818 add_stringf(errors,
4819 "\"%s\": Bad boolen value %d "
c1420c87 4820 "(RFC 2911 section 4.1.11).", attr->name,
dcb445bc 4821 attr->values[i].boolean);
f8b3a85b
MS
4822 }
4823 }
4824 break;
4825
4826 case IPP_TAG_ENUM :
4827 for (i = 0; i < attr->num_values; i ++)
4828 {
4829 if (attr->values[i].integer < 1)
4830 {
4831 valid = 0;
4832
dcb445bc
MS
4833 add_stringf(errors,
4834 "\"%s\": Bad enum value %d - out of range "
4835 "(RFC 2911 section 4.1.4).", attr->name,
4836 attr->values[i].integer);
f8b3a85b
MS
4837 }
4838 }
4839 break;
4840
4841 case IPP_TAG_STRING :
4842 for (i = 0; i < attr->num_values; i ++)
4843 {
a469f8a5 4844 if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
f8b3a85b
MS
4845 {
4846 valid = 0;
4847
dcb445bc
MS
4848 add_stringf(errors,
4849 "\"%s\": Bad octetString value - bad length %d "
4850 "(RFC 2911 section 4.1.10).", attr->name,
4851 attr->values[i].unknown.length);
f8b3a85b
MS
4852 }
4853 }
4854 break;
4855
4856 case IPP_TAG_DATE :
4857 for (i = 0; i < attr->num_values; i ++)
4858 {
4859 date = attr->values[i].date;
4860
4861 if (date[2] < 1 || date[2] > 12)
4862 {
4863 valid = 0;
4864
dcb445bc
MS
4865 add_stringf(errors,
4866 "\"%s\": Bad dateTime month %u "
c1420c87 4867 "(RFC 2911 section 4.1.14).", attr->name, date[2]);
f8b3a85b
MS
4868 }
4869
4870 if (date[3] < 1 || date[3] > 31)
4871 {
4872 valid = 0;
4873
dcb445bc
MS
4874 add_stringf(errors,
4875 "\"%s\": Bad dateTime day %u "
c1420c87 4876 "(RFC 2911 section 4.1.14).", attr->name, date[3]);
f8b3a85b
MS
4877 }
4878
4879 if (date[4] > 23)
4880 {
4881 valid = 0;
4882
dcb445bc
MS
4883 add_stringf(errors,
4884 "\"%s\": Bad dateTime hours %u "
c1420c87 4885 "(RFC 2911 section 4.1.14).", attr->name, date[4]);
f8b3a85b
MS
4886 }
4887
4888 if (date[5] > 59)
4889 {
4890 valid = 0;
4891
dcb445bc
MS
4892 add_stringf(errors,
4893 "\"%s\": Bad dateTime minutes %u "
c1420c87 4894 "(RFC 2911 section 4.1.14).", attr->name, date[5]);
f8b3a85b
MS
4895 }
4896
4897 if (date[6] > 60)
4898 {
4899 valid = 0;
4900
dcb445bc
MS
4901 add_stringf(errors,
4902 "\"%s\": Bad dateTime seconds %u "
c1420c87 4903 "(RFC 2911 section 4.1.14).", attr->name, date[6]);
f8b3a85b
MS
4904 }
4905
4906 if (date[7] > 9)
4907 {
4908 valid = 0;
4909
dcb445bc
MS
4910 add_stringf(errors,
4911 "\"%s\": Bad dateTime deciseconds %u "
c1420c87 4912 "(RFC 2911 section 4.1.14).", attr->name, date[7]);
f8b3a85b
MS
4913 }
4914
4915 if (date[8] != '-' && date[8] != '+')
4916 {
4917 valid = 0;
4918
dcb445bc
MS
4919 add_stringf(errors,
4920 "\"%s\": Bad dateTime UTC sign '%c' "
c1420c87 4921 "(RFC 2911 section 4.1.14).", attr->name, date[8]);
f8b3a85b
MS
4922 }
4923
4924 if (date[9] > 11)
4925 {
4926 valid = 0;
4927
dcb445bc
MS
4928 add_stringf(errors,
4929 "\"%s\": Bad dateTime UTC hours %u "
c1420c87 4930 "(RFC 2911 section 4.1.14).", attr->name, date[9]);
f8b3a85b
MS
4931 }
4932
4933 if (date[10] > 59)
4934 {
4935 valid = 0;
4936
dcb445bc
MS
4937 add_stringf(errors,
4938 "\"%s\": Bad dateTime UTC minutes %u "
c1420c87 4939 "(RFC 2911 section 4.1.14).", attr->name, date[10]);
f8b3a85b
MS
4940 }
4941 }
4942 break;
4943
4944 case IPP_TAG_RESOLUTION :
4945 for (i = 0; i < attr->num_values; i ++)
4946 {
4947 if (attr->values[i].resolution.xres <= 0)
4948 {
4949 valid = 0;
4950
dcb445bc
MS
4951 add_stringf(errors,
4952 "\"%s\": Bad resolution value %dx%d%s - cross "
4953 "feed resolution must be positive "
c1420c87 4954 "(RFC 2911 section 4.1.15).", attr->name,
dcb445bc
MS
4955 attr->values[i].resolution.xres,
4956 attr->values[i].resolution.yres,
4957 attr->values[i].resolution.units ==
4958 IPP_RES_PER_INCH ? "dpi" :
4959 attr->values[i].resolution.units ==
3e7fe0ca 4960 IPP_RES_PER_CM ? "dpcm" : "unknown");
f8b3a85b
MS
4961 }
4962
4963 if (attr->values[i].resolution.yres <= 0)
4964 {
4965 valid = 0;
4966
dcb445bc
MS
4967 add_stringf(errors,
4968 "\"%s\": Bad resolution value %dx%d%s - feed "
4969 "resolution must be positive "
c1420c87 4970 "(RFC 2911 section 4.1.15).", attr->name,
dcb445bc
MS
4971 attr->values[i].resolution.xres,
4972 attr->values[i].resolution.yres,
4973 attr->values[i].resolution.units ==
4974 IPP_RES_PER_INCH ? "dpi" :
4975 attr->values[i].resolution.units ==
3e7fe0ca 4976 IPP_RES_PER_CM ? "dpcm" : "unknown");
f8b3a85b
MS
4977 }
4978
4979 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
4980 attr->values[i].resolution.units != IPP_RES_PER_CM)
4981 {
4982 valid = 0;
4983
dcb445bc
MS
4984 add_stringf(errors,
4985 "\"%s\": Bad resolution value %dx%d%s - bad "
c1420c87 4986 "units value (RFC 2911 section 4.1.15).",
dcb445bc
MS
4987 attr->name, attr->values[i].resolution.xres,
4988 attr->values[i].resolution.yres,
4989 attr->values[i].resolution.units ==
4990 IPP_RES_PER_INCH ? "dpi" :
4991 attr->values[i].resolution.units ==
3e7fe0ca 4992 IPP_RES_PER_CM ? "dpcm" : "unknown");
f8b3a85b
MS
4993 }
4994 }
4995 break;
4996
4997 case IPP_TAG_RANGE :
4998 for (i = 0; i < attr->num_values; i ++)
4999 {
5000 if (attr->values[i].range.lower > attr->values[i].range.upper)
5001 {
5002 valid = 0;
5003
dcb445bc
MS
5004 add_stringf(errors,
5005 "\"%s\": Bad rangeOfInteger value %d-%d - lower "
5006 "greater than upper (RFC 2911 section 4.1.13).",
5007 attr->name, attr->values[i].range.lower,
5008 attr->values[i].range.upper);
f8b3a85b
MS
5009 }
5010 }
5011 break;
5012
5013 case IPP_TAG_BEGIN_COLLECTION :
5014 for (i = 0; i < attr->num_values; i ++)
5015 {
5016 for (colattr = attr->values[i].collection->attrs;
5017 colattr;
5018 colattr = colattr->next)
5019 {
1a18c85c 5020 if (!validate_attr(outfile, NULL, colattr))
f8b3a85b
MS
5021 {
5022 valid = 0;
5023 break;
5024 }
5025 }
5026
dcb445bc 5027 if (colattr && errors)
f8b3a85b 5028 {
dcb445bc 5029 add_stringf(errors, "\"%s\": Bad collection value.", attr->name);
f8b3a85b
MS
5030
5031 while (colattr)
5032 {
1a18c85c 5033 validate_attr(outfile, errors, colattr);
f8b3a85b
MS
5034 colattr = colattr->next;
5035 }
5036 }
5037 }
5038 break;
5039
5040 case IPP_TAG_TEXT :
5041 case IPP_TAG_TEXTLANG :
5042 for (i = 0; i < attr->num_values; i ++)
5043 {
5044 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5045 {
5046 if ((*ptr & 0xe0) == 0xc0)
5047 {
5048 ptr ++;
5049 if ((*ptr & 0xc0) != 0x80)
5050 break;
5051 }
5052 else if ((*ptr & 0xf0) == 0xe0)
5053 {
5054 ptr ++;
5055 if ((*ptr & 0xc0) != 0x80)
5056 break;
5057 ptr ++;
5058 if ((*ptr & 0xc0) != 0x80)
5059 break;
5060 }
5061 else if ((*ptr & 0xf8) == 0xf0)
5062 {
5063 ptr ++;
5064 if ((*ptr & 0xc0) != 0x80)
5065 break;
5066 ptr ++;
5067 if ((*ptr & 0xc0) != 0x80)
5068 break;
5069 ptr ++;
5070 if ((*ptr & 0xc0) != 0x80)
5071 break;
5072 }
5073 else if (*ptr & 0x80)
5074 break;
5075 }
5076
5077 if (*ptr)
5078 {
5079 valid = 0;
5080
dcb445bc
MS
5081 add_stringf(errors,
5082 "\"%s\": Bad text value \"%s\" - bad UTF-8 "
5083 "sequence (RFC 2911 section 4.1.1).", attr->name,
5084 attr->values[i].string.text);
f8b3a85b
MS
5085 }
5086
a469f8a5 5087 if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
f8b3a85b
MS
5088 {
5089 valid = 0;
5090
dcb445bc
MS
5091 add_stringf(errors,
5092 "\"%s\": Bad text value \"%s\" - bad length %d "
5093 "(RFC 2911 section 4.1.1).", attr->name,
5094 attr->values[i].string.text,
5095 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5096 }
5097 }
5098 break;
5099
5100 case IPP_TAG_NAME :
5101 case IPP_TAG_NAMELANG :
5102 for (i = 0; i < attr->num_values; i ++)
5103 {
5104 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5105 {
5106 if ((*ptr & 0xe0) == 0xc0)
5107 {
5108 ptr ++;
5109 if ((*ptr & 0xc0) != 0x80)
5110 break;
5111 }
5112 else if ((*ptr & 0xf0) == 0xe0)
5113 {
5114 ptr ++;
5115 if ((*ptr & 0xc0) != 0x80)
5116 break;
5117 ptr ++;
5118 if ((*ptr & 0xc0) != 0x80)
5119 break;
5120 }
5121 else if ((*ptr & 0xf8) == 0xf0)
5122 {
5123 ptr ++;
5124 if ((*ptr & 0xc0) != 0x80)
5125 break;
5126 ptr ++;
5127 if ((*ptr & 0xc0) != 0x80)
5128 break;
5129 ptr ++;
5130 if ((*ptr & 0xc0) != 0x80)
5131 break;
5132 }
5133 else if (*ptr & 0x80)
5134 break;
5135 }
5136
5137 if (*ptr)
5138 {
5139 valid = 0;
5140
dcb445bc
MS
5141 add_stringf(errors,
5142 "\"%s\": Bad name value \"%s\" - bad UTF-8 "
5143 "sequence (RFC 2911 section 4.1.2).", attr->name,
5144 attr->values[i].string.text);
f8b3a85b
MS
5145 }
5146
a469f8a5 5147 if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
f8b3a85b
MS
5148 {
5149 valid = 0;
5150
dcb445bc
MS
5151 add_stringf(errors,
5152 "\"%s\": Bad name value \"%s\" - bad length %d "
5153 "(RFC 2911 section 4.1.2).", attr->name,
5154 attr->values[i].string.text,
5155 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5156 }
5157 }
5158 break;
5159
5160 case IPP_TAG_KEYWORD :
5161 for (i = 0; i < attr->num_values; i ++)
5162 {
5163 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5164 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
5165 *ptr != '_')
5166 break;
5167
5168 if (*ptr || ptr == attr->values[i].string.text)
5169 {
5170 valid = 0;
5171
dcb445bc
MS
5172 add_stringf(errors,
5173 "\"%s\": Bad keyword value \"%s\" - invalid "
5174 "character (RFC 2911 section 4.1.3).",
5175 attr->name, attr->values[i].string.text);
f8b3a85b
MS
5176 }
5177
a469f8a5 5178 if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
f8b3a85b
MS
5179 {
5180 valid = 0;
5181
dcb445bc
MS
5182 add_stringf(errors,
5183 "\"%s\": Bad keyword value \"%s\" - bad "
5184 "length %d (RFC 2911 section 4.1.3).",
5185 attr->name, attr->values[i].string.text,
5186 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5187 }
5188 }
5189 break;
5190
5191 case IPP_TAG_URI :
5192 for (i = 0; i < attr->num_values; i ++)
5193 {
5194 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
5195 attr->values[i].string.text,
5196 scheme, sizeof(scheme),
5197 userpass, sizeof(userpass),
5198 hostname, sizeof(hostname),
5199 &port, resource, sizeof(resource));
5200
5201 if (uri_status < HTTP_URI_OK)
5202 {
5203 valid = 0;
5204
dcb445bc
MS
5205 add_stringf(errors,
5206 "\"%s\": Bad URI value \"%s\" - %s "
5207 "(RFC 2911 section 4.1.5).", attr->name,
5208 attr->values[i].string.text,
1a18c85c 5209 httpURIStatusString(uri_status));
f8b3a85b
MS
5210 }
5211
a469f8a5 5212 if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
f8b3a85b
MS
5213 {
5214 valid = 0;
5215
dcb445bc
MS
5216 add_stringf(errors,
5217 "\"%s\": Bad URI value \"%s\" - bad length %d "
5218 "(RFC 2911 section 4.1.5).", attr->name,
5219 attr->values[i].string.text,
5220 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5221 }
5222 }
5223 break;
5224
5225 case IPP_TAG_URISCHEME :
5226 for (i = 0; i < attr->num_values; i ++)
5227 {
5228 ptr = attr->values[i].string.text;
5229 if (islower(*ptr & 255))
5230 {
5231 for (ptr ++; *ptr; ptr ++)
5232 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
5233 *ptr != '+' && *ptr != '-' && *ptr != '.')
5234 break;
5235 }
5236
5237 if (*ptr || ptr == attr->values[i].string.text)
5238 {
5239 valid = 0;
5240
dcb445bc
MS
5241 add_stringf(errors,
5242 "\"%s\": Bad uriScheme value \"%s\" - bad "
5243 "characters (RFC 2911 section 4.1.6).",
5244 attr->name, attr->values[i].string.text);
f8b3a85b
MS
5245 }
5246
a469f8a5 5247 if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
f8b3a85b
MS
5248 {
5249 valid = 0;
5250
dcb445bc
MS
5251 add_stringf(errors,
5252 "\"%s\": Bad uriScheme value \"%s\" - bad "
5253 "length %d (RFC 2911 section 4.1.6).",
5254 attr->name, attr->values[i].string.text,
5255 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5256 }
5257 }
5258 break;
5259
5260 case IPP_TAG_CHARSET :
5261 for (i = 0; i < attr->num_values; i ++)
5262 {
5263 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5264 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
5265 isspace(*ptr & 255))
5266 break;
5267
5268 if (*ptr || ptr == attr->values[i].string.text)
5269 {
5270 valid = 0;
5271
dcb445bc
MS
5272 add_stringf(errors,
5273 "\"%s\": Bad charset value \"%s\" - bad "
5274 "characters (RFC 2911 section 4.1.7).",
5275 attr->name, attr->values[i].string.text);
f8b3a85b
MS
5276 }
5277
a469f8a5 5278 if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
f8b3a85b
MS
5279 {
5280 valid = 0;
5281
dcb445bc
MS
5282 add_stringf(errors,
5283 "\"%s\": Bad charset value \"%s\" - bad "
5284 "length %d (RFC 2911 section 4.1.7).",
5285 attr->name, attr->values[i].string.text,
5286 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5287 }
5288 }
5289 break;
5290
5291 case IPP_TAG_LANGUAGE :
5292 /*
5293 * The following regular expression is derived from the ABNF for
5294 * language tags in RFC 4646. All I can say is that this is the
5295 * easiest way to check the values...
5296 */
5297
5298 if ((i = regcomp(&re,
5299 "^("
5300 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5301 /* language */
5302 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5303 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5304 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5305 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5306 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5307 "|"
5308 "x(-[a-z0-9]{1,8})+" /* privateuse */
5309 "|"
5310 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5311 ")$",
5312 REG_NOSUB | REG_EXTENDED)) != 0)
5313 {
5314 char temp[256]; /* Temporary error string */
5315
5316 regerror(i, &re, temp, sizeof(temp));
1a18c85c 5317 print_fatal_error(outfile, "Unable to compile naturalLanguage regular "
f8b3a85b 5318 "expression: %s.", temp);
dcb445bc 5319 break;
f8b3a85b
MS
5320 }
5321
5322 for (i = 0; i < attr->num_values; i ++)
5323 {
5324 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5325 {
5326 valid = 0;
5327
dcb445bc
MS
5328 add_stringf(errors,
5329 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5330 "characters (RFC 2911 section 4.1.8).",
5331 attr->name, attr->values[i].string.text);
f8b3a85b
MS
5332 }
5333
a469f8a5 5334 if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
f8b3a85b
MS
5335 {
5336 valid = 0;
5337
dcb445bc
MS
5338 add_stringf(errors,
5339 "\"%s\": Bad naturalLanguage value \"%s\" - bad "
5340 "length %d (RFC 2911 section 4.1.8).",
5341 attr->name, attr->values[i].string.text,
5342 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5343 }
5344 }
5345
5346 regfree(&re);
5347 break;
5348
5349 case IPP_TAG_MIMETYPE :
5350 /*
5351 * The following regular expression is derived from the ABNF for
5352 * language tags in RFC 2045 and 4288. All I can say is that this is
5353 * the easiest way to check the values...
5354 */
5355
5356 if ((i = regcomp(&re,
5357 "^"
5358 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5359 "/"
5360 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5361 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5362 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5363 /* value */
5364 "$",
5365 REG_NOSUB | REG_EXTENDED)) != 0)
5366 {
5367 char temp[256]; /* Temporary error string */
5368
5369 regerror(i, &re, temp, sizeof(temp));
1a18c85c 5370 print_fatal_error(outfile, "Unable to compile mimeMediaType regular "
f8b3a85b 5371 "expression: %s.", temp);
dcb445bc 5372 break;
f8b3a85b
MS
5373 }
5374
5375 for (i = 0; i < attr->num_values; i ++)
5376 {
5377 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5378 {
5379 valid = 0;
5380
dcb445bc
MS
5381 add_stringf(errors,
5382 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5383 "characters (RFC 2911 section 4.1.9).",
5384 attr->name, attr->values[i].string.text);
f8b3a85b
MS
5385 }
5386
a469f8a5 5387 if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
f8b3a85b
MS
5388 {
5389 valid = 0;
5390
dcb445bc
MS
5391 add_stringf(errors,
5392 "\"%s\": Bad mimeMediaType value \"%s\" - bad "
5393 "length %d (RFC 2911 section 4.1.9).",
5394 attr->name, attr->values[i].string.text,
5395 (int)strlen(attr->values[i].string.text));
f8b3a85b
MS
5396 }
5397 }
dcb445bc
MS
5398
5399 regfree(&re);
f8b3a85b
MS
5400 break;
5401
5402 default :
5403 break;
5404 }
5405
5406 return (valid);
5407}
5408
5409
5410/*
5411 * 'with_value()' - Test a WITH-VALUE predicate.
5412 */
5413
5414static int /* O - 1 on match, 0 on non-match */
1a18c85c
MS
5415with_value(FILE *outfile, /* I - Output file */
5416 cups_array_t *errors, /* I - Errors array */
dcb445bc 5417 char *value, /* I - Value string */
82cc1f9a 5418 int flags, /* I - Flags for match */
7cf5915e 5419 ipp_attribute_t *attr, /* I - Attribute to compare */
9b66acc5
MS
5420 char *matchbuf, /* I - Buffer to hold matching value */
5421 size_t matchlen) /* I - Length of match buffer */
f8b3a85b 5422{
82cc1f9a
MS
5423 int i, /* Looping var */
5424 match; /* Match? */
890a10b7 5425 char temp[1024], /* Temporary value string */
db8b865d 5426 *valptr; /* Pointer into value */
f8b3a85b
MS
5427
5428
9b66acc5 5429 *matchbuf = '\0';
82cc1f9a 5430 match = (flags & _CUPS_WITH_ALL) ? 1 : 0;
9b66acc5 5431
f8b3a85b
MS
5432 /*
5433 * NULL matches everything.
5434 */
5435
5436 if (!value || !*value)
5437 return (1);
5438
5439 /*
5440 * Compare the value string to the attribute value.
5441 */
5442
5443 switch (attr->value_tag)
5444 {
5445 case IPP_TAG_INTEGER :
5446 case IPP_TAG_ENUM :
5447 for (i = 0; i < attr->num_values; i ++)
5448 {
5449 char op, /* Comparison operator */
5450 *nextptr; /* Next pointer */
82cc1f9a
MS
5451 int intvalue, /* Integer value */
5452 valmatch = 0; /* Does the current value match? */
f8b3a85b
MS
5453
5454 valptr = value;
f8b3a85b
MS
5455
5456 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5457 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5458 *valptr == '=' || *valptr == '>')
5459 {
5460 op = '=';
5461 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5462 {
5463 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5464 op = *valptr;
5465 valptr ++;
5466 }
5467
5468 if (!*valptr)
5469 break;
5470
1a18c85c 5471 intvalue = (int)strtol(valptr, &nextptr, 0);
f8b3a85b
MS
5472 if (nextptr == valptr)
5473 break;
5474 valptr = nextptr;
5475
82cc1f9a
MS
5476 if ((op == '=' && attr->values[i].integer == intvalue) ||
5477 (op == '<' && attr->values[i].integer < intvalue) ||
5478 (op == '>' && attr->values[i].integer > intvalue))
f8b3a85b 5479 {
82cc1f9a 5480 if (!matchbuf[0])
1a18c85c 5481 snprintf(matchbuf, matchlen, "%d", attr->values[i].integer);
82cc1f9a
MS
5482
5483 valmatch = 1;
5484 break;
f8b3a85b
MS
5485 }
5486 }
82cc1f9a
MS
5487
5488 if (flags & _CUPS_WITH_ALL)
5489 {
5490 if (!valmatch)
5491 {
5492 match = 0;
5493 break;
5494 }
5495 }
5496 else if (valmatch)
5497 {
5498 match = 1;
5499 break;
5500 }
f8b3a85b 5501 }
7cf5915e 5502
82cc1f9a 5503 if (!match && errors)
7cf5915e
MS
5504 {
5505 for (i = 0; i < attr->num_values; i ++)
dcb445bc
MS
5506 add_stringf(errors, "GOT: %s=%d", attr->name,
5507 attr->values[i].integer);
7cf5915e 5508 }
f8b3a85b
MS
5509 break;
5510
10d09e33
MS
5511 case IPP_TAG_RANGE :
5512 for (i = 0; i < attr->num_values; i ++)
5513 {
5514 char op, /* Comparison operator */
5515 *nextptr; /* Next pointer */
82cc1f9a
MS
5516 int intvalue, /* Integer value */
5517 valmatch = 0; /* Does the current value match? */
10d09e33
MS
5518
5519 valptr = value;
10d09e33
MS
5520
5521 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
5522 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
5523 *valptr == '=' || *valptr == '>')
5524 {
5525 op = '=';
5526 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
5527 {
5528 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
5529 op = *valptr;
5530 valptr ++;
5531 }
5532
5533 if (!*valptr)
5534 break;
5535
1a18c85c 5536 intvalue = (int)strtol(valptr, &nextptr, 0);
10d09e33
MS
5537 if (nextptr == valptr)
5538 break;
5539 valptr = nextptr;
5540
82cc1f9a
MS
5541 if ((op == '=' && (attr->values[i].range.lower == intvalue ||
5542 attr->values[i].range.upper == intvalue)) ||
5543 (op == '<' && attr->values[i].range.upper < intvalue) ||
5544 (op == '>' && attr->values[i].range.upper > intvalue))
10d09e33 5545 {
82cc1f9a
MS
5546 if (!matchbuf[0])
5547 snprintf(matchbuf, matchlen, "%d-%d",
5548 attr->values[0].range.lower,
5549 attr->values[0].range.upper);
5550
5551 valmatch = 1;
5552 break;
10d09e33
MS
5553 }
5554 }
82cc1f9a
MS
5555
5556 if (flags & _CUPS_WITH_ALL)
5557 {
5558 if (!valmatch)
5559 {
5560 match = 0;
5561 break;
5562 }
5563 }
5564 else if (valmatch)
5565 {
5566 match = 1;
5567 break;
5568 }
10d09e33
MS
5569 }
5570
82cc1f9a 5571 if (!match && errors)
10d09e33
MS
5572 {
5573 for (i = 0; i < attr->num_values; i ++)
dcb445bc 5574 add_stringf(errors, "GOT: %s=%d-%d", attr->name,
10d09e33
MS
5575 attr->values[i].range.lower,
5576 attr->values[i].range.upper);
5577 }
5578 break;
5579
f8b3a85b
MS
5580 case IPP_TAG_BOOLEAN :
5581 for (i = 0; i < attr->num_values; i ++)
5582 {
5583 if (!strcmp(value, "true") == attr->values[i].boolean)
9b66acc5 5584 {
82cc1f9a
MS
5585 if (!matchbuf[0])
5586 strlcpy(matchbuf, value, matchlen);
5587
5588 if (!(flags & _CUPS_WITH_ALL))
5589 {
5590 match = 1;
5591 break;
5592 }
5593 }
5594 else if (flags & _CUPS_WITH_ALL)
5595 {
5596 match = 0;
5597 break;
9b66acc5 5598 }
f8b3a85b 5599 }
7cf5915e 5600
82cc1f9a 5601 if (!match && errors)
7cf5915e
MS
5602 {
5603 for (i = 0; i < attr->num_values; i ++)
dcb445bc 5604 add_stringf(errors, "GOT: %s=%s", attr->name,
7cf5915e
MS
5605 attr->values[i].boolean ? "true" : "false");
5606 }
f8b3a85b
MS
5607 break;
5608
db8b865d
MS
5609 case IPP_TAG_RESOLUTION :
5610 for (i = 0; i < attr->num_values; i ++)
5611 {
5612 if (attr->values[i].resolution.xres ==
5613 attr->values[i].resolution.yres)
5614 snprintf(temp, sizeof(temp), "%d%s",
5615 attr->values[i].resolution.xres,
5616 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5617 "dpi" : "dpcm");
5618 else
5619 snprintf(temp, sizeof(temp), "%dx%d%s",
5620 attr->values[i].resolution.xres,
5621 attr->values[i].resolution.yres,
5622 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5623 "dpi" : "dpcm");
5624
5625 if (!strcmp(value, temp))
5626 {
5627 if (!matchbuf[0])
5628 strlcpy(matchbuf, value, matchlen);
5629
5630 if (!(flags & _CUPS_WITH_ALL))
5631 {
5632 match = 1;
5633 break;
5634 }
5635 }
5636 else if (flags & _CUPS_WITH_ALL)
5637 {
5638 match = 0;
5639 break;
5640 }
5641 }
5642
5643 if (!match && errors)
5644 {
5645 for (i = 0; i < attr->num_values; i ++)
5646 {
5647 if (attr->values[i].resolution.xres ==
5648 attr->values[i].resolution.yres)
5649 snprintf(temp, sizeof(temp), "%d%s",
5650 attr->values[i].resolution.xres,
5651 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5652 "dpi" : "dpcm");
5653 else
5654 snprintf(temp, sizeof(temp), "%dx%d%s",
5655 attr->values[i].resolution.xres,
5656 attr->values[i].resolution.yres,
5657 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
5658 "dpi" : "dpcm");
5659
5660 if (strcmp(value, temp))
5661 add_stringf(errors, "GOT: %s=%s", attr->name, temp);
5662 }
5663 }
5664 break;
5665
f8b3a85b 5666 case IPP_TAG_NOVALUE :
d7225fc2
MS
5667 case IPP_TAG_UNKNOWN :
5668 return (1);
f8b3a85b
MS
5669
5670 case IPP_TAG_CHARSET :
5671 case IPP_TAG_KEYWORD :
5672 case IPP_TAG_LANGUAGE :
5673 case IPP_TAG_MIMETYPE :
5674 case IPP_TAG_NAME :
5675 case IPP_TAG_NAMELANG :
5676 case IPP_TAG_TEXT :
5677 case IPP_TAG_TEXTLANG :
5678 case IPP_TAG_URI :
5679 case IPP_TAG_URISCHEME :
82cc1f9a 5680 if (flags & _CUPS_WITH_REGEX)
f8b3a85b
MS
5681 {
5682 /*
5683 * Value is an extended, case-sensitive POSIX regular expression...
5684 */
5685
5686 regex_t re; /* Regular expression */
5687
5688 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
5689 {
f8b3a85b
MS
5690 regerror(i, &re, temp, sizeof(temp));
5691
1a18c85c 5692 print_fatal_error(outfile, "Unable to compile WITH-VALUE regular expression "
f8b3a85b
MS
5693 "\"%s\" - %s", value, temp);
5694 return (0);
5695 }
5696
5697 /*
5698 * See if ALL of the values match the given regular expression.
5699 */
5700
5701 for (i = 0; i < attr->num_values; i ++)
5702 {
890a10b7
MS
5703 if (!regexec(&re, get_string(attr, i, flags, temp, sizeof(temp)),
5704 0, NULL, 0))
7cf5915e 5705 {
82cc1f9a 5706 if (!matchbuf[0])
890a10b7
MS
5707 strlcpy(matchbuf,
5708 get_string(attr, i, flags, temp, sizeof(temp)),
5709 matchlen);
82cc1f9a
MS
5710
5711 if (!(flags & _CUPS_WITH_ALL))
5712 {
5713 match = 1;
7cf5915e 5714 break;
82cc1f9a
MS
5715 }
5716 }
5717 else if (flags & _CUPS_WITH_ALL)
5718 {
5719 match = 0;
5720 break;
7cf5915e 5721 }
f8b3a85b
MS
5722 }
5723
5724 regfree(&re);
f8b3a85b
MS
5725 }
5726 else
5727 {
5728 /*
82cc1f9a 5729 * Value is a literal string, see if the value(s) match...
f8b3a85b
MS
5730 */
5731
5732 for (i = 0; i < attr->num_values; i ++)
5733 {
890a10b7 5734 if (!strcmp(value, get_string(attr, i, flags, temp, sizeof(temp))))
9b66acc5 5735 {
82cc1f9a 5736 if (!matchbuf[0])
890a10b7
MS
5737 strlcpy(matchbuf,
5738 get_string(attr, i, flags, temp, sizeof(temp)),
5739 matchlen);
7cf5915e 5740
82cc1f9a
MS
5741 if (!(flags & _CUPS_WITH_ALL))
5742 {
5743 match = 1;
5744 break;
5745 }
5746 }
5747 else if (flags & _CUPS_WITH_ALL)
5748 {
5749 match = 0;
5750 break;
5751 }
7cf5915e 5752 }
f8b3a85b 5753 }
82cc1f9a
MS
5754
5755 if (!match && errors)
5756 {
5757 for (i = 0; i < attr->num_values; i ++)
5758 add_stringf(errors, "GOT: %s=\"%s\"", attr->name,
890a10b7 5759 attr->values[i].string.text);
82cc1f9a 5760 }
f8b3a85b
MS
5761 break;
5762
5763 default :
5764 break;
5765 }
5766
82cc1f9a 5767 return (match);
f8b3a85b
MS
5768}
5769
5770
5771/*
1a18c85c 5772 * End of "$Id: ipptool.c 12031 2014-07-15 19:57:59Z msweet $".
ef416fc2 5773 */