]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptool.c
Merge changes from CUPS 1.5svn-r9525
[thirdparty/cups.git] / test / ipptool.c
1 /*
2 * "$Id$"
3 *
4 * ipptool command for CUPS.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
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/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * main() - Parse options and do tests.
20 * compare_vars() - Compare two variables.
21 * do_tests() - Do tests as specified in the test file.
22 * expand_variables() - Expand variables in a string.
23 * expect_matches() - Return true if the tag matches the specification.
24 * get_collection() - Get a collection value from the current test file.
25 * get_filename() - Get a filename based on the current test file.
26 * get_token() - Get a token from a file.
27 * get_variable() - Get the value of a variable.
28 * iso_date() - Return an ISO 8601 date/time string for the given IPP
29 * dateTime value.
30 * password_cb() - Password callback for authenticated tests.
31 * print_attr() - Print an attribute on the screen.
32 * print_col() - Print a collection attribute on the screen.
33 * print_csv() - Print a line of CSV text.
34 * print_fatal_error() - Print a fatal error message.
35 * print_line() - Print a line of formatted or CSV text.
36 * print_test_error() - Print a test error message.
37 * print_xml_header() - Print a standard XML plist header.
38 * print_xml_string() - Print an XML string with escaping.
39 * print_xml_trailer() - Print the XML trailer with success/fail value.
40 * set_variable() - Set a variable value.
41 * timeout_cb() - Handle HTTP timeouts.
42 * usage() - Show program usage.
43 * validate_attr() - Determine whether an attribute is valid.
44 * with_value() - Test a WITH-VALUE predicate.
45 */
46
47 /*
48 * Include necessary headers...
49 */
50
51 #include <cups/cups-private.h>
52 #include <cups/file-private.h>
53 #include <regex.h>
54
55 #ifndef O_BINARY
56 # define O_BINARY 0
57 #endif /* !O_BINARY */
58
59
60 /*
61 * Types...
62 */
63
64 typedef enum _cups_transfer_e /**** How to send request data ****/
65 {
66 _CUPS_TRANSFER_AUTO, /* Chunk for files, length for static */
67 _CUPS_TRANSFER_CHUNKED, /* Chunk always */
68 _CUPS_TRANSFER_LENGTH /* Length always */
69 } _cups_transfer_t;
70
71 typedef enum _cups_output_e /**** Output mode ****/
72 {
73 _CUPS_OUTPUT_QUIET, /* No output */
74 _CUPS_OUTPUT_TEST, /* Traditional CUPS test output */
75 _CUPS_OUTPUT_PLIST, /* XML plist test output */
76 _CUPS_OUTPUT_LIST, /* Tabular list output */
77 _CUPS_OUTPUT_CSV /* Comma-separated values output */
78 } _cups_output_t;
79
80 typedef struct _cups_expect_s /**** Expected attribute info ****/
81 {
82 int optional, /* Optional attribute? */
83 not_expect; /* Don't expect attribute? */
84 char *name, /* Attribute name */
85 *of_type, /* Type name */
86 *same_count_as, /* Parallel attribute name */
87 *if_defined, /* Only required if variable defined */
88 *if_not_defined, /* Only required if variable is not defined */
89 *with_value, /* Attribute must include this value */
90 *define_match, /* Variable to define on match */
91 *define_no_match, /* Variable to define on no-match */
92 *define_value; /* Variable to define with value */
93 int with_regex, /* WITH-VALUE is a regular expression */
94 count; /* Expected count if > 0 */
95 ipp_tag_t in_group; /* IN-GROUP value */
96 } _cups_expect_t;
97
98 typedef struct _cups_status_s /**** Status info ****/
99 {
100 ipp_status_t status; /* Expected status code */
101 char *if_defined, /* Only if variable is defined */
102 *if_not_defined; /* Only if variable is not defined */
103 } _cups_status_t;
104
105 typedef struct _cups_var_s /**** Variable ****/
106 {
107 char *name, /* Name of variable */
108 *value; /* Value of variable */
109 } _cups_var_t;
110
111 typedef struct _cups_vars_s /**** Set of variables ****/
112 {
113 const char *uri, /* URI for printer */
114 *filename; /* Filename */
115 char scheme[64], /* Scheme from URI */
116 userpass[256], /* Username/password from URI */
117 hostname[256], /* Hostname from URI */
118 resource[1024]; /* Resource path from URI */
119 int port; /* Port number from URI */
120 http_encryption_t encryption; /* Encryption for connection? */
121 double timeout; /* Timeout for connection */
122 int family; /* Address family */
123 cups_array_t *vars; /* Array of variables */
124 } _cups_vars_t;
125
126
127 /*
128 * Globals...
129 */
130
131 _cups_transfer_t Transfer = _CUPS_TRANSFER_AUTO;
132 /* How to transfer requests */
133 _cups_output_t Output = _CUPS_OUTPUT_LIST;
134 /* Output mode */
135 int IgnoreErrors = 0, /* Ignore errors? */
136 Verbosity = 0, /* Show all attributes? */
137 Version = 11, /* Default IPP version */
138 XMLHeader = 0; /* 1 if header is written */
139 char *Password = NULL; /* Password from URI */
140 const char * const URIStatusStrings[] = /* URI status strings */
141 {
142 "URI too large",
143 "Bad arguments to function",
144 "Bad resource in URI",
145 "Bad port number in URI",
146 "Bad hostname/address in URI",
147 "Bad username in URI",
148 "Bad scheme in URI",
149 "Bad/empty URI",
150 "OK",
151 "Missing scheme in URI",
152 "Unknown scheme in URI",
153 "Missing resource in URI"
154 };
155
156
157 /*
158 * Local functions...
159 */
160
161 static int compare_vars(_cups_var_t *a, _cups_var_t *b);
162 static int do_tests(_cups_vars_t *vars, const char *testfile);
163 static void expand_variables(_cups_vars_t *vars, char *dst, const char *src,
164 size_t dstsize)
165 #ifdef __GNUC__
166 __attribute((nonnull(1,2,3)))
167 #endif /* __GNUC__ */
168 ;
169 static int expect_matches(_cups_expect_t *expect, ipp_tag_t value_tag);
170 static ipp_t *get_collection(_cups_vars_t *vars, FILE *fp, int *linenum);
171 static char *get_filename(const char *testfile, char *dst, const char *src,
172 size_t dstsize);
173 static char *get_token(FILE *fp, char *buf, int buflen,
174 int *linenum);
175 static char *get_variable(_cups_vars_t *vars, const char *name);
176 static char *iso_date(ipp_uchar_t *date);
177 static const char *password_cb(const char *prompt);
178 static void print_attr(ipp_attribute_t *attr, ipp_tag_t *group);
179 static void print_col(ipp_t *col);
180 static void print_csv(ipp_attribute_t *attr, int num_displayed,
181 char **displayed, size_t *widths);
182 static void print_fatal_error(const char *s, ...)
183 #ifdef __GNUC__
184 __attribute__ ((__format__ (__printf__, 1, 2)))
185 #endif /* __GNUC__ */
186 ;
187 static void print_line(ipp_attribute_t *attr, int num_displayed,
188 char **displayed, size_t *widths);
189 static void print_test_error(const char *s, ...)
190 #ifdef __GNUC__
191 __attribute__ ((__format__ (__printf__, 1, 2)))
192 #endif /* __GNUC__ */
193 ;
194 static void print_xml_header(void);
195 static void print_xml_string(const char *element, const char *s);
196 static void print_xml_trailer(int success, const char *message);
197 static void set_variable(_cups_vars_t *vars, const char *name,
198 const char *value);
199 static int timeout_cb(http_t *http, void *user_data);
200 static void usage(void);
201 static int validate_attr(ipp_attribute_t *attr, int print);
202 static int with_value(char *value, int regex, ipp_attribute_t *attr,
203 int report);
204
205
206 /*
207 * 'main()' - Parse options and do tests.
208 */
209
210 int /* O - Exit status */
211 main(int argc, /* I - Number of command-line args */
212 char *argv[]) /* I - Command-line arguments */
213 {
214 int i; /* Looping var */
215 int status; /* Status of tests... */
216 char *opt, /* Current option */
217 name[1024], /* Name/value buffer */
218 *value, /* Pointer to value */
219 filename[1024], /* Real filename */
220 testname[1024]; /* Real test filename */
221 const char *testfile; /* Test file to use */
222 int interval, /* Test interval in microseconds */
223 repeat; /* Repeat count */
224 _cups_vars_t vars; /* Variables */
225 http_uri_status_t uri_status; /* URI separation status */
226 _cups_globals_t *cg = _cupsGlobals();
227 /* Global data */
228
229
230
231 /*
232 * Initialize the locale and variables...
233 */
234
235 _cupsSetLocale(argv);
236
237 memset(&vars, 0, sizeof(vars));
238 vars.family = AF_UNSPEC;
239 vars.vars = cupsArrayNew((cups_array_func_t)compare_vars, NULL);
240
241 /*
242 * We need at least:
243 *
244 * ipptool URI testfile
245 */
246
247 interval = 0;
248 repeat = 0;
249 status = 0;
250 testfile = NULL;
251
252 for (i = 1; i < argc; i ++)
253 {
254 if (argv[i][0] == '-')
255 {
256 for (opt = argv[i] + 1; *opt; opt ++)
257 {
258 switch (*opt)
259 {
260 case '4' : /* Connect using IPv4 only */
261 vars.family = AF_INET;
262 break;
263
264 #ifdef AF_INET6
265 case '6' : /* Connect using IPv6 only */
266 vars.family = AF_INET6;
267 break;
268 #endif /* AF_INET6 */
269
270 case 'C' : /* Enable HTTP chunking */
271 Transfer = _CUPS_TRANSFER_CHUNKED;
272 break;
273
274 case 'E' : /* Encrypt with TLS */
275 #ifdef HAVE_SSL
276 vars.encryption = HTTP_ENCRYPT_REQUIRED;
277 #else
278 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
279 argv[0]);
280 #endif /* HAVE_SSL */
281 break;
282
283 case 'I' : /* Ignore errors */
284 IgnoreErrors = 1;
285 break;
286
287 case 'L' : /* Disable HTTP chunking */
288 Transfer = _CUPS_TRANSFER_LENGTH;
289 break;
290
291 case 'S' : /* Encrypt with SSL */
292 #ifdef HAVE_SSL
293 vars.encryption = HTTP_ENCRYPT_ALWAYS;
294 #else
295 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
296 argv[0]);
297 #endif /* HAVE_SSL */
298 break;
299
300 case 'T' : /* Set timeout */
301 i ++;
302
303 if (i >= argc)
304 {
305 _cupsLangPuts(stderr,
306 _("ipptool: Missing timeout for \"-T\"."));
307 usage();
308 }
309
310 vars.timeout = _cupsStrScand(argv[i], NULL, localeconv());
311 break;
312
313 case 'V' : /* Set IPP version */
314 i ++;
315
316 if (i >= argc)
317 {
318 _cupsLangPuts(stderr,
319 _("ipptool: Missing version for \"-V\"."));
320 usage();
321 }
322
323 if (!strcmp(argv[i], "1.0"))
324 Version = 10;
325 else if (!strcmp(argv[i], "1.1"))
326 Version = 11;
327 else if (!strcmp(argv[i], "2.0"))
328 Version = 20;
329 else if (!strcmp(argv[i], "2.1"))
330 Version = 21;
331 else if (!strcmp(argv[i], "2.2"))
332 Version = 22;
333 else
334 {
335 _cupsLangPrintf(stderr,
336 _("ipptool: Bad version %s for \"-V\"."),
337 argv[i]);
338 usage();
339 }
340 break;
341
342 case 'X' : /* Produce XML output */
343 Output = _CUPS_OUTPUT_PLIST;
344
345 if (interval || repeat)
346 {
347 _cupsLangPuts(stderr, _("ipptool: \"-i\" and \"-n\" are "
348 "incompatible with -X\"."));
349 usage();
350 }
351 break;
352
353 case 'c' : /* CSV output */
354 Output = _CUPS_OUTPUT_CSV;
355 break;
356
357 case 'd' : /* Define a variable */
358 i ++;
359
360 if (i >= argc)
361 {
362 _cupsLangPuts(stderr,
363 _("ipptool: Missing name=value for \"-d\"."));
364 usage();
365 }
366
367 strlcpy(name, argv[i], sizeof(name));
368 if ((value = strchr(name, '=')) != NULL)
369 *value++ = '\0';
370 else
371 value = name + strlen(name);
372
373 set_variable(&vars, name, value);
374 break;
375
376 case 'f' : /* Set the default test filename */
377 i ++;
378
379 if (i >= argc)
380 {
381 _cupsLangPuts(stderr,
382 _("ipptool: Missing filename for \"-f\"."));
383 usage();
384 }
385
386 if (access(argv[i], 0) && argv[i][0] != '/')
387 {
388 snprintf(filename, sizeof(filename), "%s/ipptool/%s",
389 cg->cups_datadir, argv[i]);
390 if (access(argv[i], 0))
391 vars.filename = argv[i];
392 else
393 vars.filename = filename;
394 }
395 else
396 vars.filename = argv[i];
397 break;
398
399 case 'i' : /* Test every N seconds */
400 i ++;
401
402 if (i >= argc)
403 {
404 _cupsLangPuts(stderr,
405 _("ipptool: Missing seconds for \"-i\"."));
406 usage();
407 }
408 else
409 {
410 interval = (int)(_cupsStrScand(argv[i], NULL, localeconv()) *
411 1000000.0);
412 if (interval <= 0)
413 {
414 _cupsLangPuts(stderr,
415 _("ipptool: Invalid seconds for \"-i\"."));
416 usage();
417 }
418 }
419
420 if (Output == _CUPS_OUTPUT_PLIST && interval)
421 {
422 _cupsLangPuts(stderr, _("ipptool: \"-i\" is incompatible with "
423 "\"-X\"."));
424 usage();
425 }
426 break;
427
428 case 'l' : /* List as a table */
429 Output = _CUPS_OUTPUT_LIST;
430 break;
431
432 case 'n' : /* Repeat count */
433 i ++;
434
435 if (i >= argc)
436 {
437 _cupsLangPuts(stderr,
438 _("ipptool: Missing count for \"-n\"."));
439 usage();
440 }
441 else
442 repeat = atoi(argv[i]);
443
444 if (Output == _CUPS_OUTPUT_PLIST && repeat)
445 {
446 _cupsLangPuts(stderr, _("ipptool: \"-n\" is incompatible with "
447 "\"-X\"."));
448 usage();
449 }
450 break;
451
452 case 'q' : /* Be quiet */
453 Output = _CUPS_OUTPUT_QUIET;
454 break;
455
456 case 't' : /* CUPS test output */
457 Output = _CUPS_OUTPUT_TEST;
458 break;
459
460 case 'v' : /* Be verbose */
461 Verbosity ++;
462 break;
463
464 default :
465 _cupsLangPrintf(stderr, _("ipptool: Unknown option \"-%c\"."),
466 *opt);
467 usage();
468 break;
469 }
470 }
471 }
472 else if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "http://", 7)
473 #ifdef HAVE_SSL
474 || !strncmp(argv[i], "ipps://", 7)
475 || !strncmp(argv[i], "https://", 8)
476 #endif /* HAVE_SSL */
477 )
478 {
479 /*
480 * Set URI...
481 */
482
483 if (vars.uri)
484 {
485 _cupsLangPuts(stderr, _("ipptool: May only specify a single URI."));
486 usage();
487 }
488
489 #ifdef HAVE_SSL
490 if (!strncmp(argv[i], "ipps://", 7) || !strncmp(argv[i], "https://", 8))
491 vars.encryption = HTTP_ENCRYPT_ALWAYS;
492 #endif /* HAVE_SSL */
493
494 vars.uri = argv[i];
495 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, vars.uri,
496 vars.scheme, sizeof(vars.scheme),
497 vars.userpass, sizeof(vars.userpass),
498 vars.hostname, sizeof(vars.hostname),
499 &(vars.port),
500 vars.resource, sizeof(vars.resource));
501
502 if (uri_status != HTTP_URI_OK)
503 {
504 _cupsLangPrintf(stderr, _("ipptool: Bad URI - %s."),
505 URIStatusStrings[uri_status - HTTP_URI_OVERFLOW]);
506 return (1);
507 }
508
509 if (vars.userpass[0])
510 {
511 if ((Password = strchr(vars.userpass, ':')) != NULL)
512 *Password++ = '\0';
513
514 cupsSetUser(vars.userpass);
515 cupsSetPasswordCB(password_cb);
516 set_variable(&vars, "uriuser", vars.userpass);
517 }
518 }
519 else
520 {
521 /*
522 * Run test...
523 */
524
525 if (!vars.uri)
526 {
527 _cupsLangPuts(stderr, _("ipptool: URI required before test file."));
528 usage();
529 }
530
531 if (access(argv[i], 0) && argv[i][0] != '/')
532 {
533 snprintf(testname, sizeof(testname), "%s/ipptool/%s", cg->cups_datadir,
534 argv[i]);
535 if (access(testname, 0))
536 testfile = argv[i];
537 else
538 testfile = testname;
539 }
540 else
541 testfile = argv[i];
542
543 if (!do_tests(&vars, testfile))
544 status = 1;
545 }
546 }
547
548 if (!vars.uri || !testfile)
549 usage();
550
551 /*
552 * Loop if the interval is set...
553 */
554
555 if (Output == _CUPS_OUTPUT_PLIST)
556 print_xml_trailer(!status, NULL);
557 else if (interval > 0 && repeat > 0)
558 {
559 while (repeat > 1)
560 {
561 usleep(interval);
562 do_tests(&vars, testfile);
563 repeat --;
564 }
565 }
566 else if (interval > 0)
567 {
568 for (;;)
569 {
570 usleep(interval);
571 do_tests(&vars, testfile);
572 }
573 }
574
575 /*
576 * Exit...
577 */
578
579 return (status);
580 }
581
582
583 /*
584 * 'compare_vars()' - Compare two variables.
585 */
586
587 static int /* O - Result of comparison */
588 compare_vars(_cups_var_t *a, /* I - First variable */
589 _cups_var_t *b) /* I - Second variable */
590 {
591 return (strcasecmp(a->name, b->name));
592 }
593
594
595 /*
596 * 'do_tests()' - Do tests as specified in the test file.
597 */
598
599 static int /* 1 = success, 0 = failure */
600 do_tests(_cups_vars_t *vars, /* I - Variables */
601 const char *testfile) /* I - Test file to use */
602 {
603 int i, /* Looping var */
604 linenum, /* Current line number */
605 pass, /* Did we pass the test? */
606 prev_pass = 1, /* Did we pass the previous test? */
607 request_id, /* Current request ID */
608 show_header = 1, /* Show the test header? */
609 ignore_errors, /* Ignore test failures? */
610 skip_previous = 0; /* Skip on previous test failure? */
611 http_t *http = NULL; /* HTTP connection to server */
612 FILE *fp = NULL; /* Test file */
613 char resource[512], /* Resource for request */
614 token[1024], /* Token from file */
615 *tokenptr, /* Pointer into token */
616 temp[1024]; /* Temporary string */
617 ipp_t *request = NULL; /* IPP request */
618 ipp_t *response = NULL; /* IPP response */
619 char attr[128]; /* Attribute name */
620 ipp_op_t op; /* Operation */
621 ipp_tag_t group; /* Current group */
622 ipp_tag_t value; /* Current value type */
623 ipp_attribute_t *attrptr, /* Attribute pointer */
624 *found, /* Found attribute */
625 *lastcol = NULL; /* Last collection attribute */
626 char name[1024]; /* Name of test */
627 char filename[1024]; /* Filename */
628 _cups_transfer_t transfer; /* To chunk or not to chunk */
629 int version, /* IPP version number to use */
630 skip_test; /* Skip this test? */
631 int num_statuses = 0; /* Number of valid status codes */
632 _cups_status_t statuses[100], /* Valid status codes */
633 *last_status; /* Last STATUS (for predicates) */
634 int num_expects = 0; /* Number of expected attributes */
635 _cups_expect_t expects[200], /* Expected attributes */
636 *expect, /* Current expected attribute */
637 *last_expect; /* Last EXPECT (for predicates) */
638 int num_displayed = 0; /* Number of displayed attributes */
639 char *displayed[200]; /* Displayed attributes */
640 size_t widths[200]; /* Width of columns */
641
642
643 /*
644 * Open the test file...
645 */
646
647 if ((fp = fopen(testfile, "r")) == NULL)
648 {
649 print_fatal_error("Unable to open test file %s - %s", testfile,
650 strerror(errno));
651 pass = 0;
652 goto test_exit;
653 }
654
655 /*
656 * Connect to the server...
657 */
658
659 if ((http = _httpCreate(vars->hostname, vars->port, vars->encryption,
660 vars->family)) == NULL)
661 {
662 print_fatal_error("Unable to connect to %s on port %d - %s", vars->hostname,
663 vars->port, strerror(errno));
664 pass = 0;
665 goto test_exit;
666 }
667
668 if (httpReconnect(http))
669 {
670 print_fatal_error("Unable to connect to %s on port %d - %s", vars->hostname,
671 vars->port, strerror(errno));
672 pass = 0;
673 goto test_exit;
674 }
675
676 if (vars->timeout > 0.0)
677 _httpSetTimeout(http, vars->timeout, timeout_cb, NULL);
678
679 /*
680 * Loop on tests...
681 */
682
683 CUPS_SRAND(time(NULL));
684
685 pass = 1;
686 linenum = 1;
687 request_id = (CUPS_RAND() % 1000) * 137 + 1;
688
689 while (get_token(fp, token, sizeof(token), &linenum) != NULL)
690 {
691 /*
692 * Expect an open brace...
693 */
694
695 if (!strcmp(token, "DEFINE"))
696 {
697 /*
698 * DEFINE name value
699 */
700
701 if (get_token(fp, attr, sizeof(attr), &linenum) &&
702 get_token(fp, temp, sizeof(temp), &linenum))
703 {
704 expand_variables(vars, token, temp, sizeof(token));
705 set_variable(vars, attr, token);
706 }
707 else
708 {
709 print_fatal_error("Missing DEFINE name and/or value on line %d.",
710 linenum);
711 pass = 0;
712 goto test_exit;
713 }
714
715 continue;
716 }
717 else if (!strcmp(token, "IGNORE-ERRORS"))
718 {
719 /*
720 * IGNORE-ERRORS yes
721 * IGNORE-ERRORS no
722 */
723
724 if (get_token(fp, temp, sizeof(temp), &linenum) &&
725 (!strcasecmp(temp, "yes") || !strcasecmp(temp, "no")))
726 {
727 IgnoreErrors = !strcasecmp(temp, "yes");
728 }
729 else
730 {
731 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum);
732 pass = 0;
733 goto test_exit;
734 }
735
736 continue;
737 }
738 else if (!strcmp(token, "INCLUDE"))
739 {
740 /*
741 * INCLUDE "filename"
742 * INCLUDE <filename>
743 */
744
745 if (get_token(fp, temp, sizeof(temp), &linenum))
746 {
747 /*
748 * Map the filename to and then run the tests...
749 */
750
751 if (!do_tests(vars, get_filename(testfile, filename, temp,
752 sizeof(filename))))
753 {
754 pass = 0;
755
756 if (!IgnoreErrors)
757 goto test_exit;
758 }
759 }
760 else
761 {
762 print_fatal_error("Missing INCLUDE filename on line %d.", linenum);
763 pass = 0;
764 goto test_exit;
765 }
766
767 show_header = 1;
768 continue;
769 }
770 else if (!strcmp(token, "SKIP-IF-DEFINED"))
771 {
772 /*
773 * SKIP-IF-DEFINED variable
774 */
775
776 if (get_token(fp, temp, sizeof(temp), &linenum))
777 {
778 if (get_variable(vars, temp))
779 goto test_exit;
780 }
781 else
782 {
783 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.", linenum);
784 pass = 0;
785 goto test_exit;
786 }
787 }
788 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
789 {
790 /*
791 * SKIP-IF-NOT-DEFINED variable
792 */
793
794 if (get_token(fp, temp, sizeof(temp), &linenum))
795 {
796 if (!get_variable(vars, temp))
797 goto test_exit;
798 }
799 else
800 {
801 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
802 linenum);
803 pass = 0;
804 goto test_exit;
805 }
806 }
807 else if (!strcmp(token, "TRANSFER"))
808 {
809 /*
810 * TRANSFER auto
811 * TRANSFER chunked
812 * TRANSFER length
813 */
814
815 if (get_token(fp, temp, sizeof(temp), &linenum))
816 {
817 if (!strcmp(temp, "auto"))
818 Transfer = _CUPS_TRANSFER_AUTO;
819 else if (!strcmp(temp, "chunked"))
820 Transfer = _CUPS_TRANSFER_CHUNKED;
821 else if (!strcmp(temp, "length"))
822 Transfer = _CUPS_TRANSFER_LENGTH;
823 else
824 {
825 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp,
826 linenum);
827 pass = 0;
828 goto test_exit;
829 }
830 }
831 else
832 {
833 print_fatal_error("Missing TRANSFER value on line %d.", linenum);
834 pass = 0;
835 goto test_exit;
836 }
837
838 continue;
839 }
840 else if (!strcmp(token, "VERSION"))
841 {
842 if (get_token(fp, temp, sizeof(temp), &linenum))
843 {
844 if (!strcmp(temp, "1.0"))
845 Version = 10;
846 else if (!strcmp(temp, "1.1"))
847 Version = 11;
848 else if (!strcmp(temp, "2.0"))
849 Version = 20;
850 else if (!strcmp(temp, "2.1"))
851 Version = 21;
852 else if (!strcmp(temp, "2.2"))
853 Version = 22;
854 else
855 {
856 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp, linenum);
857 pass = 0;
858 goto test_exit;
859 }
860 }
861 else
862 {
863 print_fatal_error("Missing VERSION number on line %d.", linenum);
864 pass = 0;
865 goto test_exit;
866 }
867
868 continue;
869 }
870 else if (strcmp(token, "{"))
871 {
872 print_fatal_error("Unexpected token %s seen on line %d.", token, linenum);
873 pass = 0;
874 goto test_exit;
875 }
876
877 /*
878 * Initialize things...
879 */
880
881 if (show_header)
882 {
883 if (Output == _CUPS_OUTPUT_PLIST)
884 print_xml_header();
885 else if (Output == _CUPS_OUTPUT_TEST)
886 printf("\"%s\":\n", testfile);
887
888 show_header = 0;
889 }
890
891 strlcpy(resource, vars->resource, sizeof(resource));
892
893 request_id ++;
894 request = ippNew();
895 op = (ipp_op_t)0;
896 group = IPP_TAG_ZERO;
897 ignore_errors = IgnoreErrors;
898 last_expect = NULL;
899 last_status = NULL;
900 filename[0] = '\0';
901 skip_test = 0;
902 version = Version;
903 transfer = Transfer;
904
905 strlcpy(name, testfile, sizeof(name));
906 if (strrchr(name, '.') != NULL)
907 *strrchr(name, '.') = '\0';
908
909 /*
910 * Parse until we see a close brace...
911 */
912
913 while (get_token(fp, token, sizeof(token), &linenum) != NULL)
914 {
915 if (strcasecmp(token, "COUNT") &&
916 strcasecmp(token, "DEFINE-MATCH") &&
917 strcasecmp(token, "DEFINE-NO-MATCH") &&
918 strcasecmp(token, "DEFINE-VALUE") &&
919 strcasecmp(token, "IF-DEFINED") &&
920 strcasecmp(token, "IF-NOT-DEFINED") &&
921 strcasecmp(token, "IN-GROUP") &&
922 strcasecmp(token, "OF-TYPE") &&
923 strcasecmp(token, "SAME-COUNT-AS") &&
924 strcasecmp(token, "WITH-VALUE"))
925 last_expect = NULL;
926
927 if (strcasecmp(token, "IF-DEFINED") &&
928 strcasecmp(token, "IF-NOT-DEFINED"))
929 last_status = NULL;
930
931 if (!strcmp(token, "}"))
932 break;
933 else if (!strcmp(token, "{") && lastcol)
934 {
935 /*
936 * Another collection value
937 */
938
939 ipp_t *col = get_collection(vars, fp, &linenum);
940 /* Collection value */
941
942 if (col)
943 {
944 ipp_attribute_t *tempcol; /* Pointer to new buffer */
945
946
947 /*
948 * Reallocate memory...
949 */
950
951 if ((tempcol = realloc(lastcol, sizeof(ipp_attribute_t) +
952 (lastcol->num_values + 1) *
953 sizeof(ipp_value_t))) == NULL)
954 {
955 print_fatal_error("Unable to allocate memory on line %d.", linenum);
956 pass = 0;
957 goto test_exit;
958 }
959
960 if (tempcol != lastcol)
961 {
962 /*
963 * Reset pointers in the list...
964 */
965
966 if (request->prev)
967 request->prev->next = tempcol;
968 else
969 request->attrs = tempcol;
970
971 lastcol = request->current = request->last = tempcol;
972 }
973
974 lastcol->values[lastcol->num_values].collection = col;
975 lastcol->num_values ++;
976 }
977 else
978 {
979 pass = 0;
980 goto test_exit;
981 }
982 }
983 else if (!strcmp(token, "DEFINE"))
984 {
985 /*
986 * DEFINE name value
987 */
988
989 if (get_token(fp, attr, sizeof(attr), &linenum) &&
990 get_token(fp, temp, sizeof(temp), &linenum))
991 {
992 expand_variables(vars, token, temp, sizeof(token));
993 set_variable(vars, attr, token);
994 }
995 else
996 {
997 print_fatal_error("Missing DEFINE name and/or value on line %d.",
998 linenum);
999 pass = 0;
1000 goto test_exit;
1001 }
1002 }
1003 else if (!strcmp(token, "IGNORE-ERRORS"))
1004 {
1005 /*
1006 * IGNORE-ERRORS yes
1007 * IGNORE-ERRORS no
1008 */
1009
1010 if (get_token(fp, temp, sizeof(temp), &linenum) &&
1011 (!strcasecmp(temp, "yes") || !strcasecmp(temp, "no")))
1012 {
1013 ignore_errors = !strcasecmp(temp, "yes");
1014 }
1015 else
1016 {
1017 print_fatal_error("Missing IGNORE-ERRORS value on line %d.", linenum);
1018 pass = 0;
1019 goto test_exit;
1020 }
1021
1022 continue;
1023 }
1024 else if (!strcasecmp(token, "NAME"))
1025 {
1026 /*
1027 * Name of test...
1028 */
1029
1030 get_token(fp, name, sizeof(name), &linenum);
1031 }
1032 else if (!strcmp(token, "REQUEST-ID"))
1033 {
1034 /*
1035 * REQUEST-ID #
1036 * REQUEST-ID random
1037 */
1038
1039 if (get_token(fp, temp, sizeof(temp), &linenum))
1040 {
1041 if (isdigit(temp[0] & 255))
1042 request_id = atoi(temp);
1043 else if (!strcasecmp(temp, "random"))
1044 request_id = (CUPS_RAND() % 1000) * 137 + 1;
1045 else
1046 {
1047 print_fatal_error("Bad REQUEST-ID value \"%s\" on line %d.", temp,
1048 linenum);
1049 pass = 0;
1050 goto test_exit;
1051 }
1052 }
1053 else
1054 {
1055 print_fatal_error("Missing REQUEST-ID value on line %d.", linenum);
1056 pass = 0;
1057 goto test_exit;
1058 }
1059 }
1060 else if (!strcmp(token, "SKIP-IF-DEFINED"))
1061 {
1062 /*
1063 * SKIP-IF-DEFINED variable
1064 */
1065
1066 if (get_token(fp, temp, sizeof(temp), &linenum))
1067 {
1068 if (get_variable(vars, temp))
1069 skip_test = 1;
1070 }
1071 else
1072 {
1073 print_fatal_error("Missing SKIP-IF-DEFINED value on line %d.",
1074 linenum);
1075 pass = 0;
1076 goto test_exit;
1077 }
1078 }
1079 else if (!strcmp(token, "SKIP-IF-NOT-DEFINED"))
1080 {
1081 /*
1082 * SKIP-IF-NOT-DEFINED variable
1083 */
1084
1085 if (get_token(fp, temp, sizeof(temp), &linenum))
1086 {
1087 if (!get_variable(vars, temp))
1088 skip_test = 1;
1089 }
1090 else
1091 {
1092 print_fatal_error("Missing SKIP-IF-NOT-DEFINED value on line %d.",
1093 linenum);
1094 pass = 0;
1095 goto test_exit;
1096 }
1097 }
1098 else if (!strcmp(token, "SKIP-PREVIOUS-ERROR"))
1099 {
1100 /*
1101 * SKIP-PREVIOUS-ERROR yes
1102 * SKIP-PREVIOUS-ERROR no
1103 */
1104
1105 if (get_token(fp, temp, sizeof(temp), &linenum) &&
1106 (!strcasecmp(temp, "yes") || !strcasecmp(temp, "no")))
1107 {
1108 skip_previous = !strcasecmp(temp, "yes");
1109 }
1110 else
1111 {
1112 print_fatal_error("Missing SKIP-PREVIOUS-ERROR value on line %d.", linenum);
1113 pass = 0;
1114 goto test_exit;
1115 }
1116
1117 continue;
1118 }
1119 else if (!strcmp(token, "TRANSFER"))
1120 {
1121 /*
1122 * TRANSFER auto
1123 * TRANSFER chunked
1124 * TRANSFER length
1125 */
1126
1127 if (get_token(fp, temp, sizeof(temp), &linenum))
1128 {
1129 if (!strcmp(temp, "auto"))
1130 transfer = _CUPS_TRANSFER_AUTO;
1131 else if (!strcmp(temp, "chunked"))
1132 transfer = _CUPS_TRANSFER_CHUNKED;
1133 else if (!strcmp(temp, "length"))
1134 transfer = _CUPS_TRANSFER_LENGTH;
1135 else
1136 {
1137 print_fatal_error("Bad TRANSFER value \"%s\" on line %d.", temp,
1138 linenum);
1139 pass = 0;
1140 goto test_exit;
1141 }
1142 }
1143 else
1144 {
1145 print_fatal_error("Missing TRANSFER value on line %d.", linenum);
1146 pass = 0;
1147 goto test_exit;
1148 }
1149 }
1150 else if (!strcasecmp(token, "VERSION"))
1151 {
1152 if (get_token(fp, temp, sizeof(temp), &linenum))
1153 {
1154 if (!strcmp(temp, "0.0"))
1155 version = 0;
1156 else if (!strcmp(temp, "1.0"))
1157 version = 10;
1158 else if (!strcmp(temp, "1.1"))
1159 version = 11;
1160 else if (!strcmp(temp, "2.0"))
1161 version = 20;
1162 else if (!strcmp(temp, "2.1"))
1163 version = 21;
1164 else if (!strcmp(temp, "2.2"))
1165 version = 22;
1166 else
1167 {
1168 print_fatal_error("Bad VERSION \"%s\" on line %d.", temp, linenum);
1169 pass = 0;
1170 goto test_exit;
1171 }
1172 }
1173 else
1174 {
1175 print_fatal_error("Missing VERSION number on line %d.", linenum);
1176 pass = 0;
1177 goto test_exit;
1178 }
1179 }
1180 else if (!strcasecmp(token, "RESOURCE"))
1181 {
1182 /*
1183 * Resource name...
1184 */
1185
1186 if (!get_token(fp, resource, sizeof(resource), &linenum))
1187 {
1188 print_fatal_error("Missing RESOURCE path on line %d.", linenum);
1189 pass = 0;
1190 goto test_exit;
1191 }
1192 }
1193 else if (!strcasecmp(token, "OPERATION"))
1194 {
1195 /*
1196 * Operation...
1197 */
1198
1199 if (!get_token(fp, token, sizeof(token), &linenum))
1200 {
1201 print_fatal_error("Missing OPERATION code on line %d.", linenum);
1202 pass = 0;
1203 goto test_exit;
1204 }
1205
1206 if ((op = ippOpValue(token)) < 0 && (op = strtol(token, NULL, 0)) == 0)
1207 {
1208 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token,
1209 linenum);
1210 pass = 0;
1211 goto test_exit;
1212 }
1213 }
1214 else if (!strcasecmp(token, "GROUP"))
1215 {
1216 /*
1217 * Attribute group...
1218 */
1219
1220 if (!get_token(fp, token, sizeof(token), &linenum))
1221 {
1222 print_fatal_error("Missing GROUP tag on line %d.", linenum);
1223 pass = 0;
1224 goto test_exit;
1225 }
1226
1227 if ((value = ippTagValue(token)) < 0)
1228 {
1229 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token, linenum);
1230 pass = 0;
1231 goto test_exit;
1232 }
1233
1234 if (value == group)
1235 ippAddSeparator(request);
1236
1237 group = value;
1238 }
1239 else if (!strcasecmp(token, "DELAY"))
1240 {
1241 /*
1242 * Delay before operation...
1243 */
1244
1245 double delay;
1246
1247 if (!get_token(fp, token, sizeof(token), &linenum))
1248 {
1249 print_fatal_error("Missing DELAY value on line %d.", linenum);
1250 pass = 0;
1251 goto test_exit;
1252 }
1253
1254 if ((delay = _cupsStrScand(token, NULL, localeconv())) <= 0.0)
1255 {
1256 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token,
1257 linenum);
1258 pass = 0;
1259 goto test_exit;
1260 }
1261 else
1262 {
1263 if (Output == _CUPS_OUTPUT_TEST)
1264 printf(" [%g second delay]\n", delay);
1265
1266 usleep((int)(1000000.0 * delay));
1267 }
1268 }
1269 else if (!strcasecmp(token, "ATTR"))
1270 {
1271 /*
1272 * Attribute...
1273 */
1274
1275 if (!get_token(fp, token, sizeof(token), &linenum))
1276 {
1277 print_fatal_error("Missing ATTR value tag on line %d.", linenum);
1278 pass = 0;
1279 goto test_exit;
1280 }
1281
1282 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
1283 {
1284 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token,
1285 linenum);
1286 pass = 0;
1287 goto test_exit;
1288 }
1289
1290 if (!get_token(fp, attr, sizeof(attr), &linenum))
1291 {
1292 print_fatal_error("Missing ATTR name on line %d.", linenum);
1293 pass = 0;
1294 goto test_exit;
1295 }
1296
1297 if (!get_token(fp, temp, sizeof(temp), &linenum))
1298 {
1299 print_fatal_error("Missing ATTR value on line %d.", linenum);
1300 pass = 0;
1301 goto test_exit;
1302 }
1303
1304 expand_variables(vars, token, temp, sizeof(token));
1305
1306 switch (value)
1307 {
1308 case IPP_TAG_BOOLEAN :
1309 if (!strcasecmp(token, "true"))
1310 ippAddBoolean(request, group, attr, 1);
1311 else
1312 ippAddBoolean(request, group, attr, atoi(token));
1313 break;
1314
1315 case IPP_TAG_INTEGER :
1316 case IPP_TAG_ENUM :
1317 if (!strchr(token, ','))
1318 ippAddInteger(request, group, value, attr,
1319 strtol(token, &tokenptr, 0));
1320 else
1321 {
1322 int values[100], /* Values */
1323 num_values = 1; /* Number of values */
1324
1325 values[0] = strtol(token, &tokenptr, 10);
1326 while (tokenptr && *tokenptr &&
1327 num_values < (int)(sizeof(values) / sizeof(values[0])))
1328 {
1329 if (*tokenptr == ',')
1330 tokenptr ++;
1331 else if (!isdigit(*tokenptr & 255) && *tokenptr != '-')
1332 break;
1333
1334 values[num_values] = strtol(tokenptr, &tokenptr, 0);
1335 num_values ++;
1336 }
1337
1338 ippAddIntegers(request, group, value, attr, num_values, values);
1339 }
1340
1341 if (!tokenptr || *tokenptr)
1342 {
1343 print_fatal_error("Bad %s value \"%s\" on line %d.",
1344 ippTagString(value), token, linenum);
1345 pass = 0;
1346 goto test_exit;
1347 }
1348 break;
1349
1350 case IPP_TAG_RESOLUTION :
1351 {
1352 int xres, /* X resolution */
1353 yres; /* Y resolution */
1354 char *ptr; /* Pointer into value */
1355
1356 xres = yres = strtol(token, (char **)&ptr, 10);
1357 if (ptr > token && xres > 0)
1358 {
1359 if (*ptr == 'x')
1360 yres = strtol(ptr + 1, (char **)&ptr, 10);
1361 }
1362
1363 if (ptr <= token || xres <= 0 || yres <= 0 || !ptr ||
1364 (strcasecmp(ptr, "dpi") && strcasecmp(ptr, "dpc") &&
1365 strcasecmp(ptr, "other")))
1366 {
1367 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1368 token, linenum);
1369 pass = 0;
1370 goto test_exit;
1371 }
1372
1373 if (!strcasecmp(ptr, "dpi"))
1374 ippAddResolution(request, group, attr, IPP_RES_PER_INCH,
1375 xres, yres);
1376 else if (!strcasecmp(ptr, "dpc"))
1377 ippAddResolution(request, group, attr, IPP_RES_PER_CM,
1378 xres, yres);
1379 else
1380 ippAddResolution(request, group, attr, (ipp_res_t)0,
1381 xres, yres);
1382 }
1383 break;
1384
1385 case IPP_TAG_RANGE :
1386 {
1387 int lowers[4], /* Lower value */
1388 uppers[4], /* Upper values */
1389 num_vals; /* Number of values */
1390
1391
1392 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
1393 lowers + 0, uppers + 0,
1394 lowers + 1, uppers + 1,
1395 lowers + 2, uppers + 2,
1396 lowers + 3, uppers + 3);
1397
1398 if ((num_vals & 1) || num_vals == 0)
1399 {
1400 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1401 "%d.", token, linenum);
1402 pass = 0;
1403 goto test_exit;
1404 }
1405
1406 ippAddRanges(request, group, attr, num_vals / 2, lowers,
1407 uppers);
1408 }
1409 break;
1410
1411 case IPP_TAG_BEGIN_COLLECTION :
1412 if (!strcmp(token, "{"))
1413 {
1414 ipp_t *col = get_collection(vars, fp, &linenum);
1415 /* Collection value */
1416
1417 if (col)
1418 {
1419 lastcol = ippAddCollection(request, group, attr, col);
1420 ippDelete(col);
1421 }
1422 else
1423 {
1424 pass = 0;
1425 goto test_exit;
1426 }
1427 }
1428 else
1429 {
1430 print_fatal_error("Bad ATTR collection value on line %d.",
1431 linenum);
1432 pass = 0;
1433 goto test_exit;
1434 }
1435 break;
1436
1437 default :
1438 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1439 ippTagString(value), linenum);
1440 pass = 0;
1441 goto test_exit;
1442
1443 case IPP_TAG_TEXTLANG :
1444 case IPP_TAG_NAMELANG :
1445 case IPP_TAG_TEXT :
1446 case IPP_TAG_NAME :
1447 case IPP_TAG_KEYWORD :
1448 case IPP_TAG_URI :
1449 case IPP_TAG_URISCHEME :
1450 case IPP_TAG_CHARSET :
1451 case IPP_TAG_LANGUAGE :
1452 case IPP_TAG_MIMETYPE :
1453 if (!strchr(token, ','))
1454 ippAddString(request, group, value, attr, NULL, token);
1455 else
1456 {
1457 /*
1458 * Multiple string values...
1459 */
1460
1461 int num_values; /* Number of values */
1462 char *values[100], /* Values */
1463 *ptr; /* Pointer to next value */
1464
1465
1466 values[0] = token;
1467 num_values = 1;
1468
1469 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
1470 {
1471 *ptr++ = '\0';
1472 values[num_values] = ptr;
1473 num_values ++;
1474 }
1475
1476 ippAddStrings(request, group, value, attr, num_values,
1477 NULL, (const char **)values);
1478 }
1479 break;
1480 }
1481 }
1482 else if (!strcasecmp(token, "FILE"))
1483 {
1484 /*
1485 * File...
1486 */
1487
1488 if (!get_token(fp, temp, sizeof(temp), &linenum))
1489 {
1490 print_fatal_error("Missing FILE filename on line %d.", linenum);
1491 pass = 0;
1492 goto test_exit;
1493 }
1494
1495 expand_variables(vars, token, temp, sizeof(token));
1496 get_filename(testfile, filename, token, sizeof(filename));
1497 }
1498 else if (!strcasecmp(token, "STATUS"))
1499 {
1500 /*
1501 * Status...
1502 */
1503
1504 if (num_statuses >= (int)(sizeof(statuses) / sizeof(statuses[0])))
1505 {
1506 print_fatal_error("Too many STATUS's on line %d.", linenum);
1507 pass = 0;
1508 goto test_exit;
1509 }
1510
1511 if (!get_token(fp, token, sizeof(token), &linenum))
1512 {
1513 print_fatal_error("Missing STATUS code on line %d.", linenum);
1514 pass = 0;
1515 goto test_exit;
1516 }
1517
1518 if ((statuses[num_statuses].status = ippErrorValue(token)) < 0 &&
1519 (statuses[num_statuses].status = strtol(token, NULL, 0)) == 0)
1520 {
1521 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token,
1522 linenum);
1523 pass = 0;
1524 goto test_exit;
1525 }
1526
1527 last_status = statuses + num_statuses;
1528 num_statuses ++;
1529
1530 last_status->if_defined = NULL;
1531 last_status->if_not_defined = NULL;
1532 }
1533 else if (!strcasecmp(token, "EXPECT"))
1534 {
1535 /*
1536 * Expected attributes...
1537 */
1538
1539 if (num_expects >= (int)(sizeof(expects) / sizeof(expects[0])))
1540 {
1541 print_fatal_error("Too many EXPECT's on line %d.", linenum);
1542 pass = 0;
1543 goto test_exit;
1544 }
1545
1546 if (!get_token(fp, token, sizeof(token), &linenum))
1547 {
1548 print_fatal_error("Missing EXPECT name on line %d.", linenum);
1549 pass = 0;
1550 goto test_exit;
1551 }
1552
1553 last_expect = expects + num_expects;
1554 num_expects ++;
1555
1556 memset(last_expect, 0, sizeof(_cups_expect_t));
1557
1558 if (token[0] == '!')
1559 {
1560 last_expect->not_expect = 1;
1561 last_expect->name = strdup(token + 1);
1562 }
1563 else if (token[0] == '?')
1564 {
1565 last_expect->optional = 1;
1566 last_expect->name = strdup(token + 1);
1567 }
1568 else
1569 last_expect->name = strdup(token);
1570 }
1571 else if (!strcasecmp(token, "COUNT"))
1572 {
1573 if (!get_token(fp, token, sizeof(token), &linenum))
1574 {
1575 print_fatal_error("Missing COUNT number on line %d.", linenum);
1576 pass = 0;
1577 goto test_exit;
1578 }
1579
1580 if ((i = atoi(token)) <= 0)
1581 {
1582 print_fatal_error("Bad COUNT \"%s\" on line %d.", token, linenum);
1583 pass = 0;
1584 goto test_exit;
1585 }
1586
1587 if (last_expect)
1588 last_expect->count = i;
1589 else
1590 {
1591 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1592 linenum);
1593 pass = 0;
1594 goto test_exit;
1595 }
1596 }
1597 else if (!strcasecmp(token, "DEFINE-MATCH"))
1598 {
1599 if (!get_token(fp, token, sizeof(token), &linenum))
1600 {
1601 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1602 linenum);
1603 pass = 0;
1604 goto test_exit;
1605 }
1606
1607 if (last_expect)
1608 last_expect->define_match = strdup(token);
1609 else
1610 {
1611 print_fatal_error("DEFINE-MATCH without a preceding EXPECT on line "
1612 "%d.", linenum);
1613 pass = 0;
1614 goto test_exit;
1615 }
1616 }
1617 else if (!strcasecmp(token, "DEFINE-NO-MATCH"))
1618 {
1619 if (!get_token(fp, token, sizeof(token), &linenum))
1620 {
1621 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1622 linenum);
1623 pass = 0;
1624 goto test_exit;
1625 }
1626
1627 if (last_expect)
1628 last_expect->define_no_match = strdup(token);
1629 else
1630 {
1631 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT on "
1632 "line %d.", linenum);
1633 pass = 0;
1634 goto test_exit;
1635 }
1636 }
1637 else if (!strcasecmp(token, "DEFINE-VALUE"))
1638 {
1639 if (!get_token(fp, token, sizeof(token), &linenum))
1640 {
1641 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1642 linenum);
1643 pass = 0;
1644 goto test_exit;
1645 }
1646
1647 if (last_expect)
1648 last_expect->define_value = strdup(token);
1649 else
1650 {
1651 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on line "
1652 "%d.", linenum);
1653 pass = 0;
1654 goto test_exit;
1655 }
1656 }
1657 else if (!strcasecmp(token, "OF-TYPE"))
1658 {
1659 if (!get_token(fp, token, sizeof(token), &linenum))
1660 {
1661 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1662 linenum);
1663 pass = 0;
1664 goto test_exit;
1665 }
1666
1667 if (last_expect)
1668 last_expect->of_type = strdup(token);
1669 else
1670 {
1671 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1672 linenum);
1673 pass = 0;
1674 goto test_exit;
1675 }
1676 }
1677 else if (!strcasecmp(token, "IN-GROUP"))
1678 {
1679 ipp_tag_t in_group; /* IN-GROUP value */
1680
1681
1682 if (!get_token(fp, token, sizeof(token), &linenum))
1683 {
1684 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum);
1685 pass = 0;
1686 goto test_exit;
1687 }
1688
1689 if ((in_group = ippTagValue(token)) == (ipp_tag_t)-1)
1690 {
1691 }
1692 else if (last_expect)
1693 last_expect->in_group = in_group;
1694 else
1695 {
1696 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1697 linenum);
1698 pass = 0;
1699 goto test_exit;
1700 }
1701 }
1702 else if (!strcasecmp(token, "SAME-COUNT-AS"))
1703 {
1704 if (!get_token(fp, token, sizeof(token), &linenum))
1705 {
1706 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum);
1707 pass = 0;
1708 goto test_exit;
1709 }
1710
1711 if (last_expect)
1712 last_expect->same_count_as = strdup(token);
1713 else
1714 {
1715 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1716 "%d.", linenum);
1717 pass = 0;
1718 goto test_exit;
1719 }
1720 }
1721 else if (!strcasecmp(token, "IF-DEFINED"))
1722 {
1723 if (!get_token(fp, token, sizeof(token), &linenum))
1724 {
1725 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum);
1726 pass = 0;
1727 goto test_exit;
1728 }
1729
1730 if (last_expect)
1731 last_expect->if_defined = strdup(token);
1732 else if (last_status)
1733 last_status->if_defined = strdup(token);
1734 else
1735 {
1736 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1737 "on line %d.", linenum);
1738 pass = 0;
1739 goto test_exit;
1740 }
1741 }
1742 else if (!strcasecmp(token, "IF-NOT-DEFINED"))
1743 {
1744 if (!get_token(fp, token, sizeof(token), &linenum))
1745 {
1746 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum);
1747 pass = 0;
1748 goto test_exit;
1749 }
1750
1751 if (last_expect)
1752 last_expect->if_not_defined = strdup(token);
1753 else if (last_status)
1754 last_status->if_not_defined = strdup(token);
1755 else
1756 {
1757 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
1758 "on line %d.", linenum);
1759 pass = 0;
1760 goto test_exit;
1761 }
1762 }
1763 else if (!strcasecmp(token, "WITH-VALUE"))
1764 {
1765 if (!get_token(fp, temp, sizeof(temp), &linenum))
1766 {
1767 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum);
1768 pass = 0;
1769 goto test_exit;
1770 }
1771
1772 if (last_expect)
1773 {
1774 /*
1775 * Expand any variables in the value and then save it.
1776 */
1777
1778 expand_variables(vars, token, temp, sizeof(token));
1779
1780 tokenptr = token + strlen(token) - 1;
1781
1782 if (token[0] == '/' && tokenptr > token && *tokenptr == '/')
1783 {
1784 /*
1785 * WITH-VALUE is a POSIX extended regular expression.
1786 */
1787
1788 last_expect->with_value = calloc(1, tokenptr - token);
1789 last_expect->with_regex = 1;
1790
1791 if (last_expect->with_value)
1792 memcpy(last_expect->with_value, token + 1, tokenptr - token - 1);
1793 }
1794 else
1795 {
1796 /*
1797 * WITH-VALUE is a literal value...
1798 */
1799
1800 last_expect->with_value = strdup(token);
1801 }
1802 }
1803 else
1804 {
1805 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
1806 linenum);
1807 pass = 0;
1808 goto test_exit;
1809 }
1810 }
1811 else if (!strcasecmp(token, "DISPLAY"))
1812 {
1813 /*
1814 * Display attributes...
1815 */
1816
1817 if (num_displayed >= (int)(sizeof(displayed) / sizeof(displayed[0])))
1818 {
1819 print_fatal_error("Too many DISPLAY's on line %d", linenum);
1820 pass = 0;
1821 goto test_exit;
1822 }
1823
1824 if (!get_token(fp, token, sizeof(token), &linenum))
1825 {
1826 print_fatal_error("Missing DISPLAY name on line %d.", linenum);
1827 pass = 0;
1828 goto test_exit;
1829 }
1830
1831 displayed[num_displayed] = strdup(token);
1832 num_displayed ++;
1833 }
1834 else
1835 {
1836 print_fatal_error("Unexpected token %s seen on line %d.", token,
1837 linenum);
1838 pass = 0;
1839 goto test_exit;
1840 }
1841 }
1842
1843 /*
1844 * Submit the IPP request...
1845 */
1846
1847 request->request.op.version[0] = version / 10;
1848 request->request.op.version[1] = version % 10;
1849 request->request.op.operation_id = op;
1850 request->request.op.request_id = request_id;
1851
1852 if (Output == _CUPS_OUTPUT_PLIST)
1853 {
1854 puts("<dict>");
1855 puts("<key>Name</key>");
1856 print_xml_string("string", name);
1857 puts("<key>Operation</key>");
1858 print_xml_string("string", ippOpString(op));
1859 puts("<key>RequestAttributes</key>");
1860 puts("<array>");
1861 puts("<dict>");
1862 for (attrptr = request->attrs, group = attrptr->group_tag;
1863 attrptr;
1864 attrptr = attrptr->next)
1865 print_attr(attrptr, &group);
1866 puts("</dict>");
1867 puts("</array>");
1868 }
1869 else if (Output == _CUPS_OUTPUT_TEST)
1870 {
1871 if (Verbosity)
1872 {
1873 printf(" %s:\n", ippOpString(op));
1874
1875 for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
1876 print_attr(attrptr, NULL);
1877 }
1878
1879 printf(" %-69.69s [", name);
1880 fflush(stdout);
1881 }
1882
1883 if ((skip_previous && !prev_pass) || skip_test)
1884 {
1885 ippDelete(request);
1886 request = NULL;
1887
1888 if (Output == _CUPS_OUTPUT_PLIST)
1889 {
1890 puts("<key>Successful</key>");
1891 puts("<true />");
1892 puts("<key>StatusCode</key>");
1893 print_xml_string("string", "skip");
1894 puts("<key>ResponseAttributes</key>");
1895 puts("<dict>");
1896 puts("</dict>");
1897 }
1898 else if (Output == _CUPS_OUTPUT_TEST)
1899 puts("SKIP]");
1900
1901 goto skip_error;
1902 }
1903
1904 if (transfer == _CUPS_TRANSFER_CHUNKED ||
1905 (transfer == _CUPS_TRANSFER_AUTO && filename[0]))
1906 {
1907 /*
1908 * Send request using chunking...
1909 */
1910
1911 http_status_t status = cupsSendRequest(http, request, resource, 0);
1912
1913 if (status == HTTP_CONTINUE && filename[0])
1914 {
1915 int fd; /* File to send */
1916 char buffer[8192]; /* Copy buffer */
1917 ssize_t bytes; /* Bytes read/written */
1918
1919 if ((fd = open(filename, O_RDONLY | O_BINARY)) >= 0)
1920 {
1921 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1922 if ((status = cupsWriteRequestData(http, buffer,
1923 bytes)) != HTTP_CONTINUE)
1924 break;
1925 }
1926 else
1927 {
1928 snprintf(buffer, sizeof(buffer), "%s: %s", filename, strerror(errno));
1929 _cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
1930
1931 status = HTTP_ERROR;
1932 }
1933 }
1934
1935 ippDelete(request);
1936
1937 if (status == HTTP_CONTINUE)
1938 response = cupsGetResponse(http, resource);
1939 else
1940 response = NULL;
1941 }
1942 else if (filename[0])
1943 response = cupsDoFileRequest(http, request, resource, filename);
1944 else
1945 response = cupsDoRequest(http, request, resource);
1946
1947 request = NULL;
1948 prev_pass = 1;
1949
1950 if (!response)
1951 prev_pass = pass = 0;
1952 else
1953 {
1954 if (http->version != HTTP_1_1)
1955 prev_pass = pass = 0;
1956
1957 if (response->request.status.request_id != request_id)
1958 prev_pass = pass = 0;
1959
1960 if (version &&
1961 (response->request.status.version[0] != (version / 10) ||
1962 response->request.status.version[1] != (version % 10)))
1963 prev_pass = pass = 0;
1964
1965 if ((attrptr = ippFindAttribute(response, "job-id",
1966 IPP_TAG_INTEGER)) != NULL)
1967 {
1968 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1969 set_variable(vars, "job-id", temp);
1970 }
1971
1972 if ((attrptr = ippFindAttribute(response, "job-uri",
1973 IPP_TAG_URI)) != NULL)
1974 set_variable(vars, "job-uri", attrptr->values[0].string.text);
1975
1976 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
1977 IPP_TAG_INTEGER)) != NULL)
1978 {
1979 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1980 set_variable(vars, "notify-subscription-id", temp);
1981 }
1982
1983 attrptr = response->attrs;
1984 if (!attrptr || !attrptr->name ||
1985 attrptr->value_tag != IPP_TAG_CHARSET ||
1986 attrptr->group_tag != IPP_TAG_OPERATION ||
1987 attrptr->num_values != 1 ||
1988 strcmp(attrptr->name, "attributes-charset"))
1989 prev_pass = pass = 0;
1990
1991 if (attrptr)
1992 {
1993 attrptr = attrptr->next;
1994 if (!attrptr || !attrptr->name ||
1995 attrptr->value_tag != IPP_TAG_LANGUAGE ||
1996 attrptr->group_tag != IPP_TAG_OPERATION ||
1997 attrptr->num_values != 1 ||
1998 strcmp(attrptr->name, "attributes-natural-language"))
1999 prev_pass = pass = 0;
2000 }
2001
2002 if ((attrptr = ippFindAttribute(response, "status-message",
2003 IPP_TAG_ZERO)) != NULL &&
2004 (attrptr->value_tag != IPP_TAG_TEXT ||
2005 attrptr->group_tag != IPP_TAG_OPERATION ||
2006 attrptr->num_values != 1 ||
2007 (attrptr->value_tag == IPP_TAG_TEXT &&
2008 strlen(attrptr->values[0].string.text) > 255)))
2009 prev_pass = pass = 0;
2010
2011 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
2012 IPP_TAG_ZERO)) != NULL &&
2013 (attrptr->value_tag != IPP_TAG_TEXT ||
2014 attrptr->group_tag != IPP_TAG_OPERATION ||
2015 attrptr->num_values != 1 ||
2016 (attrptr->value_tag == IPP_TAG_TEXT &&
2017 strlen(attrptr->values[0].string.text) > 1023)))
2018 prev_pass = pass = 0;
2019
2020 for (attrptr = response->attrs, group = attrptr->group_tag;
2021 attrptr;
2022 attrptr = attrptr->next)
2023 {
2024 if (attrptr->group_tag < group && attrptr->group_tag != IPP_TAG_ZERO)
2025 {
2026 prev_pass = pass = 0;
2027 break;
2028 }
2029
2030 if (!validate_attr(attrptr, 0))
2031 {
2032 prev_pass = pass = 0;
2033 break;
2034 }
2035 }
2036
2037 for (i = 0; i < num_statuses; i ++)
2038 {
2039 if (statuses[i].if_defined &&
2040 !get_variable(vars, statuses[i].if_defined))
2041 continue;
2042
2043 if (statuses[i].if_not_defined &&
2044 get_variable(vars, statuses[i].if_not_defined))
2045 continue;
2046
2047 if (response->request.status.status_code == statuses[i].status)
2048 break;
2049 }
2050
2051 if (i == num_statuses && num_statuses > 0)
2052 prev_pass = pass = 0;
2053 else
2054 {
2055 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2056 {
2057 if (expect->if_defined && !get_variable(vars, expect->if_defined))
2058 continue;
2059
2060 if (expect->if_not_defined &&
2061 get_variable(vars, expect->if_not_defined))
2062 continue;
2063
2064 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
2065
2066 if ((found && expect->not_expect) ||
2067 (!found && !(expect->not_expect || expect->optional)) ||
2068 (found && !expect_matches(expect, found->value_tag)) ||
2069 (found && expect->in_group &&
2070 found->group_tag != expect->in_group))
2071 {
2072 if (expect->define_no_match)
2073 set_variable(vars, expect->define_no_match, "1");
2074 else if (!expect->define_match)
2075 prev_pass = pass = 0;
2076
2077 continue;
2078 }
2079
2080 if (found &&
2081 !with_value(expect->with_value, expect->with_regex, found, 0))
2082 {
2083 if (expect->define_no_match)
2084 set_variable(vars, expect->define_no_match, "1");
2085 else if (!expect->define_match)
2086 prev_pass = pass = 0;
2087
2088 continue;
2089 }
2090
2091 if (found && expect->count > 0 && found->num_values != expect->count)
2092 {
2093 if (expect->define_no_match)
2094 set_variable(vars, expect->define_no_match, "1");
2095 else if (!expect->define_match)
2096 prev_pass = pass = 0;
2097
2098 continue;
2099 }
2100
2101 if (found && expect->same_count_as)
2102 {
2103 attrptr = ippFindAttribute(response, expect->same_count_as,
2104 IPP_TAG_ZERO);
2105
2106 if (!attrptr || attrptr->num_values != found->num_values)
2107 {
2108 if (expect->define_no_match)
2109 set_variable(vars, expect->define_no_match, "1");
2110 else if (!expect->define_match)
2111 prev_pass = pass = 0;
2112
2113 continue;
2114 }
2115 }
2116
2117 if (found && expect->define_match)
2118 set_variable(vars, expect->define_match, "1");
2119
2120 if (found && expect->define_value)
2121 {
2122 _ippAttrString(found, token, sizeof(token));
2123 set_variable(vars, expect->define_value, token);
2124 }
2125 }
2126 }
2127 }
2128
2129 if (Output == _CUPS_OUTPUT_PLIST)
2130 {
2131 puts("<key>Successful</key>");
2132 puts(prev_pass ? "<true />" : "<false />");
2133 puts("<key>StatusCode</key>");
2134 print_xml_string("string", ippErrorString(cupsLastError()));
2135 puts("<key>ResponseAttributes</key>");
2136 puts("<array>");
2137 puts("<dict>");
2138 for (attrptr = response ? response->attrs : NULL,
2139 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
2140 attrptr;
2141 attrptr = attrptr->next)
2142 print_attr(attrptr, &group);
2143 puts("</dict>");
2144 puts("</array>");
2145 }
2146 else if (Output == _CUPS_OUTPUT_TEST)
2147 {
2148 puts(prev_pass ? "PASS]" : "FAIL]");
2149
2150 if (Verbosity && response)
2151 {
2152 printf(" RECEIVED: %lu bytes in response\n",
2153 (unsigned long)ippLength(response));
2154 printf(" status-code = %x (%s)\n", cupsLastError(),
2155 ippErrorString(cupsLastError()));
2156
2157 for (attrptr = response->attrs;
2158 attrptr != NULL;
2159 attrptr = attrptr->next)
2160 {
2161 print_attr(attrptr, NULL);
2162 }
2163 }
2164 }
2165 else if (!prev_pass)
2166 fprintf(stderr, "%s\n", cupsLastErrorString());
2167
2168 if (prev_pass && Output != _CUPS_OUTPUT_PLIST &&
2169 Output != _CUPS_OUTPUT_QUIET && !Verbosity && num_displayed > 0)
2170 {
2171 if (Output >= _CUPS_OUTPUT_LIST)
2172 {
2173 size_t width; /* Length of value */
2174
2175
2176 for (i = 0; i < num_displayed; i ++)
2177 {
2178 widths[i] = strlen(displayed[i]);
2179
2180 for (attrptr = ippFindAttribute(response, displayed[i], IPP_TAG_ZERO);
2181 attrptr;
2182 attrptr = ippFindNextAttribute(response, displayed[i],
2183 IPP_TAG_ZERO))
2184 {
2185 width = _ippAttrString(attrptr, NULL, 0);
2186 if (width > widths[i])
2187 widths[i] = width;
2188 }
2189 }
2190
2191 if (Output == _CUPS_OUTPUT_CSV)
2192 print_csv(NULL, num_displayed, displayed, widths);
2193 else
2194 print_line(NULL, num_displayed, displayed, widths);
2195
2196 attrptr = response->attrs;
2197
2198 while (attrptr)
2199 {
2200 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
2201 attrptr = attrptr->next;
2202
2203 if (attrptr)
2204 {
2205 if (Output == _CUPS_OUTPUT_CSV)
2206 print_csv(attrptr, num_displayed, displayed, widths);
2207 else
2208 print_line(attrptr, num_displayed, displayed, widths);
2209
2210 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
2211 attrptr = attrptr->next;
2212 }
2213 }
2214 }
2215 else
2216 {
2217 for (attrptr = response->attrs;
2218 attrptr != NULL;
2219 attrptr = attrptr->next)
2220 {
2221 if (attrptr->name)
2222 {
2223 for (i = 0; i < num_displayed; i ++)
2224 {
2225 if (!strcmp(displayed[i], attrptr->name))
2226 {
2227 print_attr(attrptr, NULL);
2228 break;
2229 }
2230 }
2231 }
2232 }
2233 }
2234 }
2235 else if (!prev_pass)
2236 {
2237 if (Output == _CUPS_OUTPUT_PLIST)
2238 {
2239 puts("<key>Errors</key>");
2240 puts("<array>");
2241 }
2242
2243 if (http->version != HTTP_1_1)
2244 print_test_error("Bad HTTP version (%d.%d)", http->version / 100,
2245 http->version % 100);
2246
2247 if (!response)
2248 print_test_error("IPP request failed with status %s (%s)",
2249 ippErrorString(cupsLastError()),
2250 cupsLastErrorString());
2251 else
2252 {
2253 if (version &&
2254 (response->request.status.version[0] != (version / 10) ||
2255 response->request.status.version[1] != (version % 10)))
2256 print_test_error("Bad version %d.%d in response - expected %d.%d "
2257 "(RFC 2911 section 3.1.8).",
2258 response->request.status.version[0],
2259 response->request.status.version[1],
2260 version / 10, version % 10);
2261
2262 if (response->request.status.request_id != request_id)
2263 print_test_error("Bad request ID %d in response - expected %d "
2264 "(RFC 2911 section 3.1.1)",
2265 response->request.status.request_id, request_id);
2266
2267 attrptr = response->attrs;
2268 if (!attrptr)
2269 print_test_error("Missing first attribute \"attributes-charset "
2270 "(charset)\" in group operation-attributes-tag "
2271 "(RFC 2911 section 3.1.4).");
2272 else
2273 {
2274 if (!attrptr->name ||
2275 attrptr->value_tag != IPP_TAG_CHARSET ||
2276 attrptr->group_tag != IPP_TAG_OPERATION ||
2277 attrptr->num_values != 1 ||
2278 strcmp(attrptr->name, "attributes-charset"))
2279 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2280 "expected \"attributes-charset (charset)\" in "
2281 "group operation-attributes-tag (RFC 2911 section "
2282 "3.1.4).",
2283 attrptr->name ? attrptr->name : "(null)",
2284 attrptr->num_values > 1 ? "1setOf " : "",
2285 ippTagString(attrptr->value_tag),
2286 ippTagString(attrptr->group_tag));
2287
2288 attrptr = attrptr->next;
2289 if (!attrptr)
2290 print_test_error("Missing second attribute \"attributes-natural-"
2291 "language (naturalLanguage)\" in group "
2292 "operation-attributes-tag (RFC 2911 section "
2293 "3.1.4).");
2294 else if (!attrptr->name ||
2295 attrptr->value_tag != IPP_TAG_LANGUAGE ||
2296 attrptr->group_tag != IPP_TAG_OPERATION ||
2297 attrptr->num_values != 1 ||
2298 strcmp(attrptr->name, "attributes-natural-language"))
2299 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2300 "expected \"attributes-natural-language "
2301 "(naturalLanguage)\" in group "
2302 "operation-attributes-tag (RFC 2911 section "
2303 "3.1.4).",
2304 attrptr->name ? attrptr->name : "(null)",
2305 attrptr->num_values > 1 ? "1setOf " : "",
2306 ippTagString(attrptr->value_tag),
2307 ippTagString(attrptr->group_tag));
2308 }
2309
2310 if ((attrptr = ippFindAttribute(response, "status-message",
2311 IPP_TAG_ZERO)) != NULL)
2312 {
2313 if (attrptr->value_tag != IPP_TAG_TEXT)
2314 print_test_error("status-message (text(255)) has wrong value tag "
2315 "%s (RFC 2911 section 3.1.6.2).",
2316 ippTagString(attrptr->value_tag));
2317 if (attrptr->group_tag != IPP_TAG_OPERATION)
2318 print_test_error("status-message (text(255)) has wrong group tag "
2319 "%s (RFC 2911 section 3.1.6.2).",
2320 ippTagString(attrptr->group_tag));
2321 if (attrptr->num_values != 1)
2322 print_test_error("status-message (text(255)) has %d values "
2323 "(RFC 2911 section 3.1.6.2).",
2324 attrptr->num_values);
2325 if (attrptr->value_tag == IPP_TAG_TEXT &&
2326 strlen(attrptr->values[0].string.text) > 255)
2327 print_test_error("status-message (text(255)) has bad length %d"
2328 " (RFC 2911 section 3.1.6.2).",
2329 (int)strlen(attrptr->values[0].string.text));
2330 }
2331
2332 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
2333 IPP_TAG_ZERO)) != NULL)
2334 {
2335 if (attrptr->value_tag != IPP_TAG_TEXT)
2336 print_test_error("detailed-status-message (text(MAX)) has wrong "
2337 "value tag %s (RFC 2911 section 3.1.6.3).",
2338 ippTagString(attrptr->value_tag));
2339 if (attrptr->group_tag != IPP_TAG_OPERATION)
2340 print_test_error("detailed-status-message (text(MAX)) has wrong "
2341 "group tag %s (RFC 2911 section 3.1.6.3).",
2342 ippTagString(attrptr->group_tag));
2343 if (attrptr->num_values != 1)
2344 print_test_error("detailed-status-message (text(MAX)) has %d values"
2345 " (RFC 2911 section 3.1.6.3).",
2346 attrptr->num_values);
2347 if (attrptr->value_tag == IPP_TAG_TEXT &&
2348 strlen(attrptr->values[0].string.text) > 1023)
2349 print_test_error("detailed-status-message (text(MAX)) has bad "
2350 "length %d (RFC 2911 section 3.1.6.3).",
2351 (int)strlen(attrptr->values[0].string.text));
2352 }
2353
2354 for (attrptr = response->attrs, group = attrptr->group_tag;
2355 attrptr;
2356 attrptr = attrptr->next)
2357 {
2358 if (attrptr->group_tag < group && attrptr->group_tag != IPP_TAG_ZERO)
2359 print_test_error("Attribute groups out of order (%s < %s)",
2360 ippTagString(attrptr->group_tag),
2361 ippTagString(group));
2362
2363 validate_attr(attrptr, 1);
2364 }
2365
2366 for (i = 0; i < num_statuses; i ++)
2367 {
2368 if (statuses[i].if_defined &&
2369 !get_variable(vars, statuses[i].if_defined))
2370 continue;
2371
2372 if (statuses[i].if_not_defined &&
2373 get_variable(vars, statuses[i].if_not_defined))
2374 continue;
2375
2376 if (response->request.status.status_code == statuses[i].status)
2377 break;
2378 }
2379
2380 if (i == num_statuses && num_statuses > 0)
2381 {
2382 for (i = 0; i < num_statuses; i ++)
2383 {
2384 if (statuses[i].if_defined &&
2385 !get_variable(vars, statuses[i].if_defined))
2386 continue;
2387
2388 if (statuses[i].if_not_defined &&
2389 get_variable(vars, statuses[i].if_not_defined))
2390 continue;
2391
2392 print_test_error("EXPECTED: STATUS %s (got %s)",
2393 ippErrorString(statuses[i].status),
2394 ippErrorString(cupsLastError()));
2395 }
2396
2397 if ((attrptr = ippFindAttribute(response, "status-message",
2398 IPP_TAG_TEXT)) != NULL)
2399 print_test_error("status-message=\"%s\"",
2400 attrptr->values[0].string.text);
2401 }
2402
2403 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2404 {
2405 if (expect->define_match || expect->define_no_match)
2406 continue;
2407
2408 if (expect->if_defined && !get_variable(vars, expect->if_defined))
2409 continue;
2410
2411 if (expect->if_not_defined &&
2412 get_variable(vars, expect->if_not_defined))
2413 continue;
2414
2415 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
2416
2417 if (found && expect->not_expect)
2418 print_test_error("NOT EXPECTED: %s", expect->name);
2419 else if (!found && !(expect->not_expect || expect->optional))
2420 print_test_error("EXPECTED: %s", expect->name);
2421 else if (found)
2422 {
2423 if (!expect_matches(expect, found->value_tag))
2424 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
2425 expect->name, expect->of_type,
2426 ippTagString(found->value_tag));
2427
2428 if (expect->in_group && found->group_tag != expect->in_group)
2429 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
2430 expect->name, ippTagString(expect->in_group),
2431 ippTagString(found->group_tag));
2432
2433 if (!with_value(expect->with_value, expect->with_regex, found, 0))
2434 {
2435 if (expect->with_regex)
2436 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
2437 expect->name, expect->with_value);
2438 else
2439 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
2440 expect->name, expect->with_value);
2441
2442 with_value(expect->with_value, expect->with_regex, found, 1);
2443 }
2444
2445 if (expect->count > 0 && found->num_values != expect->count)
2446 {
2447 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect->name,
2448 expect->count, found->num_values);
2449 }
2450
2451 if (expect->same_count_as)
2452 {
2453 attrptr = ippFindAttribute(response, expect->same_count_as,
2454 IPP_TAG_ZERO);
2455
2456 if (!attrptr)
2457 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2458 "(not returned)", expect->name,
2459 found->num_values, expect->same_count_as);
2460 else if (attrptr->num_values != found->num_values)
2461 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2462 "(%d values)", expect->name, found->num_values,
2463 expect->same_count_as, attrptr->num_values);
2464 }
2465 }
2466 }
2467 }
2468
2469 if (Output == _CUPS_OUTPUT_PLIST)
2470 puts("</array>");
2471 }
2472
2473 if (Output == _CUPS_OUTPUT_PLIST)
2474 puts("</dict>");
2475
2476 skip_error:
2477
2478 ippDelete(response);
2479 response = NULL;
2480
2481 for (i = 0; i < num_statuses; i ++)
2482 {
2483 if (statuses[i].if_defined)
2484 free(statuses[i].if_defined);
2485 if (statuses[i].if_not_defined)
2486 free(statuses[i].if_not_defined);
2487 }
2488 num_statuses = 0;
2489
2490 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2491 {
2492 free(expect->name);
2493 if (expect->of_type)
2494 free(expect->of_type);
2495 if (expect->same_count_as)
2496 free(expect->same_count_as);
2497 if (expect->if_defined)
2498 free(expect->if_defined);
2499 if (expect->if_not_defined)
2500 free(expect->if_not_defined);
2501 if (expect->with_value)
2502 free(expect->with_value);
2503 if (expect->define_match)
2504 free(expect->define_match);
2505 if (expect->define_no_match)
2506 free(expect->define_no_match);
2507 if (expect->define_value)
2508 free(expect->define_value);
2509 }
2510 num_expects = 0;
2511
2512 for (i = 0; i < num_displayed; i ++)
2513 free(displayed[i]);
2514 num_displayed = 0;
2515
2516 if (!ignore_errors && !prev_pass)
2517 break;
2518 }
2519
2520 test_exit:
2521
2522 if (fp)
2523 fclose(fp);
2524
2525 httpClose(http);
2526 ippDelete(request);
2527 ippDelete(response);
2528
2529 for (i = 0; i < num_statuses; i ++)
2530 {
2531 if (statuses[i].if_defined)
2532 free(statuses[i].if_defined);
2533 if (statuses[i].if_not_defined)
2534 free(statuses[i].if_not_defined);
2535 }
2536
2537 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2538 {
2539 free(expect->name);
2540 if (expect->of_type)
2541 free(expect->of_type);
2542 if (expect->same_count_as)
2543 free(expect->same_count_as);
2544 if (expect->if_defined)
2545 free(expect->if_defined);
2546 if (expect->if_not_defined)
2547 free(expect->if_not_defined);
2548 if (expect->with_value)
2549 free(expect->with_value);
2550 if (expect->define_match)
2551 free(expect->define_match);
2552 if (expect->define_no_match)
2553 free(expect->define_no_match);
2554 if (expect->define_value)
2555 free(expect->define_value);
2556 }
2557
2558 for (i = 0; i < num_displayed; i ++)
2559 free(displayed[i]);
2560
2561 return (pass);
2562 }
2563
2564
2565 /*
2566 * 'expand_variables()' - Expand variables in a string.
2567 */
2568
2569 static void
2570 expand_variables(_cups_vars_t *vars, /* I - Variables */
2571 char *dst, /* I - Destination string buffer */
2572 const char *src, /* I - Source string */
2573 size_t dstsize) /* I - Size of destination buffer */
2574 {
2575 char *dstptr, /* Pointer into destination */
2576 *dstend, /* End of destination */
2577 temp[256], /* Temporary string */
2578 *tempptr; /* Pointer into temporary string */
2579 const char *value; /* Value to substitute */
2580
2581
2582 dstptr = dst;
2583 dstend = dst + dstsize - 1;
2584
2585 while (*src && dstptr < dstend)
2586 {
2587 if (*src == '$')
2588 {
2589 /*
2590 * Substitute a string/number...
2591 */
2592
2593 if (!strncmp(src, "$$", 2))
2594 {
2595 value = "$";
2596 src += 2;
2597 }
2598 else if (!strncmp(src, "$ENV[", 5))
2599 {
2600 strlcpy(temp, src + 5, sizeof(temp));
2601
2602 for (tempptr = temp; *tempptr; tempptr ++)
2603 if (*tempptr == ']')
2604 break;
2605
2606 if (*tempptr)
2607 *tempptr++ = '\0';
2608
2609 value = getenv(temp);
2610 src += tempptr - temp + 5;
2611 }
2612 else if (vars)
2613 {
2614 strlcpy(temp, src + 1, sizeof(temp));
2615
2616 for (tempptr = temp; *tempptr; tempptr ++)
2617 if (!isalnum(*tempptr & 255) && *tempptr != '-' && *tempptr != '_')
2618 break;
2619
2620 if (*tempptr)
2621 *tempptr = '\0';
2622
2623 if (!strcmp(temp, "uri"))
2624 value = vars->uri;
2625 else if (!strcmp(temp, "filename"))
2626 value = vars->filename;
2627 else if (!strcmp(temp, "scheme") || !strcmp(temp, "method"))
2628 value = vars->scheme;
2629 else if (!strcmp(temp, "username"))
2630 value = vars->userpass;
2631 else if (!strcmp(temp, "hostname"))
2632 value = vars->hostname;
2633 else if (!strcmp(temp, "port"))
2634 {
2635 snprintf(temp, sizeof(temp), "%d", vars->port);
2636 value = temp;
2637 }
2638 else if (!strcmp(temp, "resource"))
2639 value = vars->resource;
2640 else if (!strcmp(temp, "user"))
2641 value = cupsUser();
2642 else
2643 value = get_variable(vars, temp);
2644
2645 src += tempptr - temp + 1;
2646 }
2647 else
2648 {
2649 value = "$";
2650 src ++;
2651 }
2652
2653 if (value)
2654 {
2655 strlcpy(dstptr, value, dstend - dstptr + 1);
2656 dstptr += strlen(dstptr);
2657 }
2658 }
2659 else
2660 *dstptr++ = *src++;
2661 }
2662
2663 *dstptr = '\0';
2664 }
2665
2666
2667 /*
2668 * 'expect_matches()' - Return true if the tag matches the specification.
2669 */
2670
2671 static int /* O - 1 if matches, 0 otherwise */
2672 expect_matches(
2673 _cups_expect_t *expect, /* I - Expected attribute */
2674 ipp_tag_t value_tag) /* I - Value tag for attribute */
2675 {
2676 int match; /* Match? */
2677 char *of_type, /* Type name to match */
2678 *next, /* Next name to match */
2679 sep; /* Separator character */
2680
2681
2682 /*
2683 * If we don't expect a particular type, return immediately...
2684 */
2685
2686 if (!expect->of_type)
2687 return (1);
2688
2689 /*
2690 * Parse the "of_type" value since the string can contain multiple attribute
2691 * types separated by "," or "|"...
2692 */
2693
2694 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
2695 {
2696 /*
2697 * Find the next separator, and set it (temporarily) to nul if present.
2698 */
2699
2700 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
2701
2702 if ((sep = *next) != '\0')
2703 *next = '\0';
2704
2705 /*
2706 * Support some meta-types to make it easier to write the test file.
2707 */
2708
2709 if (!strcmp(of_type, "text"))
2710 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
2711 else if (!strcmp(of_type, "name"))
2712 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
2713 else if (!strcmp(of_type, "collection"))
2714 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
2715 else
2716 match = value_tag == ippTagValue(of_type);
2717
2718 /*
2719 * Restore the separator if we have one...
2720 */
2721
2722 if (sep)
2723 *next++ = sep;
2724 }
2725
2726 return (match);
2727 }
2728
2729
2730 /*
2731 * 'get_collection()' - Get a collection value from the current test file.
2732 */
2733
2734 static ipp_t * /* O - Collection value */
2735 get_collection(_cups_vars_t *vars, /* I - Variables */
2736 FILE *fp, /* I - File to read from */
2737 int *linenum) /* IO - Line number */
2738 {
2739 char token[1024], /* Token from file */
2740 temp[1024], /* Temporary string */
2741 attr[128]; /* Attribute name */
2742 ipp_tag_t value; /* Current value type */
2743 ipp_t *col = ippNew(); /* Collection value */
2744 ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
2745
2746
2747 while (get_token(fp, token, sizeof(token), linenum) != NULL)
2748 {
2749 if (!strcmp(token, "}"))
2750 break;
2751 else if (!strcmp(token, "{") && lastcol)
2752 {
2753 /*
2754 * Another collection value
2755 */
2756
2757 ipp_t *subcol = get_collection(vars, fp, linenum);
2758 /* Collection value */
2759
2760 if (subcol)
2761 {
2762 ipp_attribute_t *tempcol; /* Pointer to new buffer */
2763
2764
2765 /*
2766 * Reallocate memory...
2767 */
2768
2769 if ((tempcol = realloc(lastcol, sizeof(ipp_attribute_t) +
2770 (lastcol->num_values + 1) *
2771 sizeof(ipp_value_t))) == NULL)
2772 {
2773 print_fatal_error("Unable to allocate memory on line %d.", *linenum);
2774 goto col_error;
2775 }
2776
2777 if (tempcol != lastcol)
2778 {
2779 /*
2780 * Reset pointers in the list...
2781 */
2782
2783 if (col->prev)
2784 col->prev->next = tempcol;
2785 else
2786 col->attrs = tempcol;
2787
2788 lastcol = col->current = col->last = tempcol;
2789 }
2790
2791 lastcol->values[lastcol->num_values].collection = subcol;
2792 lastcol->num_values ++;
2793 }
2794 else
2795 goto col_error;
2796 }
2797 else if (!strcasecmp(token, "MEMBER"))
2798 {
2799 /*
2800 * Attribute...
2801 */
2802
2803 lastcol = NULL;
2804
2805 if (!get_token(fp, token, sizeof(token), linenum))
2806 {
2807 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum);
2808 goto col_error;
2809 }
2810
2811 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
2812 {
2813 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token,
2814 *linenum);
2815 goto col_error;
2816 }
2817
2818 if (!get_token(fp, attr, sizeof(attr), linenum))
2819 {
2820 print_fatal_error("Missing MEMBER name on line %d.", *linenum);
2821 goto col_error;
2822 }
2823
2824 if (!get_token(fp, temp, sizeof(temp), linenum))
2825 {
2826 print_fatal_error("Missing MEMBER value on line %d.", *linenum);
2827 goto col_error;
2828 }
2829
2830 expand_variables(vars, token, temp, sizeof(token));
2831
2832 switch (value)
2833 {
2834 case IPP_TAG_BOOLEAN :
2835 if (!strcasecmp(token, "true"))
2836 ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
2837 else
2838 ippAddBoolean(col, IPP_TAG_ZERO, attr, atoi(token));
2839 break;
2840
2841 case IPP_TAG_INTEGER :
2842 case IPP_TAG_ENUM :
2843 ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
2844 break;
2845
2846 case IPP_TAG_RESOLUTION :
2847 {
2848 int xres, /* X resolution */
2849 yres; /* Y resolution */
2850 char units[6]; /* Units */
2851
2852 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
2853 (strcasecmp(units, "dpi") && strcasecmp(units, "dpc") &&
2854 strcasecmp(units, "other")))
2855 {
2856 print_fatal_error("Bad resolution value \"%s\" on line %d.",
2857 token, *linenum);
2858 goto col_error;
2859 }
2860
2861 if (!strcasecmp(units, "dpi"))
2862 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2863 IPP_RES_PER_INCH);
2864 else if (!strcasecmp(units, "dpc"))
2865 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2866 IPP_RES_PER_CM);
2867 else
2868 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2869 (ipp_res_t)0);
2870 }
2871 break;
2872
2873 case IPP_TAG_RANGE :
2874 {
2875 int lowers[4], /* Lower value */
2876 uppers[4], /* Upper values */
2877 num_vals; /* Number of values */
2878
2879
2880 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
2881 lowers + 0, uppers + 0,
2882 lowers + 1, uppers + 1,
2883 lowers + 2, uppers + 2,
2884 lowers + 3, uppers + 3);
2885
2886 if ((num_vals & 1) || num_vals == 0)
2887 {
2888 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
2889 token, *linenum);
2890 goto col_error;
2891 }
2892
2893 ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
2894 uppers);
2895 }
2896 break;
2897
2898 case IPP_TAG_BEGIN_COLLECTION :
2899 if (!strcmp(token, "{"))
2900 {
2901 ipp_t *subcol = get_collection(vars, fp, linenum);
2902 /* Collection value */
2903
2904 if (subcol)
2905 {
2906 lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
2907 ippDelete(subcol);
2908 }
2909 else
2910 goto col_error;
2911 }
2912 else
2913 {
2914 print_fatal_error("Bad collection value on line %d.", *linenum);
2915 goto col_error;
2916 }
2917 break;
2918
2919 default :
2920 if (!strchr(token, ','))
2921 ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
2922 else
2923 {
2924 /*
2925 * Multiple string values...
2926 */
2927
2928 int num_values; /* Number of values */
2929 char *values[100], /* Values */
2930 *ptr; /* Pointer to next value */
2931
2932
2933 values[0] = token;
2934 num_values = 1;
2935
2936 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
2937 {
2938 *ptr++ = '\0';
2939 values[num_values] = ptr;
2940 num_values ++;
2941 }
2942
2943 ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
2944 NULL, (const char **)values);
2945 }
2946 break;
2947 }
2948 }
2949 }
2950
2951 return (col);
2952
2953 /*
2954 * If we get here there was a parse error; free memory and return.
2955 */
2956
2957 col_error:
2958
2959 ippDelete(col);
2960
2961 return (NULL);
2962 }
2963
2964
2965 /*
2966 * 'get_filename()' - Get a filename based on the current test file.
2967 */
2968
2969 static char * /* O - Filename */
2970 get_filename(const char *testfile, /* I - Current test file */
2971 char *dst, /* I - Destination filename */
2972 const char *src, /* I - Source filename */
2973 size_t dstsize) /* I - Size of destination buffer */
2974 {
2975 char *dstptr; /* Pointer into destination */
2976 _cups_globals_t *cg = _cupsGlobals();
2977 /* Global data */
2978
2979
2980 if (*src == '<' && src[strlen(src) - 1] == '>')
2981 {
2982 /*
2983 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2984 */
2985
2986 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
2987 dstptr = dst + strlen(dst) - 1;
2988 if (*dstptr == '>')
2989 *dstptr = '\0';
2990 }
2991 else if (*src == '/' || !strchr(testfile, '/'))
2992 {
2993 /*
2994 * Use the path as-is...
2995 */
2996
2997 strlcpy(dst, src, dstsize);
2998 }
2999 else
3000 {
3001 /*
3002 * Make path relative to testfile...
3003 */
3004
3005 strlcpy(dst, testfile, dstsize);
3006 if ((dstptr = strrchr(dst, '/')) != NULL)
3007 dstptr ++;
3008 else
3009 dstptr = dst; /* Should never happen */
3010
3011 strlcpy(dstptr, src, dstsize - (dstptr - dst));
3012 }
3013
3014 return (dst);
3015 }
3016
3017
3018 /*
3019 * 'get_token()' - Get a token from a file.
3020 */
3021
3022 static char * /* O - Token from file or NULL on EOF */
3023 get_token(FILE *fp, /* I - File to read from */
3024 char *buf, /* I - Buffer to read into */
3025 int buflen, /* I - Length of buffer */
3026 int *linenum) /* IO - Current line number */
3027 {
3028 int ch, /* Character from file */
3029 quote; /* Quoting character */
3030 char *bufptr, /* Pointer into buffer */
3031 *bufend; /* End of buffer */
3032
3033
3034 for (;;)
3035 {
3036 /*
3037 * Skip whitespace...
3038 */
3039
3040 while (isspace(ch = getc(fp)))
3041 {
3042 if (ch == '\n')
3043 (*linenum) ++;
3044 }
3045
3046 /*
3047 * Read a token...
3048 */
3049
3050 if (ch == EOF)
3051 return (NULL);
3052 else if (ch == '\'' || ch == '\"')
3053 {
3054 /*
3055 * Quoted text or regular expression...
3056 */
3057
3058 quote = ch;
3059 bufptr = buf;
3060 bufend = buf + buflen - 1;
3061
3062 while ((ch = getc(fp)) != EOF)
3063 {
3064 if (ch == '\\')
3065 {
3066 /*
3067 * Escape next character...
3068 */
3069
3070 if (bufptr < bufend)
3071 *bufptr++ = ch;
3072
3073 if ((ch = getc(fp)) != EOF && bufptr < bufend)
3074 *bufptr++ = ch;
3075 }
3076 else if (ch == quote)
3077 break;
3078 else if (bufptr < bufend)
3079 *bufptr++ = ch;
3080 }
3081
3082 *bufptr = '\0';
3083
3084 return (buf);
3085 }
3086 else if (ch == '#')
3087 {
3088 /*
3089 * Comment...
3090 */
3091
3092 while ((ch = getc(fp)) != EOF)
3093 if (ch == '\n')
3094 break;
3095
3096 (*linenum) ++;
3097 }
3098 else
3099 {
3100 /*
3101 * Whitespace delimited text...
3102 */
3103
3104 ungetc(ch, fp);
3105
3106 bufptr = buf;
3107 bufend = buf + buflen - 1;
3108
3109 while ((ch = getc(fp)) != EOF)
3110 if (isspace(ch) || ch == '#')
3111 break;
3112 else if (bufptr < bufend)
3113 *bufptr++ = ch;
3114
3115 if (ch == '#')
3116 ungetc(ch, fp);
3117 else if (ch == '\n')
3118 (*linenum) ++;
3119
3120 *bufptr = '\0';
3121
3122 return (buf);
3123 }
3124 }
3125 }
3126
3127
3128 /*
3129 * 'get_variable()' - Get the value of a variable.
3130 */
3131
3132 static char * /* O - Value or NULL */
3133 get_variable(_cups_vars_t *vars, /* I - Variables */
3134 const char *name) /* I - Variable name */
3135 {
3136 _cups_var_t key, /* Search key */
3137 *match; /* Matching variable, if any */
3138
3139
3140 key.name = (char *)name;
3141 match = cupsArrayFind(vars->vars, &key);
3142
3143 return (match ? match->value : NULL);
3144 }
3145
3146
3147 /*
3148 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3149 * value.
3150 */
3151
3152 static char * /* O - ISO 8601 date/time string */
3153 iso_date(ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
3154 {
3155 unsigned year = (date[0] << 8) + date[1];
3156 /* Year */
3157 static char buffer[255]; /* String buffer */
3158
3159
3160 if (date[9] == 0 && date[10] == 0)
3161 snprintf(buffer, sizeof(buffer), "%04u-%02u-%02uT%02u:%02u:%02uZ",
3162 year, date[2], date[3], date[4], date[5], date[6]);
3163 else
3164 snprintf(buffer, sizeof(buffer), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
3165 year, date[2], date[3], date[4], date[5], date[6],
3166 date[8], date[9], date[10]);
3167
3168 return (buffer);
3169 }
3170
3171
3172 /*
3173 * 'password_cb()' - Password callback for authenticated tests.
3174 */
3175
3176 static const char * /* O - Password */
3177 password_cb(const char *prompt) /* I - Prompt (unused) */
3178 {
3179 (void)prompt;
3180
3181 return (Password);
3182 }
3183
3184
3185 /*
3186 * 'print_attr()' - Print an attribute on the screen.
3187 */
3188
3189 static void
3190 print_attr(ipp_attribute_t *attr, /* I - Attribute to print */
3191 ipp_tag_t *group) /* IO - Current group */
3192 {
3193 int i; /* Looping var */
3194 ipp_attribute_t *colattr; /* Collection attribute */
3195
3196
3197 if (Output == _CUPS_OUTPUT_PLIST)
3198 {
3199 if (!attr->name || (group && *group != attr->group_tag))
3200 {
3201 puts("</dict>");
3202 puts("<dict>");
3203
3204 if (group)
3205 *group = attr->group_tag;
3206 }
3207
3208 if (!attr->name)
3209 return;
3210
3211 print_xml_string("key", attr->name);
3212 if (attr->num_values > 1)
3213 puts("<array>");
3214 }
3215 else if (Output == _CUPS_OUTPUT_TEST)
3216 {
3217 if (!attr->name)
3218 {
3219 puts(" -- separator --");
3220 return;
3221 }
3222
3223 printf(" %s (%s%s) = ", attr->name,
3224 attr->num_values > 1 ? "1setOf " : "",
3225 ippTagString(attr->value_tag));
3226 }
3227
3228 switch (attr->value_tag)
3229 {
3230 case IPP_TAG_INTEGER :
3231 case IPP_TAG_ENUM :
3232 for (i = 0; i < attr->num_values; i ++)
3233 if (Output == _CUPS_OUTPUT_PLIST)
3234 printf("<integer>%d</integer>\n", attr->values[i].integer);
3235 else
3236 printf("%d ", attr->values[i].integer);
3237 break;
3238
3239 case IPP_TAG_BOOLEAN :
3240 for (i = 0; i < attr->num_values; i ++)
3241 if (Output == _CUPS_OUTPUT_PLIST)
3242 puts(attr->values[i].boolean ? "<true />" : "<false />");
3243 else if (attr->values[i].boolean)
3244 fputs("true ", stdout);
3245 else
3246 fputs("false ", stdout);
3247 break;
3248
3249 case IPP_TAG_RANGE :
3250 for (i = 0; i < attr->num_values; i ++)
3251 if (Output == _CUPS_OUTPUT_PLIST)
3252 printf("<dict><key>lower</key><integer>%d</integer>"
3253 "<key>upper</key><integer>%d</integer></dict>\n",
3254 attr->values[i].range.lower, attr->values[i].range.upper);
3255 else
3256 printf("%d-%d ", attr->values[i].range.lower,
3257 attr->values[i].range.upper);
3258 break;
3259
3260 case IPP_TAG_RESOLUTION :
3261 for (i = 0; i < attr->num_values; i ++)
3262 if (Output == _CUPS_OUTPUT_PLIST)
3263 printf("<dict><key>xres</key><integer>%d</integer>"
3264 "<key>yres</key><integer>%d</integer>"
3265 "<key>units</key><string>%s</string></dict>\n",
3266 attr->values[i].resolution.xres,
3267 attr->values[i].resolution.yres,
3268 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3269 "dpi" : "dpc");
3270 else
3271 printf("%dx%d%s ", attr->values[i].resolution.xres,
3272 attr->values[i].resolution.yres,
3273 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3274 "dpi" : "dpc");
3275 break;
3276
3277 case IPP_TAG_DATE :
3278 for (i = 0; i < attr->num_values; i ++)
3279 if (Output == _CUPS_OUTPUT_PLIST)
3280 printf("<date>%s</date>\n", iso_date(attr->values[i].date));
3281 else
3282 printf("%s ", iso_date(attr->values[i].date));
3283 break;
3284
3285 case IPP_TAG_STRING :
3286 case IPP_TAG_TEXT :
3287 case IPP_TAG_NAME :
3288 case IPP_TAG_KEYWORD :
3289 case IPP_TAG_CHARSET :
3290 case IPP_TAG_URI :
3291 case IPP_TAG_MIMETYPE :
3292 case IPP_TAG_LANGUAGE :
3293 for (i = 0; i < attr->num_values; i ++)
3294 if (Output == _CUPS_OUTPUT_PLIST)
3295 print_xml_string("string", attr->values[i].string.text);
3296 else
3297 printf("\"%s\" ", attr->values[i].string.text);
3298 break;
3299
3300 case IPP_TAG_TEXTLANG :
3301 case IPP_TAG_NAMELANG :
3302 for (i = 0; i < attr->num_values; i ++)
3303 if (Output == _CUPS_OUTPUT_PLIST)
3304 {
3305 fputs("<dict><key>language</key><string>", stdout);
3306 print_xml_string(NULL, attr->values[i].string.charset);
3307 fputs("</string><key>string</key><string>", stdout);
3308 print_xml_string(NULL, attr->values[i].string.text);
3309 puts("</string></dict>");
3310 }
3311 else
3312 printf("\"%s\"(%s) ", attr->values[i].string.text,
3313 attr->values[i].string.charset);
3314 break;
3315
3316 case IPP_TAG_BEGIN_COLLECTION :
3317 for (i = 0; i < attr->num_values; i ++)
3318 {
3319 if (Output == _CUPS_OUTPUT_PLIST)
3320 {
3321 puts("<dict>");
3322 for (colattr = attr->values[i].collection->attrs;
3323 colattr;
3324 colattr = colattr->next)
3325 print_attr(colattr, NULL);
3326 puts("</dict>");
3327 }
3328 else
3329 {
3330 if (i)
3331 putchar(' ');
3332
3333 print_col(attr->values[i].collection);
3334 }
3335 }
3336 break;
3337
3338 default :
3339 if (Output == _CUPS_OUTPUT_PLIST)
3340 printf("<string>&lt;&lt;%s&gt;&gt;</string>\n",
3341 ippTagString(attr->value_tag));
3342 else
3343 fputs(ippTagString(attr->value_tag), stdout);
3344 break;
3345 }
3346
3347 if (Output == _CUPS_OUTPUT_PLIST)
3348 {
3349 if (attr->num_values > 1)
3350 puts("</array>");
3351 }
3352 else
3353 putchar('\n');
3354 }
3355
3356
3357 /*
3358 * 'print_col()' - Print a collection attribute on the screen.
3359 */
3360
3361 static void
3362 print_col(ipp_t *col) /* I - Collection attribute to print */
3363 {
3364 int i; /* Looping var */
3365 ipp_attribute_t *attr; /* Current attribute in collection */
3366
3367
3368 fputs("{ ", stdout);
3369 for (attr = col->attrs; attr; attr = attr->next)
3370 {
3371 printf("%s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "",
3372 ippTagString(attr->value_tag));
3373
3374 switch (attr->value_tag)
3375 {
3376 case IPP_TAG_INTEGER :
3377 case IPP_TAG_ENUM :
3378 for (i = 0; i < attr->num_values; i ++)
3379 printf("%d ", attr->values[i].integer);
3380 break;
3381
3382 case IPP_TAG_BOOLEAN :
3383 for (i = 0; i < attr->num_values; i ++)
3384 if (attr->values[i].boolean)
3385 printf("true ");
3386 else
3387 printf("false ");
3388 break;
3389
3390 case IPP_TAG_NOVALUE :
3391 printf("novalue");
3392 break;
3393
3394 case IPP_TAG_RANGE :
3395 for (i = 0; i < attr->num_values; i ++)
3396 printf("%d-%d ", attr->values[i].range.lower,
3397 attr->values[i].range.upper);
3398 break;
3399
3400 case IPP_TAG_RESOLUTION :
3401 for (i = 0; i < attr->num_values; i ++)
3402 printf("%dx%d%s ", attr->values[i].resolution.xres,
3403 attr->values[i].resolution.yres,
3404 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3405 "dpi" : "dpc");
3406 break;
3407
3408 case IPP_TAG_STRING :
3409 case IPP_TAG_TEXT :
3410 case IPP_TAG_NAME :
3411 case IPP_TAG_KEYWORD :
3412 case IPP_TAG_CHARSET :
3413 case IPP_TAG_URI :
3414 case IPP_TAG_MIMETYPE :
3415 case IPP_TAG_LANGUAGE :
3416 for (i = 0; i < attr->num_values; i ++)
3417 printf("\"%s\" ", attr->values[i].string.text);
3418 break;
3419
3420 case IPP_TAG_TEXTLANG :
3421 case IPP_TAG_NAMELANG :
3422 for (i = 0; i < attr->num_values; i ++)
3423 printf("\"%s\",%s ", attr->values[i].string.text,
3424 attr->values[i].string.charset);
3425 break;
3426
3427 case IPP_TAG_BEGIN_COLLECTION :
3428 for (i = 0; i < attr->num_values; i ++)
3429 {
3430 print_col(attr->values[i].collection);
3431 putchar(' ');
3432 }
3433 break;
3434
3435 default :
3436 break; /* anti-compiler-warning-code */
3437 }
3438 }
3439
3440 putchar('}');
3441 }
3442
3443
3444 /*
3445 * 'print_csv()' - Print a line of CSV text.
3446 */
3447
3448 static void
3449 print_csv(
3450 ipp_attribute_t *attr, /* I - First attribute for line */
3451 int num_displayed, /* I - Number of attributes to display */
3452 char **displayed, /* I - Attributes to display */
3453 size_t *widths) /* I - Column widths */
3454 {
3455 int i; /* Looping var */
3456 size_t maxlength; /* Max length of all columns */
3457 char *buffer, /* String buffer */
3458 *bufptr; /* Pointer into buffer */
3459 ipp_attribute_t *current; /* Current attribute */
3460
3461
3462 /*
3463 * Get the maximum string length we have to show and allocate...
3464 */
3465
3466 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3467 if (widths[i] > maxlength)
3468 maxlength = widths[i];
3469
3470 maxlength += 2;
3471
3472 if ((buffer = malloc(maxlength)) == NULL)
3473 return;
3474
3475 /*
3476 * Loop through the attributes to display...
3477 */
3478
3479 if (attr)
3480 {
3481 for (i = 0; i < num_displayed; i ++)
3482 {
3483 if (i)
3484 putchar(',');
3485
3486 buffer[0] = '\0';
3487
3488 for (current = attr; current; current = current->next)
3489 {
3490 if (!current->name)
3491 break;
3492 else if (!strcmp(current->name, displayed[i]))
3493 {
3494 _ippAttrString(current, buffer, maxlength);
3495 break;
3496 }
3497 }
3498
3499 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
3500 strchr(buffer, '\\') != NULL)
3501 {
3502 putchar('\"');
3503 for (bufptr = buffer; *bufptr; bufptr ++)
3504 {
3505 if (*bufptr == '\\' || *bufptr == '\"')
3506 putchar('\\');
3507 putchar(*bufptr);
3508 }
3509 putchar('\"');
3510 }
3511 else
3512 fputs(buffer, stdout);
3513 }
3514 putchar('\n');
3515 }
3516 else
3517 {
3518 for (i = 0; i < num_displayed; i ++)
3519 {
3520 if (i)
3521 putchar(',');
3522
3523 fputs(displayed[i], stdout);
3524 }
3525 putchar('\n');
3526 }
3527
3528 free(buffer);
3529 }
3530
3531
3532 /*
3533 * 'print_fatal_error()' - Print a fatal error message.
3534 */
3535
3536 static void
3537 print_fatal_error(const char *s, /* I - Printf-style format string */
3538 ...) /* I - Additional arguments as needed */
3539 {
3540 char buffer[10240]; /* Format buffer */
3541 va_list ap; /* Pointer to arguments */
3542
3543
3544 /*
3545 * Format the error message...
3546 */
3547
3548 va_start(ap, s);
3549 vsnprintf(buffer, sizeof(buffer), s, ap);
3550 va_end(ap);
3551
3552 /*
3553 * Then output it...
3554 */
3555
3556 if (Output == _CUPS_OUTPUT_PLIST)
3557 {
3558 print_xml_header();
3559 print_xml_trailer(0, buffer);
3560 }
3561 else
3562 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
3563 }
3564
3565
3566 /*
3567 * 'print_line()' - Print a line of formatted or CSV text.
3568 */
3569
3570 static void
3571 print_line(
3572 ipp_attribute_t *attr, /* I - First attribute for line */
3573 int num_displayed, /* I - Number of attributes to display */
3574 char **displayed, /* I - Attributes to display */
3575 size_t *widths) /* I - Column widths */
3576 {
3577 int i; /* Looping var */
3578 size_t maxlength; /* Max length of all columns */
3579 char *buffer; /* String buffer */
3580 ipp_attribute_t *current; /* Current attribute */
3581
3582
3583 /*
3584 * Get the maximum string length we have to show and allocate...
3585 */
3586
3587 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3588 if (widths[i] > maxlength)
3589 maxlength = widths[i];
3590
3591 maxlength += 2;
3592
3593 if ((buffer = malloc(maxlength)) == NULL)
3594 return;
3595
3596 /*
3597 * Loop through the attributes to display...
3598 */
3599
3600 if (attr)
3601 {
3602 for (i = 0; i < num_displayed; i ++)
3603 {
3604 if (i)
3605 putchar(' ');
3606
3607 buffer[0] = '\0';
3608
3609 for (current = attr; current; current = current->next)
3610 {
3611 if (!current->name)
3612 break;
3613 else if (!strcmp(current->name, displayed[i]))
3614 {
3615 _ippAttrString(current, buffer, maxlength);
3616 break;
3617 }
3618 }
3619
3620 printf("%*s", (int)-widths[i], buffer);
3621 }
3622 putchar('\n');
3623 }
3624 else
3625 {
3626 for (i = 0; i < num_displayed; i ++)
3627 {
3628 if (i)
3629 putchar(' ');
3630
3631 printf("%*s", (int)-widths[i], displayed[i]);
3632 }
3633 putchar('\n');
3634
3635 for (i = 0; i < num_displayed; i ++)
3636 {
3637 if (i)
3638 putchar(' ');
3639
3640 memset(buffer, '-', widths[i]);
3641 buffer[widths[i]] = '\0';
3642 fputs(buffer, stdout);
3643 }
3644 putchar('\n');
3645 }
3646
3647 free(buffer);
3648 }
3649
3650
3651 /*
3652 * 'print_test_error()' - Print a test error message.
3653 */
3654
3655 static void
3656 print_test_error(const char *s, /* I - Printf-style format string */
3657 ...) /* I - Additional arguments as needed */
3658 {
3659 char buffer[10240]; /* Format buffer */
3660 va_list ap; /* Pointer to arguments */
3661
3662
3663 /*
3664 * Format the error message...
3665 */
3666
3667 va_start(ap, s);
3668 vsnprintf(buffer, sizeof(buffer), s, ap);
3669 va_end(ap);
3670
3671 /*
3672 * Then output it...
3673 */
3674
3675 if (Output == _CUPS_OUTPUT_PLIST)
3676 print_xml_string("string", buffer);
3677 else
3678 printf(" %s\n", buffer);
3679 }
3680
3681
3682 /*
3683 * 'print_xml_header()' - Print a standard XML plist header.
3684 */
3685
3686 static void
3687 print_xml_header(void)
3688 {
3689 if (!XMLHeader)
3690 {
3691 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
3692 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
3693 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
3694 puts("<plist version=\"1.0\">");
3695 puts("<dict>");
3696 puts("<key>Transfer</key>");
3697 printf("<string>%s</string>\n",
3698 Transfer == _CUPS_TRANSFER_AUTO ? "auto" :
3699 Transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
3700 puts("<key>Tests</key>");
3701 puts("<array>");
3702
3703 XMLHeader = 1;
3704 }
3705 }
3706
3707
3708 /*
3709 * 'print_xml_string()' - Print an XML string with escaping.
3710 */
3711
3712 static void
3713 print_xml_string(const char *element, /* I - Element name or NULL */
3714 const char *s) /* I - String to print */
3715 {
3716 if (element)
3717 printf("<%s>", element);
3718
3719 while (*s)
3720 {
3721 if (*s == '&')
3722 fputs("&amp;", stdout);
3723 else if (*s == '<')
3724 fputs("&lt;", stdout);
3725 else if (*s == '>')
3726 fputs("&gt;", stdout);
3727 else
3728 putchar(*s);
3729
3730 s ++;
3731 }
3732
3733 if (element)
3734 printf("</%s>\n", element);
3735 }
3736
3737
3738 /*
3739 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3740 */
3741
3742 static void
3743 print_xml_trailer(int success, /* I - 1 on success, 0 on failure */
3744 const char *message) /* I - Error message or NULL */
3745 {
3746 if (XMLHeader)
3747 {
3748 puts("</array>");
3749 puts("<key>Successful</key>");
3750 puts(success ? "<true />" : "<false />");
3751 if (message)
3752 {
3753 puts("<key>ErrorMessage</key>");
3754 print_xml_string("string", message);
3755 }
3756 puts("</dict>");
3757 puts("</plist>");
3758
3759 XMLHeader = 0;
3760 }
3761 }
3762
3763
3764 /*
3765 * 'set_variable()' - Set a variable value.
3766 */
3767
3768 static void
3769 set_variable(_cups_vars_t *vars, /* I - Variables */
3770 const char *name, /* I - Variable name */
3771 const char *value) /* I - Value string */
3772 {
3773 _cups_var_t key, /* Search key */
3774 *var; /* New variable */
3775
3776
3777 key.name = (char *)name;
3778 if ((var = cupsArrayFind(vars->vars, &key)) != NULL)
3779 {
3780 free(var->value);
3781 var->value = strdup(value);
3782 }
3783 else if ((var = malloc(sizeof(_cups_var_t))) == NULL)
3784 {
3785 print_fatal_error("Unable to allocate memory for variable \"%s\".", name);
3786 exit(1);
3787 }
3788 else
3789 {
3790 var->name = strdup(name);
3791 var->value = strdup(value);
3792
3793 cupsArrayAdd(vars->vars, var);
3794 }
3795 }
3796
3797
3798 /*
3799 * 'timeout_cb()' - Handle HTTP timeouts.
3800 */
3801
3802 static int /* O - 1 to continue, 0 to cancel */
3803 timeout_cb(http_t *http, /* I - Connection to server (unused) */
3804 void *user_data) /* I - User data (unused) */
3805 {
3806 (void)http;
3807 (void)user_data;
3808
3809 return (0);
3810 }
3811
3812
3813 /*
3814 * 'usage()' - Show program usage.
3815 */
3816
3817 static void
3818 usage(void)
3819 {
3820 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... "
3821 "filenameN ]"));
3822 _cupsLangPuts(stderr, _("Options:"));
3823 _cupsLangPuts(stderr, _(" -4 Connect using IPv4."));
3824 _cupsLangPuts(stderr, _(" -6 Connect using IPv6."));
3825 _cupsLangPuts(stderr, _(" -C Send requests using chunking "
3826 "(default)."));
3827 _cupsLangPuts(stderr, _(" -E Test with TLS encryption."));
3828 _cupsLangPuts(stderr, _(" -I Ignore errors."));
3829 _cupsLangPuts(stderr, _(" -L Send requests using "
3830 "content-length."));
3831 _cupsLangPuts(stderr, _(" -S Test with SSL encryption."));
3832 _cupsLangPuts(stderr, _(" -T Set the receive/send timeout in "
3833 "seconds."));
3834 _cupsLangPuts(stderr, _(" -V version Set default IPP version."));
3835 _cupsLangPuts(stderr, _(" -X Produce XML plist instead of plain "
3836 "text."));
3837 _cupsLangPuts(stderr, _(" -d name=value Define variable."));
3838 _cupsLangPuts(stderr, _(" -f filename Set default request filename."));
3839 _cupsLangPuts(stderr, _(" -i seconds Repeat the last file with the "
3840 "given time interval."));
3841 _cupsLangPuts(stderr, _(" -n count Repeat the last file the given "
3842 "number of times."));
3843 _cupsLangPuts(stderr, _(" -q Be quiet - no output except "
3844 "errors."));
3845 _cupsLangPuts(stderr, _(" -t Produce a test report."));
3846 _cupsLangPuts(stderr, _(" -v Show all attributes sent and "
3847 "received."));
3848
3849 exit(1);
3850 }
3851
3852
3853 /*
3854 * 'validate_attr()' - Determine whether an attribute is valid.
3855 */
3856
3857 static int /* O - 1 if valid, 0 otherwise */
3858 validate_attr(ipp_attribute_t *attr, /* I - Attribute to validate */
3859 int print) /* I - 1 = report issues to stdout */
3860 {
3861 int i; /* Looping var */
3862 char scheme[64], /* Scheme from URI */
3863 userpass[256], /* Username/password from URI */
3864 hostname[256], /* Hostname from URI */
3865 resource[1024]; /* Resource from URI */
3866 int port, /* Port number from URI */
3867 uri_status, /* URI separation status */
3868 valid = 1; /* Is the attribute valid? */
3869 const char *ptr; /* Pointer into string */
3870 ipp_attribute_t *colattr; /* Collection attribute */
3871 regex_t re; /* Regular expression */
3872 ipp_uchar_t *date; /* Current date value */
3873
3874
3875 /*
3876 * Skip separators.
3877 */
3878
3879 if (!attr->name)
3880 return (1);
3881
3882 /*
3883 * Validate the attribute name.
3884 */
3885
3886 for (ptr = attr->name; *ptr; ptr ++)
3887 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
3888 break;
3889
3890 if (*ptr || ptr == attr->name)
3891 {
3892 valid = 0;
3893
3894 if (print)
3895 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
3896 "2911 section 4.1.3).", attr->name);
3897 }
3898
3899 if ((ptr - attr->name) > 255)
3900 {
3901 valid = 0;
3902
3903 if (print)
3904 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
3905 "section 4.1.3).", attr->name);
3906 }
3907
3908 switch (attr->value_tag)
3909 {
3910 case IPP_TAG_INTEGER :
3911 break;
3912
3913 case IPP_TAG_BOOLEAN :
3914 for (i = 0; i < attr->num_values; i ++)
3915 {
3916 if (attr->values[i].boolean != 0 &&
3917 attr->values[i].boolean != 1)
3918 {
3919 valid = 0;
3920
3921 if (print)
3922 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
3923 "4.1.10).", attr->name, attr->values[i].boolean);
3924 else
3925 break;
3926 }
3927 }
3928 break;
3929
3930 case IPP_TAG_ENUM :
3931 for (i = 0; i < attr->num_values; i ++)
3932 {
3933 if (attr->values[i].integer < 1)
3934 {
3935 valid = 0;
3936
3937 if (print)
3938 print_test_error("\"%s\": Bad enum value %d - out of range "
3939 "(RFC 2911 section 4.1.4).", attr->name,
3940 attr->values[i].integer);
3941 else
3942 break;
3943 }
3944 }
3945 break;
3946
3947 case IPP_TAG_STRING :
3948 for (i = 0; i < attr->num_values; i ++)
3949 {
3950 if (attr->values[i].unknown.length > 1023)
3951 {
3952 valid = 0;
3953
3954 if (print)
3955 print_test_error("\"%s\": Bad octetString value - bad length %d "
3956 "(RFC 2911 section 4.1.10).", attr->name,
3957 attr->values[i].unknown.length);
3958 else
3959 break;
3960 }
3961 }
3962 break;
3963
3964 case IPP_TAG_DATE :
3965 for (i = 0; i < attr->num_values; i ++)
3966 {
3967 date = attr->values[i].date;
3968
3969 if (date[2] < 1 || date[2] > 12)
3970 {
3971 valid = 0;
3972
3973 if (print)
3974 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
3975 "section 4.1.13).", attr->name, date[2]);
3976 else
3977 break;
3978 }
3979
3980 if (date[3] < 1 || date[3] > 31)
3981 {
3982 valid = 0;
3983
3984 if (print)
3985 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
3986 "section 4.1.13).", attr->name, date[3]);
3987 else
3988 break;
3989 }
3990
3991 if (date[4] > 23)
3992 {
3993 valid = 0;
3994
3995 if (print)
3996 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
3997 "section 4.1.13).", attr->name, date[4]);
3998 else
3999 break;
4000 }
4001
4002 if (date[5] > 59)
4003 {
4004 valid = 0;
4005
4006 if (print)
4007 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
4008 "section 4.1.13).", attr->name, date[5]);
4009 else
4010 break;
4011 }
4012
4013 if (date[6] > 60)
4014 {
4015 valid = 0;
4016
4017 if (print)
4018 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
4019 "section 4.1.13).", attr->name, date[6]);
4020 else
4021 break;
4022 }
4023
4024 if (date[7] > 9)
4025 {
4026 valid = 0;
4027
4028 if (print)
4029 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
4030 "section 4.1.13).", attr->name, date[7]);
4031 else
4032 break;
4033 }
4034
4035 if (date[8] != '-' && date[8] != '+')
4036 {
4037 valid = 0;
4038
4039 if (print)
4040 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
4041 "section 4.1.13).", attr->name, date[8]);
4042 else
4043 break;
4044 }
4045
4046 if (date[9] > 11)
4047 {
4048 valid = 0;
4049
4050 if (print)
4051 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
4052 "section 4.1.13).", attr->name, date[9]);
4053 else
4054 break;
4055 }
4056
4057 if (date[10] > 59)
4058 {
4059 valid = 0;
4060
4061 if (print)
4062 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
4063 "section 4.1.13).", attr->name, date[10]);
4064 else
4065 break;
4066 }
4067 }
4068 break;
4069
4070 case IPP_TAG_RESOLUTION :
4071 for (i = 0; i < attr->num_values; i ++)
4072 {
4073 if (attr->values[i].resolution.xres <= 0)
4074 {
4075 valid = 0;
4076
4077 if (print)
4078 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
4079 "feed resolution must be positive (RFC 2911 "
4080 "section 4.1.13).", attr->name,
4081 attr->values[i].resolution.xres,
4082 attr->values[i].resolution.yres,
4083 attr->values[i].resolution.units ==
4084 IPP_RES_PER_INCH ? "dpi" :
4085 attr->values[i].resolution.units ==
4086 IPP_RES_PER_CM ? "dpc" : "unknown");
4087 else
4088 break;
4089 }
4090
4091 if (attr->values[i].resolution.yres <= 0)
4092 {
4093 valid = 0;
4094
4095 if (print)
4096 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
4097 "resolution must be positive (RFC 2911 section "
4098 "4.1.13).", attr->name,
4099 attr->values[i].resolution.xres,
4100 attr->values[i].resolution.yres,
4101 attr->values[i].resolution.units ==
4102 IPP_RES_PER_INCH ? "dpi" :
4103 attr->values[i].resolution.units ==
4104 IPP_RES_PER_CM ? "dpc" : "unknown");
4105 else
4106 break;
4107 }
4108
4109 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
4110 attr->values[i].resolution.units != IPP_RES_PER_CM)
4111 {
4112 valid = 0;
4113
4114 if (print)
4115 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
4116 "units value (RFC 2911 section 4.1.13).",
4117 attr->name, attr->values[i].resolution.xres,
4118 attr->values[i].resolution.yres,
4119 attr->values[i].resolution.units ==
4120 IPP_RES_PER_INCH ? "dpi" :
4121 attr->values[i].resolution.units ==
4122 IPP_RES_PER_CM ? "dpc" : "unknown");
4123 else
4124 break;
4125 }
4126 }
4127 break;
4128
4129 case IPP_TAG_RANGE :
4130 for (i = 0; i < attr->num_values; i ++)
4131 {
4132 if (attr->values[i].range.lower > attr->values[i].range.upper)
4133 {
4134 valid = 0;
4135
4136 if (print)
4137 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
4138 "greater than upper (RFC 2911 section 4.1.13).",
4139 attr->name, attr->values[i].range.lower,
4140 attr->values[i].range.upper);
4141 else
4142 break;
4143 }
4144 }
4145 break;
4146
4147 case IPP_TAG_BEGIN_COLLECTION :
4148 for (i = 0; i < attr->num_values; i ++)
4149 {
4150 for (colattr = attr->values[i].collection->attrs;
4151 colattr;
4152 colattr = colattr->next)
4153 {
4154 if (!validate_attr(colattr, 0))
4155 {
4156 valid = 0;
4157 break;
4158 }
4159 }
4160
4161 if (colattr && print)
4162 {
4163 print_test_error("\"%s\": Bad collection value.", attr->name);
4164
4165 while (colattr)
4166 {
4167 validate_attr(colattr, print);
4168 colattr = colattr->next;
4169 }
4170 }
4171 }
4172 break;
4173
4174 case IPP_TAG_TEXT :
4175 case IPP_TAG_TEXTLANG :
4176 for (i = 0; i < attr->num_values; i ++)
4177 {
4178 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4179 {
4180 if ((*ptr & 0xe0) == 0xc0)
4181 {
4182 ptr ++;
4183 if ((*ptr & 0xc0) != 0x80)
4184 break;
4185 }
4186 else if ((*ptr & 0xf0) == 0xe0)
4187 {
4188 ptr ++;
4189 if ((*ptr & 0xc0) != 0x80)
4190 break;
4191 ptr ++;
4192 if ((*ptr & 0xc0) != 0x80)
4193 break;
4194 }
4195 else if ((*ptr & 0xf8) == 0xf0)
4196 {
4197 ptr ++;
4198 if ((*ptr & 0xc0) != 0x80)
4199 break;
4200 ptr ++;
4201 if ((*ptr & 0xc0) != 0x80)
4202 break;
4203 ptr ++;
4204 if ((*ptr & 0xc0) != 0x80)
4205 break;
4206 }
4207 else if (*ptr & 0x80)
4208 break;
4209 }
4210
4211 if (*ptr)
4212 {
4213 valid = 0;
4214
4215 if (print)
4216 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
4217 "sequence (RFC 2911 section 4.1.1).", attr->name,
4218 attr->values[i].string.text);
4219 else
4220 break;
4221 }
4222
4223 if ((ptr - attr->values[i].string.text) > 1023)
4224 {
4225 valid = 0;
4226
4227 if (print)
4228 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
4229 "(RFC 2911 section 4.1.1).", attr->name,
4230 attr->values[i].string.text,
4231 (int)strlen(attr->values[i].string.text));
4232 else
4233 break;
4234 }
4235 }
4236 break;
4237
4238 case IPP_TAG_NAME :
4239 case IPP_TAG_NAMELANG :
4240 for (i = 0; i < attr->num_values; i ++)
4241 {
4242 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4243 {
4244 if ((*ptr & 0xe0) == 0xc0)
4245 {
4246 ptr ++;
4247 if ((*ptr & 0xc0) != 0x80)
4248 break;
4249 }
4250 else if ((*ptr & 0xf0) == 0xe0)
4251 {
4252 ptr ++;
4253 if ((*ptr & 0xc0) != 0x80)
4254 break;
4255 ptr ++;
4256 if ((*ptr & 0xc0) != 0x80)
4257 break;
4258 }
4259 else if ((*ptr & 0xf8) == 0xf0)
4260 {
4261 ptr ++;
4262 if ((*ptr & 0xc0) != 0x80)
4263 break;
4264 ptr ++;
4265 if ((*ptr & 0xc0) != 0x80)
4266 break;
4267 ptr ++;
4268 if ((*ptr & 0xc0) != 0x80)
4269 break;
4270 }
4271 else if (*ptr & 0x80)
4272 break;
4273 }
4274
4275 if (*ptr)
4276 {
4277 valid = 0;
4278
4279 if (print)
4280 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
4281 "sequence (RFC 2911 section 4.1.2).", attr->name,
4282 attr->values[i].string.text);
4283 else
4284 break;
4285 }
4286
4287 if ((ptr - attr->values[i].string.text) > 1023)
4288 {
4289 valid = 0;
4290
4291 if (print)
4292 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
4293 "(RFC 2911 section 4.1.2).", attr->name,
4294 attr->values[i].string.text,
4295 (int)strlen(attr->values[i].string.text));
4296 else
4297 break;
4298 }
4299 }
4300 break;
4301
4302 case IPP_TAG_KEYWORD :
4303 for (i = 0; i < attr->num_values; i ++)
4304 {
4305 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4306 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
4307 *ptr != '_')
4308 break;
4309
4310 if (*ptr || ptr == attr->values[i].string.text)
4311 {
4312 valid = 0;
4313
4314 if (print)
4315 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
4316 "character (RFC 2911 section 4.1.3).",
4317 attr->name, attr->values[i].string.text);
4318 else
4319 break;
4320 }
4321
4322 if ((ptr - attr->values[i].string.text) > 255)
4323 {
4324 valid = 0;
4325
4326 if (print)
4327 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
4328 "length %d (RFC 2911 section 4.1.3).",
4329 attr->name, attr->values[i].string.text,
4330 (int)strlen(attr->values[i].string.text));
4331 else
4332 break;
4333 }
4334 }
4335 break;
4336
4337 case IPP_TAG_URI :
4338 for (i = 0; i < attr->num_values; i ++)
4339 {
4340 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
4341 attr->values[i].string.text,
4342 scheme, sizeof(scheme),
4343 userpass, sizeof(userpass),
4344 hostname, sizeof(hostname),
4345 &port, resource, sizeof(resource));
4346
4347 if (uri_status < HTTP_URI_OK)
4348 {
4349 valid = 0;
4350
4351 if (print)
4352 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
4353 "(RFC 2911 section 4.1.5).", attr->name,
4354 attr->values[i].string.text,
4355 URIStatusStrings[uri_status -
4356 HTTP_URI_OVERFLOW]);
4357 else
4358 break;
4359 }
4360
4361 if (strlen(attr->values[i].string.text) > 1023)
4362 {
4363 valid = 0;
4364
4365 if (print)
4366 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
4367 "(RFC 2911 section 4.1.5).", attr->name,
4368 attr->values[i].string.text,
4369 (int)strlen(attr->values[i].string.text));
4370 else
4371 break;
4372 }
4373 }
4374 break;
4375
4376 case IPP_TAG_URISCHEME :
4377 for (i = 0; i < attr->num_values; i ++)
4378 {
4379 ptr = attr->values[i].string.text;
4380 if (islower(*ptr & 255))
4381 {
4382 for (ptr ++; *ptr; ptr ++)
4383 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
4384 *ptr != '+' && *ptr != '-' && *ptr != '.')
4385 break;
4386 }
4387
4388 if (*ptr || ptr == attr->values[i].string.text)
4389 {
4390 valid = 0;
4391
4392 if (print)
4393 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4394 "characters (RFC 2911 section 4.1.6).",
4395 attr->name, attr->values[i].string.text);
4396 else
4397 break;
4398 }
4399
4400 if ((ptr - attr->values[i].string.text) > 63)
4401 {
4402 valid = 0;
4403
4404 if (print)
4405 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4406 "length %d (RFC 2911 section 4.1.6).",
4407 attr->name, attr->values[i].string.text,
4408 (int)strlen(attr->values[i].string.text));
4409 else
4410 break;
4411 }
4412 }
4413 break;
4414
4415 case IPP_TAG_CHARSET :
4416 for (i = 0; i < attr->num_values; i ++)
4417 {
4418 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4419 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
4420 isspace(*ptr & 255))
4421 break;
4422
4423 if (*ptr || ptr == attr->values[i].string.text)
4424 {
4425 valid = 0;
4426
4427 if (print)
4428 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4429 "characters (RFC 2911 section 4.1.7).",
4430 attr->name, attr->values[i].string.text);
4431 else
4432 break;
4433 }
4434
4435 if ((ptr - attr->values[i].string.text) > 40)
4436 {
4437 valid = 0;
4438
4439 if (print)
4440 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4441 "length %d (RFC 2911 section 4.1.7).",
4442 attr->name, attr->values[i].string.text,
4443 (int)strlen(attr->values[i].string.text));
4444 else
4445 break;
4446 }
4447 }
4448 break;
4449
4450 case IPP_TAG_LANGUAGE :
4451 /*
4452 * The following regular expression is derived from the ABNF for
4453 * language tags in RFC 4646. All I can say is that this is the
4454 * easiest way to check the values...
4455 */
4456
4457 if ((i = regcomp(&re,
4458 "^("
4459 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4460 /* language */
4461 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4462 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4463 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4464 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4465 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4466 "|"
4467 "x(-[a-z0-9]{1,8})+" /* privateuse */
4468 "|"
4469 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4470 ")$",
4471 REG_NOSUB | REG_EXTENDED)) != 0)
4472 {
4473 char temp[256]; /* Temporary error string */
4474
4475 regerror(i, &re, temp, sizeof(temp));
4476 print_fatal_error("Unable to compile naturalLanguage regular "
4477 "expression: %s.", temp);
4478 }
4479
4480 for (i = 0; i < attr->num_values; i ++)
4481 {
4482 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4483 {
4484 valid = 0;
4485
4486 if (print)
4487 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4488 "characters (RFC 2911 section 4.1.8).",
4489 attr->name, attr->values[i].string.text);
4490 else
4491 break;
4492 }
4493
4494 if (strlen(attr->values[i].string.text) > 63)
4495 {
4496 valid = 0;
4497
4498 if (print)
4499 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4500 "length %d (RFC 2911 section 4.1.8).",
4501 attr->name, attr->values[i].string.text,
4502 (int)strlen(attr->values[i].string.text));
4503 else
4504 break;
4505 }
4506 }
4507
4508 regfree(&re);
4509 break;
4510
4511 case IPP_TAG_MIMETYPE :
4512 /*
4513 * The following regular expression is derived from the ABNF for
4514 * language tags in RFC 2045 and 4288. All I can say is that this is
4515 * the easiest way to check the values...
4516 */
4517
4518 if ((i = regcomp(&re,
4519 "^"
4520 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4521 "/"
4522 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4523 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4524 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4525 /* value */
4526 "$",
4527 REG_NOSUB | REG_EXTENDED)) != 0)
4528 {
4529 char temp[256]; /* Temporary error string */
4530
4531 regerror(i, &re, temp, sizeof(temp));
4532 print_fatal_error("Unable to compile mimeMediaType regular "
4533 "expression: %s.", temp);
4534 }
4535
4536 for (i = 0; i < attr->num_values; i ++)
4537 {
4538 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4539 {
4540 valid = 0;
4541
4542 if (print)
4543 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4544 "characters (RFC 2911 section 4.1.9).",
4545 attr->name, attr->values[i].string.text);
4546 else
4547 break;
4548 }
4549
4550 if (strlen(attr->values[i].string.text) > 255)
4551 {
4552 valid = 0;
4553
4554 if (print)
4555 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4556 "length %d (RFC 2911 section 4.1.9).",
4557 attr->name, attr->values[i].string.text,
4558 (int)strlen(attr->values[i].string.text));
4559 else
4560 break;
4561 }
4562 }
4563 break;
4564
4565 default :
4566 break;
4567 }
4568
4569 return (valid);
4570 }
4571
4572
4573 /*
4574 * 'with_value()' - Test a WITH-VALUE predicate.
4575 */
4576
4577 static int /* O - 1 on match, 0 on non-match */
4578 with_value(char *value, /* I - Value string */
4579 int regex, /* I - Value is a regular expression */
4580 ipp_attribute_t *attr, /* I - Attribute to compare */
4581 int report) /* I - 1 = report failures */
4582 {
4583 int i; /* Looping var */
4584 char *valptr; /* Pointer into value */
4585
4586
4587 /*
4588 * NULL matches everything.
4589 */
4590
4591 if (!value || !*value)
4592 return (1);
4593
4594 /*
4595 * Compare the value string to the attribute value.
4596 */
4597
4598 switch (attr->value_tag)
4599 {
4600 case IPP_TAG_INTEGER :
4601 case IPP_TAG_ENUM :
4602 for (i = 0; i < attr->num_values; i ++)
4603 {
4604 char op, /* Comparison operator */
4605 *nextptr; /* Next pointer */
4606 int intvalue; /* Integer value */
4607
4608
4609 valptr = value;
4610 if (!strncmp(valptr, "no-value,", 9))
4611 valptr += 9;
4612
4613 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
4614 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
4615 *valptr == '=' || *valptr == '>')
4616 {
4617 op = '=';
4618 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
4619 {
4620 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
4621 op = *valptr;
4622 valptr ++;
4623 }
4624
4625 if (!*valptr)
4626 break;
4627
4628 intvalue = strtol(valptr, &nextptr, 0);
4629 if (nextptr == valptr)
4630 break;
4631 valptr = nextptr;
4632
4633 switch (op)
4634 {
4635 case '=' :
4636 if (attr->values[i].integer == intvalue)
4637 return (1);
4638 break;
4639 case '<' :
4640 if (attr->values[i].integer < intvalue)
4641 return (1);
4642 break;
4643 case '>' :
4644 if (attr->values[i].integer > intvalue)
4645 return (1);
4646 break;
4647 }
4648 }
4649 }
4650
4651 if (report)
4652 {
4653 for (i = 0; i < attr->num_values; i ++)
4654 print_test_error("GOT: %s=%d", attr->name, attr->values[i].integer);
4655 }
4656 break;
4657
4658 case IPP_TAG_RANGE :
4659 for (i = 0; i < attr->num_values; i ++)
4660 {
4661 char op, /* Comparison operator */
4662 *nextptr; /* Next pointer */
4663 int intvalue; /* Integer value */
4664
4665
4666 valptr = value;
4667 if (!strncmp(valptr, "no-value,", 9))
4668 valptr += 9;
4669
4670 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
4671 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
4672 *valptr == '=' || *valptr == '>')
4673 {
4674 op = '=';
4675 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
4676 {
4677 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
4678 op = *valptr;
4679 valptr ++;
4680 }
4681
4682 if (!*valptr)
4683 break;
4684
4685 intvalue = strtol(valptr, &nextptr, 0);
4686 if (nextptr == valptr)
4687 break;
4688 valptr = nextptr;
4689
4690 switch (op)
4691 {
4692 case '=' :
4693 if (attr->values[i].range.lower == intvalue ||
4694 attr->values[i].range.upper == intvalue)
4695 return (1);
4696 break;
4697 case '<' :
4698 if (attr->values[i].range.upper < intvalue)
4699 return (1);
4700 break;
4701 case '>' :
4702 if (attr->values[i].range.upper > intvalue)
4703 return (1);
4704 break;
4705 }
4706 }
4707 }
4708
4709 if (report)
4710 {
4711 for (i = 0; i < attr->num_values; i ++)
4712 print_test_error("GOT: %s=%d-%d", attr->name,
4713 attr->values[i].range.lower,
4714 attr->values[i].range.upper);
4715 }
4716 break;
4717
4718 case IPP_TAG_BOOLEAN :
4719 for (i = 0; i < attr->num_values; i ++)
4720 {
4721 if (!strcmp(value, "true") == attr->values[i].boolean)
4722 return (1);
4723 }
4724
4725 if (report)
4726 {
4727 for (i = 0; i < attr->num_values; i ++)
4728 print_test_error("GOT: %s=%s", attr->name,
4729 attr->values[i].boolean ? "true" : "false");
4730 }
4731 break;
4732
4733 case IPP_TAG_NOVALUE :
4734 return (!strcmp(value, "no-value") || !strncmp(value, "no-value,", 9));
4735
4736 case IPP_TAG_CHARSET :
4737 case IPP_TAG_KEYWORD :
4738 case IPP_TAG_LANGUAGE :
4739 case IPP_TAG_MIMETYPE :
4740 case IPP_TAG_NAME :
4741 case IPP_TAG_NAMELANG :
4742 case IPP_TAG_TEXT :
4743 case IPP_TAG_TEXTLANG :
4744 case IPP_TAG_URI :
4745 case IPP_TAG_URISCHEME :
4746 if (regex)
4747 {
4748 /*
4749 * Value is an extended, case-sensitive POSIX regular expression...
4750 */
4751
4752 regex_t re; /* Regular expression */
4753
4754 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
4755 {
4756 char temp[256]; /* Temporary string */
4757
4758 regerror(i, &re, temp, sizeof(temp));
4759
4760 print_fatal_error("Unable to compile WITH-VALUE regular expression "
4761 "\"%s\" - %s", value, temp);
4762 return (0);
4763 }
4764
4765 /*
4766 * See if ALL of the values match the given regular expression.
4767 */
4768
4769 for (i = 0; i < attr->num_values; i ++)
4770 {
4771 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4772 {
4773 if (report)
4774 print_test_error("GOT: %s=\"%s\"", attr->name,
4775 attr->values[i].string.text);
4776 else
4777 break;
4778 }
4779 }
4780
4781 regfree(&re);
4782
4783 return (i == attr->num_values);
4784 }
4785 else
4786 {
4787 /*
4788 * Value is a literal string, see if at least one value matches the
4789 * literal string...
4790 */
4791
4792 for (i = 0; i < attr->num_values; i ++)
4793 {
4794 if (!strcmp(value, attr->values[i].string.text))
4795 return (1);
4796 }
4797
4798 if (report)
4799 {
4800 for (i = 0; i < attr->num_values; i ++)
4801 print_test_error("GOT: %s=\"%s\"", attr->name,
4802 attr->values[i].string.text);
4803 }
4804 }
4805 break;
4806
4807 default :
4808 break;
4809 }
4810
4811 return (0);
4812 }
4813
4814
4815 /*
4816 * End of "$Id$".
4817 */