]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptool.c
Merge changes from CUPS 1.5svn-r9641
[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, NULL, 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)) == (ipp_op_t)-1 &&
1207 (op = strtol(token, NULL, 0)) == 0)
1208 {
1209 print_fatal_error("Bad OPERATION code \"%s\" on line %d.", token,
1210 linenum);
1211 pass = 0;
1212 goto test_exit;
1213 }
1214 }
1215 else if (!strcasecmp(token, "GROUP"))
1216 {
1217 /*
1218 * Attribute group...
1219 */
1220
1221 if (!get_token(fp, token, sizeof(token), &linenum))
1222 {
1223 print_fatal_error("Missing GROUP tag on line %d.", linenum);
1224 pass = 0;
1225 goto test_exit;
1226 }
1227
1228 if ((value = ippTagValue(token)) < 0)
1229 {
1230 print_fatal_error("Bad GROUP tag \"%s\" on line %d.", token, linenum);
1231 pass = 0;
1232 goto test_exit;
1233 }
1234
1235 if (value == group)
1236 ippAddSeparator(request);
1237
1238 group = value;
1239 }
1240 else if (!strcasecmp(token, "DELAY"))
1241 {
1242 /*
1243 * Delay before operation...
1244 */
1245
1246 double delay;
1247
1248 if (!get_token(fp, token, sizeof(token), &linenum))
1249 {
1250 print_fatal_error("Missing DELAY value on line %d.", linenum);
1251 pass = 0;
1252 goto test_exit;
1253 }
1254
1255 if ((delay = _cupsStrScand(token, NULL, localeconv())) <= 0.0)
1256 {
1257 print_fatal_error("Bad DELAY value \"%s\" on line %d.", token,
1258 linenum);
1259 pass = 0;
1260 goto test_exit;
1261 }
1262 else
1263 {
1264 if (Output == _CUPS_OUTPUT_TEST)
1265 printf(" [%g second delay]\n", delay);
1266
1267 usleep((int)(1000000.0 * delay));
1268 }
1269 }
1270 else if (!strcasecmp(token, "ATTR"))
1271 {
1272 /*
1273 * Attribute...
1274 */
1275
1276 if (!get_token(fp, token, sizeof(token), &linenum))
1277 {
1278 print_fatal_error("Missing ATTR value tag on line %d.", linenum);
1279 pass = 0;
1280 goto test_exit;
1281 }
1282
1283 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
1284 {
1285 print_fatal_error("Bad ATTR value tag \"%s\" on line %d.", token,
1286 linenum);
1287 pass = 0;
1288 goto test_exit;
1289 }
1290
1291 if (!get_token(fp, attr, sizeof(attr), &linenum))
1292 {
1293 print_fatal_error("Missing ATTR name on line %d.", linenum);
1294 pass = 0;
1295 goto test_exit;
1296 }
1297
1298 if (!get_token(fp, temp, sizeof(temp), &linenum))
1299 {
1300 print_fatal_error("Missing ATTR value on line %d.", linenum);
1301 pass = 0;
1302 goto test_exit;
1303 }
1304
1305 expand_variables(vars, token, temp, sizeof(token));
1306
1307 switch (value)
1308 {
1309 case IPP_TAG_BOOLEAN :
1310 if (!strcasecmp(token, "true"))
1311 ippAddBoolean(request, group, attr, 1);
1312 else
1313 ippAddBoolean(request, group, attr, atoi(token));
1314 break;
1315
1316 case IPP_TAG_INTEGER :
1317 case IPP_TAG_ENUM :
1318 if (!strchr(token, ','))
1319 ippAddInteger(request, group, value, attr,
1320 strtol(token, &tokenptr, 0));
1321 else
1322 {
1323 int values[100], /* Values */
1324 num_values = 1; /* Number of values */
1325
1326 values[0] = strtol(token, &tokenptr, 10);
1327 while (tokenptr && *tokenptr &&
1328 num_values < (int)(sizeof(values) / sizeof(values[0])))
1329 {
1330 if (*tokenptr == ',')
1331 tokenptr ++;
1332 else if (!isdigit(*tokenptr & 255) && *tokenptr != '-')
1333 break;
1334
1335 values[num_values] = strtol(tokenptr, &tokenptr, 0);
1336 num_values ++;
1337 }
1338
1339 ippAddIntegers(request, group, value, attr, num_values, values);
1340 }
1341
1342 if (!tokenptr || *tokenptr)
1343 {
1344 print_fatal_error("Bad %s value \"%s\" on line %d.",
1345 ippTagString(value), token, linenum);
1346 pass = 0;
1347 goto test_exit;
1348 }
1349 break;
1350
1351 case IPP_TAG_RESOLUTION :
1352 {
1353 int xres, /* X resolution */
1354 yres; /* Y resolution */
1355 char *ptr; /* Pointer into value */
1356
1357 xres = yres = strtol(token, (char **)&ptr, 10);
1358 if (ptr > token && xres > 0)
1359 {
1360 if (*ptr == 'x')
1361 yres = strtol(ptr + 1, (char **)&ptr, 10);
1362 }
1363
1364 if (ptr <= token || xres <= 0 || yres <= 0 || !ptr ||
1365 (strcasecmp(ptr, "dpi") && strcasecmp(ptr, "dpc") &&
1366 strcasecmp(ptr, "other")))
1367 {
1368 print_fatal_error("Bad resolution value \"%s\" on line %d.",
1369 token, linenum);
1370 pass = 0;
1371 goto test_exit;
1372 }
1373
1374 if (!strcasecmp(ptr, "dpi"))
1375 ippAddResolution(request, group, attr, IPP_RES_PER_INCH,
1376 xres, yres);
1377 else if (!strcasecmp(ptr, "dpc"))
1378 ippAddResolution(request, group, attr, IPP_RES_PER_CM,
1379 xres, yres);
1380 else
1381 ippAddResolution(request, group, attr, (ipp_res_t)0,
1382 xres, yres);
1383 }
1384 break;
1385
1386 case IPP_TAG_RANGE :
1387 {
1388 int lowers[4], /* Lower value */
1389 uppers[4], /* Upper values */
1390 num_vals; /* Number of values */
1391
1392
1393 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
1394 lowers + 0, uppers + 0,
1395 lowers + 1, uppers + 1,
1396 lowers + 2, uppers + 2,
1397 lowers + 3, uppers + 3);
1398
1399 if ((num_vals & 1) || num_vals == 0)
1400 {
1401 print_fatal_error("Bad rangeOfInteger value \"%s\" on line "
1402 "%d.", token, linenum);
1403 pass = 0;
1404 goto test_exit;
1405 }
1406
1407 ippAddRanges(request, group, attr, num_vals / 2, lowers,
1408 uppers);
1409 }
1410 break;
1411
1412 case IPP_TAG_BEGIN_COLLECTION :
1413 if (!strcmp(token, "{"))
1414 {
1415 ipp_t *col = get_collection(vars, fp, &linenum);
1416 /* Collection value */
1417
1418 if (col)
1419 {
1420 lastcol = ippAddCollection(request, group, attr, col);
1421 ippDelete(col);
1422 }
1423 else
1424 {
1425 pass = 0;
1426 goto test_exit;
1427 }
1428 }
1429 else
1430 {
1431 print_fatal_error("Bad ATTR collection value on line %d.",
1432 linenum);
1433 pass = 0;
1434 goto test_exit;
1435 }
1436 break;
1437
1438 default :
1439 print_fatal_error("Unsupported ATTR value tag %s on line %d.",
1440 ippTagString(value), linenum);
1441 pass = 0;
1442 goto test_exit;
1443
1444 case IPP_TAG_TEXTLANG :
1445 case IPP_TAG_NAMELANG :
1446 case IPP_TAG_TEXT :
1447 case IPP_TAG_NAME :
1448 case IPP_TAG_KEYWORD :
1449 case IPP_TAG_URI :
1450 case IPP_TAG_URISCHEME :
1451 case IPP_TAG_CHARSET :
1452 case IPP_TAG_LANGUAGE :
1453 case IPP_TAG_MIMETYPE :
1454 if (!strchr(token, ','))
1455 ippAddString(request, group, value, attr, NULL, token);
1456 else
1457 {
1458 /*
1459 * Multiple string values...
1460 */
1461
1462 int num_values; /* Number of values */
1463 char *values[100], /* Values */
1464 *ptr; /* Pointer to next value */
1465
1466
1467 values[0] = token;
1468 num_values = 1;
1469
1470 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
1471 {
1472 *ptr++ = '\0';
1473 values[num_values] = ptr;
1474 num_values ++;
1475 }
1476
1477 ippAddStrings(request, group, value, attr, num_values,
1478 NULL, (const char **)values);
1479 }
1480 break;
1481 }
1482 }
1483 else if (!strcasecmp(token, "FILE"))
1484 {
1485 /*
1486 * File...
1487 */
1488
1489 if (!get_token(fp, temp, sizeof(temp), &linenum))
1490 {
1491 print_fatal_error("Missing FILE filename on line %d.", linenum);
1492 pass = 0;
1493 goto test_exit;
1494 }
1495
1496 expand_variables(vars, token, temp, sizeof(token));
1497 get_filename(testfile, filename, token, sizeof(filename));
1498 }
1499 else if (!strcasecmp(token, "STATUS"))
1500 {
1501 /*
1502 * Status...
1503 */
1504
1505 if (num_statuses >= (int)(sizeof(statuses) / sizeof(statuses[0])))
1506 {
1507 print_fatal_error("Too many STATUS's on line %d.", linenum);
1508 pass = 0;
1509 goto test_exit;
1510 }
1511
1512 if (!get_token(fp, token, sizeof(token), &linenum))
1513 {
1514 print_fatal_error("Missing STATUS code on line %d.", linenum);
1515 pass = 0;
1516 goto test_exit;
1517 }
1518
1519 if ((statuses[num_statuses].status = ippErrorValue(token))
1520 == (ipp_status_t)-1 &&
1521 (statuses[num_statuses].status = strtol(token, NULL, 0)) == 0)
1522 {
1523 print_fatal_error("Bad STATUS code \"%s\" on line %d.", token,
1524 linenum);
1525 pass = 0;
1526 goto test_exit;
1527 }
1528
1529 last_status = statuses + num_statuses;
1530 num_statuses ++;
1531
1532 last_status->if_defined = NULL;
1533 last_status->if_not_defined = NULL;
1534 }
1535 else if (!strcasecmp(token, "EXPECT"))
1536 {
1537 /*
1538 * Expected attributes...
1539 */
1540
1541 if (num_expects >= (int)(sizeof(expects) / sizeof(expects[0])))
1542 {
1543 print_fatal_error("Too many EXPECT's on line %d.", linenum);
1544 pass = 0;
1545 goto test_exit;
1546 }
1547
1548 if (!get_token(fp, token, sizeof(token), &linenum))
1549 {
1550 print_fatal_error("Missing EXPECT name on line %d.", linenum);
1551 pass = 0;
1552 goto test_exit;
1553 }
1554
1555 last_expect = expects + num_expects;
1556 num_expects ++;
1557
1558 memset(last_expect, 0, sizeof(_cups_expect_t));
1559
1560 if (token[0] == '!')
1561 {
1562 last_expect->not_expect = 1;
1563 last_expect->name = strdup(token + 1);
1564 }
1565 else if (token[0] == '?')
1566 {
1567 last_expect->optional = 1;
1568 last_expect->name = strdup(token + 1);
1569 }
1570 else
1571 last_expect->name = strdup(token);
1572 }
1573 else if (!strcasecmp(token, "COUNT"))
1574 {
1575 if (!get_token(fp, token, sizeof(token), &linenum))
1576 {
1577 print_fatal_error("Missing COUNT number on line %d.", linenum);
1578 pass = 0;
1579 goto test_exit;
1580 }
1581
1582 if ((i = atoi(token)) <= 0)
1583 {
1584 print_fatal_error("Bad COUNT \"%s\" on line %d.", token, linenum);
1585 pass = 0;
1586 goto test_exit;
1587 }
1588
1589 if (last_expect)
1590 last_expect->count = i;
1591 else
1592 {
1593 print_fatal_error("COUNT without a preceding EXPECT on line %d.",
1594 linenum);
1595 pass = 0;
1596 goto test_exit;
1597 }
1598 }
1599 else if (!strcasecmp(token, "DEFINE-MATCH"))
1600 {
1601 if (!get_token(fp, token, sizeof(token), &linenum))
1602 {
1603 print_fatal_error("Missing DEFINE-MATCH variable on line %d.",
1604 linenum);
1605 pass = 0;
1606 goto test_exit;
1607 }
1608
1609 if (last_expect)
1610 last_expect->define_match = strdup(token);
1611 else
1612 {
1613 print_fatal_error("DEFINE-MATCH without a preceding EXPECT on line "
1614 "%d.", linenum);
1615 pass = 0;
1616 goto test_exit;
1617 }
1618 }
1619 else if (!strcasecmp(token, "DEFINE-NO-MATCH"))
1620 {
1621 if (!get_token(fp, token, sizeof(token), &linenum))
1622 {
1623 print_fatal_error("Missing DEFINE-NO-MATCH variable on line %d.",
1624 linenum);
1625 pass = 0;
1626 goto test_exit;
1627 }
1628
1629 if (last_expect)
1630 last_expect->define_no_match = strdup(token);
1631 else
1632 {
1633 print_fatal_error("DEFINE-NO-MATCH without a preceding EXPECT on "
1634 "line %d.", linenum);
1635 pass = 0;
1636 goto test_exit;
1637 }
1638 }
1639 else if (!strcasecmp(token, "DEFINE-VALUE"))
1640 {
1641 if (!get_token(fp, token, sizeof(token), &linenum))
1642 {
1643 print_fatal_error("Missing DEFINE-VALUE variable on line %d.",
1644 linenum);
1645 pass = 0;
1646 goto test_exit;
1647 }
1648
1649 if (last_expect)
1650 last_expect->define_value = strdup(token);
1651 else
1652 {
1653 print_fatal_error("DEFINE-VALUE without a preceding EXPECT on line "
1654 "%d.", linenum);
1655 pass = 0;
1656 goto test_exit;
1657 }
1658 }
1659 else if (!strcasecmp(token, "OF-TYPE"))
1660 {
1661 if (!get_token(fp, token, sizeof(token), &linenum))
1662 {
1663 print_fatal_error("Missing OF-TYPE value tag(s) on line %d.",
1664 linenum);
1665 pass = 0;
1666 goto test_exit;
1667 }
1668
1669 if (last_expect)
1670 last_expect->of_type = strdup(token);
1671 else
1672 {
1673 print_fatal_error("OF-TYPE without a preceding EXPECT on line %d.",
1674 linenum);
1675 pass = 0;
1676 goto test_exit;
1677 }
1678 }
1679 else if (!strcasecmp(token, "IN-GROUP"))
1680 {
1681 ipp_tag_t in_group; /* IN-GROUP value */
1682
1683
1684 if (!get_token(fp, token, sizeof(token), &linenum))
1685 {
1686 print_fatal_error("Missing IN-GROUP group tag on line %d.", linenum);
1687 pass = 0;
1688 goto test_exit;
1689 }
1690
1691 if ((in_group = ippTagValue(token)) == (ipp_tag_t)-1)
1692 {
1693 }
1694 else if (last_expect)
1695 last_expect->in_group = in_group;
1696 else
1697 {
1698 print_fatal_error("IN-GROUP without a preceding EXPECT on line %d.",
1699 linenum);
1700 pass = 0;
1701 goto test_exit;
1702 }
1703 }
1704 else if (!strcasecmp(token, "SAME-COUNT-AS"))
1705 {
1706 if (!get_token(fp, token, sizeof(token), &linenum))
1707 {
1708 print_fatal_error("Missing SAME-COUNT-AS name on line %d.", linenum);
1709 pass = 0;
1710 goto test_exit;
1711 }
1712
1713 if (last_expect)
1714 last_expect->same_count_as = strdup(token);
1715 else
1716 {
1717 print_fatal_error("SAME-COUNT-AS without a preceding EXPECT on line "
1718 "%d.", linenum);
1719 pass = 0;
1720 goto test_exit;
1721 }
1722 }
1723 else if (!strcasecmp(token, "IF-DEFINED"))
1724 {
1725 if (!get_token(fp, token, sizeof(token), &linenum))
1726 {
1727 print_fatal_error("Missing IF-DEFINED name on line %d.", linenum);
1728 pass = 0;
1729 goto test_exit;
1730 }
1731
1732 if (last_expect)
1733 last_expect->if_defined = strdup(token);
1734 else if (last_status)
1735 last_status->if_defined = strdup(token);
1736 else
1737 {
1738 print_fatal_error("IF-DEFINED without a preceding EXPECT or STATUS "
1739 "on line %d.", linenum);
1740 pass = 0;
1741 goto test_exit;
1742 }
1743 }
1744 else if (!strcasecmp(token, "IF-NOT-DEFINED"))
1745 {
1746 if (!get_token(fp, token, sizeof(token), &linenum))
1747 {
1748 print_fatal_error("Missing IF-NOT-DEFINED name on line %d.", linenum);
1749 pass = 0;
1750 goto test_exit;
1751 }
1752
1753 if (last_expect)
1754 last_expect->if_not_defined = strdup(token);
1755 else if (last_status)
1756 last_status->if_not_defined = strdup(token);
1757 else
1758 {
1759 print_fatal_error("IF-NOT-DEFINED without a preceding EXPECT or STATUS "
1760 "on line %d.", linenum);
1761 pass = 0;
1762 goto test_exit;
1763 }
1764 }
1765 else if (!strcasecmp(token, "WITH-VALUE"))
1766 {
1767 if (!get_token(fp, temp, sizeof(temp), &linenum))
1768 {
1769 print_fatal_error("Missing WITH-VALUE value on line %d.", linenum);
1770 pass = 0;
1771 goto test_exit;
1772 }
1773
1774 if (last_expect)
1775 {
1776 /*
1777 * Expand any variables in the value and then save it.
1778 */
1779
1780 expand_variables(vars, token, temp, sizeof(token));
1781
1782 tokenptr = token + strlen(token) - 1;
1783
1784 if (token[0] == '/' && tokenptr > token && *tokenptr == '/')
1785 {
1786 /*
1787 * WITH-VALUE is a POSIX extended regular expression.
1788 */
1789
1790 last_expect->with_value = calloc(1, tokenptr - token);
1791 last_expect->with_regex = 1;
1792
1793 if (last_expect->with_value)
1794 memcpy(last_expect->with_value, token + 1, tokenptr - token - 1);
1795 }
1796 else
1797 {
1798 /*
1799 * WITH-VALUE is a literal value...
1800 */
1801
1802 last_expect->with_value = strdup(token);
1803 }
1804 }
1805 else
1806 {
1807 print_fatal_error("WITH-VALUE without a preceding EXPECT on line %d.",
1808 linenum);
1809 pass = 0;
1810 goto test_exit;
1811 }
1812 }
1813 else if (!strcasecmp(token, "DISPLAY"))
1814 {
1815 /*
1816 * Display attributes...
1817 */
1818
1819 if (num_displayed >= (int)(sizeof(displayed) / sizeof(displayed[0])))
1820 {
1821 print_fatal_error("Too many DISPLAY's on line %d", linenum);
1822 pass = 0;
1823 goto test_exit;
1824 }
1825
1826 if (!get_token(fp, token, sizeof(token), &linenum))
1827 {
1828 print_fatal_error("Missing DISPLAY name on line %d.", linenum);
1829 pass = 0;
1830 goto test_exit;
1831 }
1832
1833 displayed[num_displayed] = strdup(token);
1834 num_displayed ++;
1835 }
1836 else
1837 {
1838 print_fatal_error("Unexpected token %s seen on line %d.", token,
1839 linenum);
1840 pass = 0;
1841 goto test_exit;
1842 }
1843 }
1844
1845 /*
1846 * Submit the IPP request...
1847 */
1848
1849 request->request.op.version[0] = version / 10;
1850 request->request.op.version[1] = version % 10;
1851 request->request.op.operation_id = op;
1852 request->request.op.request_id = request_id;
1853
1854 if (Output == _CUPS_OUTPUT_PLIST)
1855 {
1856 puts("<dict>");
1857 puts("<key>Name</key>");
1858 print_xml_string("string", name);
1859 puts("<key>Operation</key>");
1860 print_xml_string("string", ippOpString(op));
1861 puts("<key>RequestAttributes</key>");
1862 puts("<array>");
1863 if (request->attrs)
1864 {
1865 puts("<dict>");
1866 for (attrptr = request->attrs, group = attrptr->group_tag;
1867 attrptr;
1868 attrptr = attrptr->next)
1869 print_attr(attrptr, &group);
1870 puts("</dict>");
1871 }
1872 puts("</array>");
1873 }
1874 else if (Output == _CUPS_OUTPUT_TEST)
1875 {
1876 if (Verbosity)
1877 {
1878 printf(" %s:\n", ippOpString(op));
1879
1880 for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
1881 print_attr(attrptr, NULL);
1882 }
1883
1884 printf(" %-69.69s [", name);
1885 fflush(stdout);
1886 }
1887
1888 if ((skip_previous && !prev_pass) || skip_test)
1889 {
1890 ippDelete(request);
1891 request = NULL;
1892
1893 if (Output == _CUPS_OUTPUT_PLIST)
1894 {
1895 puts("<key>Successful</key>");
1896 puts("<true />");
1897 puts("<key>StatusCode</key>");
1898 print_xml_string("string", "skip");
1899 puts("<key>ResponseAttributes</key>");
1900 puts("<dict>");
1901 puts("</dict>");
1902 }
1903 else if (Output == _CUPS_OUTPUT_TEST)
1904 puts("SKIP]");
1905
1906 goto skip_error;
1907 }
1908
1909 if (transfer == _CUPS_TRANSFER_CHUNKED ||
1910 (transfer == _CUPS_TRANSFER_AUTO && filename[0]))
1911 {
1912 /*
1913 * Send request using chunking...
1914 */
1915
1916 http_status_t status = cupsSendRequest(http, request, resource, 0);
1917
1918 if (status == HTTP_CONTINUE && filename[0])
1919 {
1920 int fd; /* File to send */
1921 char buffer[8192]; /* Copy buffer */
1922 ssize_t bytes; /* Bytes read/written */
1923
1924 if ((fd = open(filename, O_RDONLY | O_BINARY)) >= 0)
1925 {
1926 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1927 if ((status = cupsWriteRequestData(http, buffer,
1928 bytes)) != HTTP_CONTINUE)
1929 break;
1930 }
1931 else
1932 {
1933 snprintf(buffer, sizeof(buffer), "%s: %s", filename, strerror(errno));
1934 _cupsSetError(IPP_INTERNAL_ERROR, buffer, 0);
1935
1936 status = HTTP_ERROR;
1937 }
1938 }
1939
1940 ippDelete(request);
1941
1942 if (status == HTTP_CONTINUE)
1943 response = cupsGetResponse(http, resource);
1944 else
1945 response = NULL;
1946 }
1947 else if (filename[0])
1948 response = cupsDoFileRequest(http, request, resource, filename);
1949 else
1950 response = cupsDoRequest(http, request, resource);
1951
1952 request = NULL;
1953 prev_pass = 1;
1954
1955 if (!response)
1956 prev_pass = pass = 0;
1957 else
1958 {
1959 if (http->version != HTTP_1_1)
1960 prev_pass = pass = 0;
1961
1962 if (response->request.status.request_id != request_id)
1963 prev_pass = pass = 0;
1964
1965 if (version &&
1966 (response->request.status.version[0] != (version / 10) ||
1967 response->request.status.version[1] != (version % 10)))
1968 prev_pass = pass = 0;
1969
1970 if ((attrptr = ippFindAttribute(response, "job-id",
1971 IPP_TAG_INTEGER)) != NULL)
1972 {
1973 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1974 set_variable(vars, "job-id", temp);
1975 }
1976
1977 if ((attrptr = ippFindAttribute(response, "job-uri",
1978 IPP_TAG_URI)) != NULL)
1979 set_variable(vars, "job-uri", attrptr->values[0].string.text);
1980
1981 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
1982 IPP_TAG_INTEGER)) != NULL)
1983 {
1984 snprintf(temp, sizeof(temp), "%d", attrptr->values[0].integer);
1985 set_variable(vars, "notify-subscription-id", temp);
1986 }
1987
1988 attrptr = response->attrs;
1989 if (!attrptr || !attrptr->name ||
1990 attrptr->value_tag != IPP_TAG_CHARSET ||
1991 attrptr->group_tag != IPP_TAG_OPERATION ||
1992 attrptr->num_values != 1 ||
1993 strcmp(attrptr->name, "attributes-charset"))
1994 prev_pass = pass = 0;
1995
1996 if (attrptr)
1997 {
1998 attrptr = attrptr->next;
1999 if (!attrptr || !attrptr->name ||
2000 attrptr->value_tag != IPP_TAG_LANGUAGE ||
2001 attrptr->group_tag != IPP_TAG_OPERATION ||
2002 attrptr->num_values != 1 ||
2003 strcmp(attrptr->name, "attributes-natural-language"))
2004 prev_pass = pass = 0;
2005 }
2006
2007 if ((attrptr = ippFindAttribute(response, "status-message",
2008 IPP_TAG_ZERO)) != NULL &&
2009 (attrptr->value_tag != IPP_TAG_TEXT ||
2010 attrptr->group_tag != IPP_TAG_OPERATION ||
2011 attrptr->num_values != 1 ||
2012 (attrptr->value_tag == IPP_TAG_TEXT &&
2013 strlen(attrptr->values[0].string.text) > 255)))
2014 prev_pass = pass = 0;
2015
2016 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
2017 IPP_TAG_ZERO)) != NULL &&
2018 (attrptr->value_tag != IPP_TAG_TEXT ||
2019 attrptr->group_tag != IPP_TAG_OPERATION ||
2020 attrptr->num_values != 1 ||
2021 (attrptr->value_tag == IPP_TAG_TEXT &&
2022 strlen(attrptr->values[0].string.text) > 1023)))
2023 prev_pass = pass = 0;
2024
2025 for (attrptr = response->attrs, group = attrptr->group_tag;
2026 attrptr;
2027 attrptr = attrptr->next)
2028 {
2029 if (attrptr->group_tag < group && attrptr->group_tag != IPP_TAG_ZERO)
2030 {
2031 prev_pass = pass = 0;
2032 break;
2033 }
2034
2035 if (!validate_attr(attrptr, 0))
2036 {
2037 prev_pass = pass = 0;
2038 break;
2039 }
2040 }
2041
2042 for (i = 0; i < num_statuses; i ++)
2043 {
2044 if (statuses[i].if_defined &&
2045 !get_variable(vars, statuses[i].if_defined))
2046 continue;
2047
2048 if (statuses[i].if_not_defined &&
2049 get_variable(vars, statuses[i].if_not_defined))
2050 continue;
2051
2052 if (response->request.status.status_code == statuses[i].status)
2053 break;
2054 }
2055
2056 if (i == num_statuses && num_statuses > 0)
2057 prev_pass = pass = 0;
2058 else
2059 {
2060 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2061 {
2062 if (expect->if_defined && !get_variable(vars, expect->if_defined))
2063 continue;
2064
2065 if (expect->if_not_defined &&
2066 get_variable(vars, expect->if_not_defined))
2067 continue;
2068
2069 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
2070
2071 if ((found && expect->not_expect) ||
2072 (!found && !(expect->not_expect || expect->optional)) ||
2073 (found && !expect_matches(expect, found->value_tag)) ||
2074 (found && expect->in_group &&
2075 found->group_tag != expect->in_group))
2076 {
2077 if (expect->define_no_match)
2078 set_variable(vars, expect->define_no_match, "1");
2079 else if (!expect->define_match)
2080 prev_pass = pass = 0;
2081
2082 continue;
2083 }
2084
2085 if (found &&
2086 !with_value(expect->with_value, expect->with_regex, found, 0))
2087 {
2088 if (expect->define_no_match)
2089 set_variable(vars, expect->define_no_match, "1");
2090 else if (!expect->define_match)
2091 prev_pass = pass = 0;
2092
2093 continue;
2094 }
2095
2096 if (found && expect->count > 0 && found->num_values != expect->count)
2097 {
2098 if (expect->define_no_match)
2099 set_variable(vars, expect->define_no_match, "1");
2100 else if (!expect->define_match)
2101 prev_pass = pass = 0;
2102
2103 continue;
2104 }
2105
2106 if (found && expect->same_count_as)
2107 {
2108 attrptr = ippFindAttribute(response, expect->same_count_as,
2109 IPP_TAG_ZERO);
2110
2111 if (!attrptr || attrptr->num_values != found->num_values)
2112 {
2113 if (expect->define_no_match)
2114 set_variable(vars, expect->define_no_match, "1");
2115 else if (!expect->define_match)
2116 prev_pass = pass = 0;
2117
2118 continue;
2119 }
2120 }
2121
2122 if (found && expect->define_match)
2123 set_variable(vars, expect->define_match, "1");
2124
2125 if (found && expect->define_value)
2126 {
2127 _ippAttrString(found, token, sizeof(token));
2128 set_variable(vars, expect->define_value, token);
2129 }
2130 }
2131 }
2132 }
2133
2134 if (Output == _CUPS_OUTPUT_PLIST)
2135 {
2136 puts("<key>Successful</key>");
2137 puts(prev_pass ? "<true />" : "<false />");
2138 puts("<key>StatusCode</key>");
2139 print_xml_string("string", ippErrorString(cupsLastError()));
2140 puts("<key>ResponseAttributes</key>");
2141 puts("<array>");
2142 puts("<dict>");
2143 for (attrptr = response ? response->attrs : NULL,
2144 group = attrptr ? attrptr->group_tag : IPP_TAG_ZERO;
2145 attrptr;
2146 attrptr = attrptr->next)
2147 print_attr(attrptr, &group);
2148 puts("</dict>");
2149 puts("</array>");
2150 }
2151 else if (Output == _CUPS_OUTPUT_TEST)
2152 {
2153 puts(prev_pass ? "PASS]" : "FAIL]");
2154
2155 if (Verbosity && response)
2156 {
2157 printf(" RECEIVED: %lu bytes in response\n",
2158 (unsigned long)ippLength(response));
2159 printf(" status-code = %x (%s)\n", cupsLastError(),
2160 ippErrorString(cupsLastError()));
2161
2162 for (attrptr = response->attrs;
2163 attrptr != NULL;
2164 attrptr = attrptr->next)
2165 {
2166 print_attr(attrptr, NULL);
2167 }
2168 }
2169 }
2170 else if (!prev_pass)
2171 fprintf(stderr, "%s\n", cupsLastErrorString());
2172
2173 if (prev_pass && Output != _CUPS_OUTPUT_PLIST &&
2174 Output != _CUPS_OUTPUT_QUIET && !Verbosity && num_displayed > 0)
2175 {
2176 if (Output >= _CUPS_OUTPUT_LIST)
2177 {
2178 size_t width; /* Length of value */
2179
2180
2181 for (i = 0; i < num_displayed; i ++)
2182 {
2183 widths[i] = strlen(displayed[i]);
2184
2185 for (attrptr = ippFindAttribute(response, displayed[i], IPP_TAG_ZERO);
2186 attrptr;
2187 attrptr = ippFindNextAttribute(response, displayed[i],
2188 IPP_TAG_ZERO))
2189 {
2190 width = _ippAttrString(attrptr, NULL, 0);
2191 if (width > widths[i])
2192 widths[i] = width;
2193 }
2194 }
2195
2196 if (Output == _CUPS_OUTPUT_CSV)
2197 print_csv(NULL, num_displayed, displayed, widths);
2198 else
2199 print_line(NULL, num_displayed, displayed, widths);
2200
2201 attrptr = response->attrs;
2202
2203 while (attrptr)
2204 {
2205 while (attrptr && attrptr->group_tag <= IPP_TAG_OPERATION)
2206 attrptr = attrptr->next;
2207
2208 if (attrptr)
2209 {
2210 if (Output == _CUPS_OUTPUT_CSV)
2211 print_csv(attrptr, num_displayed, displayed, widths);
2212 else
2213 print_line(attrptr, num_displayed, displayed, widths);
2214
2215 while (attrptr && attrptr->group_tag > IPP_TAG_OPERATION)
2216 attrptr = attrptr->next;
2217 }
2218 }
2219 }
2220 else
2221 {
2222 for (attrptr = response->attrs;
2223 attrptr != NULL;
2224 attrptr = attrptr->next)
2225 {
2226 if (attrptr->name)
2227 {
2228 for (i = 0; i < num_displayed; i ++)
2229 {
2230 if (!strcmp(displayed[i], attrptr->name))
2231 {
2232 print_attr(attrptr, NULL);
2233 break;
2234 }
2235 }
2236 }
2237 }
2238 }
2239 }
2240 else if (!prev_pass)
2241 {
2242 if (Output == _CUPS_OUTPUT_PLIST)
2243 {
2244 puts("<key>Errors</key>");
2245 puts("<array>");
2246 }
2247
2248 if (http->version != HTTP_1_1)
2249 print_test_error("Bad HTTP version (%d.%d)", http->version / 100,
2250 http->version % 100);
2251
2252 if (!response)
2253 print_test_error("IPP request failed with status %s (%s)",
2254 ippErrorString(cupsLastError()),
2255 cupsLastErrorString());
2256 else
2257 {
2258 if (version &&
2259 (response->request.status.version[0] != (version / 10) ||
2260 response->request.status.version[1] != (version % 10)))
2261 print_test_error("Bad version %d.%d in response - expected %d.%d "
2262 "(RFC 2911 section 3.1.8).",
2263 response->request.status.version[0],
2264 response->request.status.version[1],
2265 version / 10, version % 10);
2266
2267 if (response->request.status.request_id != request_id)
2268 print_test_error("Bad request ID %d in response - expected %d "
2269 "(RFC 2911 section 3.1.1)",
2270 response->request.status.request_id, request_id);
2271
2272 attrptr = response->attrs;
2273 if (!attrptr)
2274 print_test_error("Missing first attribute \"attributes-charset "
2275 "(charset)\" in group operation-attributes-tag "
2276 "(RFC 2911 section 3.1.4).");
2277 else
2278 {
2279 if (!attrptr->name ||
2280 attrptr->value_tag != IPP_TAG_CHARSET ||
2281 attrptr->group_tag != IPP_TAG_OPERATION ||
2282 attrptr->num_values != 1 ||
2283 strcmp(attrptr->name, "attributes-charset"))
2284 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2285 "expected \"attributes-charset (charset)\" in "
2286 "group operation-attributes-tag (RFC 2911 section "
2287 "3.1.4).",
2288 attrptr->name ? attrptr->name : "(null)",
2289 attrptr->num_values > 1 ? "1setOf " : "",
2290 ippTagString(attrptr->value_tag),
2291 ippTagString(attrptr->group_tag));
2292
2293 attrptr = attrptr->next;
2294 if (!attrptr)
2295 print_test_error("Missing second attribute \"attributes-natural-"
2296 "language (naturalLanguage)\" in group "
2297 "operation-attributes-tag (RFC 2911 section "
2298 "3.1.4).");
2299 else if (!attrptr->name ||
2300 attrptr->value_tag != IPP_TAG_LANGUAGE ||
2301 attrptr->group_tag != IPP_TAG_OPERATION ||
2302 attrptr->num_values != 1 ||
2303 strcmp(attrptr->name, "attributes-natural-language"))
2304 print_test_error("Bad first attribute \"%s (%s%s)\" in group %s, "
2305 "expected \"attributes-natural-language "
2306 "(naturalLanguage)\" in group "
2307 "operation-attributes-tag (RFC 2911 section "
2308 "3.1.4).",
2309 attrptr->name ? attrptr->name : "(null)",
2310 attrptr->num_values > 1 ? "1setOf " : "",
2311 ippTagString(attrptr->value_tag),
2312 ippTagString(attrptr->group_tag));
2313 }
2314
2315 if ((attrptr = ippFindAttribute(response, "status-message",
2316 IPP_TAG_ZERO)) != NULL)
2317 {
2318 if (attrptr->value_tag != IPP_TAG_TEXT)
2319 print_test_error("status-message (text(255)) has wrong value tag "
2320 "%s (RFC 2911 section 3.1.6.2).",
2321 ippTagString(attrptr->value_tag));
2322 if (attrptr->group_tag != IPP_TAG_OPERATION)
2323 print_test_error("status-message (text(255)) has wrong group tag "
2324 "%s (RFC 2911 section 3.1.6.2).",
2325 ippTagString(attrptr->group_tag));
2326 if (attrptr->num_values != 1)
2327 print_test_error("status-message (text(255)) has %d values "
2328 "(RFC 2911 section 3.1.6.2).",
2329 attrptr->num_values);
2330 if (attrptr->value_tag == IPP_TAG_TEXT &&
2331 strlen(attrptr->values[0].string.text) > 255)
2332 print_test_error("status-message (text(255)) has bad length %d"
2333 " (RFC 2911 section 3.1.6.2).",
2334 (int)strlen(attrptr->values[0].string.text));
2335 }
2336
2337 if ((attrptr = ippFindAttribute(response, "detailed-status-message",
2338 IPP_TAG_ZERO)) != NULL)
2339 {
2340 if (attrptr->value_tag != IPP_TAG_TEXT)
2341 print_test_error("detailed-status-message (text(MAX)) has wrong "
2342 "value tag %s (RFC 2911 section 3.1.6.3).",
2343 ippTagString(attrptr->value_tag));
2344 if (attrptr->group_tag != IPP_TAG_OPERATION)
2345 print_test_error("detailed-status-message (text(MAX)) has wrong "
2346 "group tag %s (RFC 2911 section 3.1.6.3).",
2347 ippTagString(attrptr->group_tag));
2348 if (attrptr->num_values != 1)
2349 print_test_error("detailed-status-message (text(MAX)) has %d values"
2350 " (RFC 2911 section 3.1.6.3).",
2351 attrptr->num_values);
2352 if (attrptr->value_tag == IPP_TAG_TEXT &&
2353 strlen(attrptr->values[0].string.text) > 1023)
2354 print_test_error("detailed-status-message (text(MAX)) has bad "
2355 "length %d (RFC 2911 section 3.1.6.3).",
2356 (int)strlen(attrptr->values[0].string.text));
2357 }
2358
2359 for (attrptr = response->attrs, group = attrptr->group_tag;
2360 attrptr;
2361 attrptr = attrptr->next)
2362 {
2363 if (attrptr->group_tag < group && attrptr->group_tag != IPP_TAG_ZERO)
2364 print_test_error("Attribute groups out of order (%s < %s)",
2365 ippTagString(attrptr->group_tag),
2366 ippTagString(group));
2367
2368 validate_attr(attrptr, 1);
2369 }
2370
2371 for (i = 0; i < num_statuses; i ++)
2372 {
2373 if (statuses[i].if_defined &&
2374 !get_variable(vars, statuses[i].if_defined))
2375 continue;
2376
2377 if (statuses[i].if_not_defined &&
2378 get_variable(vars, statuses[i].if_not_defined))
2379 continue;
2380
2381 if (response->request.status.status_code == statuses[i].status)
2382 break;
2383 }
2384
2385 if (i == num_statuses && num_statuses > 0)
2386 {
2387 for (i = 0; i < num_statuses; i ++)
2388 {
2389 if (statuses[i].if_defined &&
2390 !get_variable(vars, statuses[i].if_defined))
2391 continue;
2392
2393 if (statuses[i].if_not_defined &&
2394 get_variable(vars, statuses[i].if_not_defined))
2395 continue;
2396
2397 print_test_error("EXPECTED: STATUS %s (got %s)",
2398 ippErrorString(statuses[i].status),
2399 ippErrorString(cupsLastError()));
2400 }
2401
2402 if ((attrptr = ippFindAttribute(response, "status-message",
2403 IPP_TAG_TEXT)) != NULL)
2404 print_test_error("status-message=\"%s\"",
2405 attrptr->values[0].string.text);
2406 }
2407
2408 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2409 {
2410 if (expect->define_match || expect->define_no_match)
2411 continue;
2412
2413 if (expect->if_defined && !get_variable(vars, expect->if_defined))
2414 continue;
2415
2416 if (expect->if_not_defined &&
2417 get_variable(vars, expect->if_not_defined))
2418 continue;
2419
2420 found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO);
2421
2422 if (found && expect->not_expect)
2423 print_test_error("NOT EXPECTED: %s", expect->name);
2424 else if (!found && !(expect->not_expect || expect->optional))
2425 print_test_error("EXPECTED: %s", expect->name);
2426 else if (found)
2427 {
2428 if (!expect_matches(expect, found->value_tag))
2429 print_test_error("EXPECTED: %s OF-TYPE %s (got %s)",
2430 expect->name, expect->of_type,
2431 ippTagString(found->value_tag));
2432
2433 if (expect->in_group && found->group_tag != expect->in_group)
2434 print_test_error("EXPECTED: %s IN-GROUP %s (got %s).",
2435 expect->name, ippTagString(expect->in_group),
2436 ippTagString(found->group_tag));
2437
2438 if (!with_value(expect->with_value, expect->with_regex, found, 0))
2439 {
2440 if (expect->with_regex)
2441 print_test_error("EXPECTED: %s WITH-VALUE /%s/",
2442 expect->name, expect->with_value);
2443 else
2444 print_test_error("EXPECTED: %s WITH-VALUE \"%s\"",
2445 expect->name, expect->with_value);
2446
2447 with_value(expect->with_value, expect->with_regex, found, 1);
2448 }
2449
2450 if (expect->count > 0 && found->num_values != expect->count)
2451 {
2452 print_test_error("EXPECTED: %s COUNT %d (got %d)", expect->name,
2453 expect->count, found->num_values);
2454 }
2455
2456 if (expect->same_count_as)
2457 {
2458 attrptr = ippFindAttribute(response, expect->same_count_as,
2459 IPP_TAG_ZERO);
2460
2461 if (!attrptr)
2462 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2463 "(not returned)", expect->name,
2464 found->num_values, expect->same_count_as);
2465 else if (attrptr->num_values != found->num_values)
2466 print_test_error("EXPECTED: %s (%d values) SAME-COUNT-AS %s "
2467 "(%d values)", expect->name, found->num_values,
2468 expect->same_count_as, attrptr->num_values);
2469 }
2470 }
2471 }
2472 }
2473
2474 if (Output == _CUPS_OUTPUT_PLIST)
2475 puts("</array>");
2476 }
2477
2478 if (Output == _CUPS_OUTPUT_PLIST)
2479 puts("</dict>");
2480
2481 skip_error:
2482
2483 ippDelete(response);
2484 response = NULL;
2485
2486 for (i = 0; i < num_statuses; i ++)
2487 {
2488 if (statuses[i].if_defined)
2489 free(statuses[i].if_defined);
2490 if (statuses[i].if_not_defined)
2491 free(statuses[i].if_not_defined);
2492 }
2493 num_statuses = 0;
2494
2495 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2496 {
2497 free(expect->name);
2498 if (expect->of_type)
2499 free(expect->of_type);
2500 if (expect->same_count_as)
2501 free(expect->same_count_as);
2502 if (expect->if_defined)
2503 free(expect->if_defined);
2504 if (expect->if_not_defined)
2505 free(expect->if_not_defined);
2506 if (expect->with_value)
2507 free(expect->with_value);
2508 if (expect->define_match)
2509 free(expect->define_match);
2510 if (expect->define_no_match)
2511 free(expect->define_no_match);
2512 if (expect->define_value)
2513 free(expect->define_value);
2514 }
2515 num_expects = 0;
2516
2517 for (i = 0; i < num_displayed; i ++)
2518 free(displayed[i]);
2519 num_displayed = 0;
2520
2521 if (!ignore_errors && !prev_pass)
2522 break;
2523 }
2524
2525 test_exit:
2526
2527 if (fp)
2528 fclose(fp);
2529
2530 httpClose(http);
2531 ippDelete(request);
2532 ippDelete(response);
2533
2534 for (i = 0; i < num_statuses; i ++)
2535 {
2536 if (statuses[i].if_defined)
2537 free(statuses[i].if_defined);
2538 if (statuses[i].if_not_defined)
2539 free(statuses[i].if_not_defined);
2540 }
2541
2542 for (i = num_expects, expect = expects; i > 0; i --, expect ++)
2543 {
2544 free(expect->name);
2545 if (expect->of_type)
2546 free(expect->of_type);
2547 if (expect->same_count_as)
2548 free(expect->same_count_as);
2549 if (expect->if_defined)
2550 free(expect->if_defined);
2551 if (expect->if_not_defined)
2552 free(expect->if_not_defined);
2553 if (expect->with_value)
2554 free(expect->with_value);
2555 if (expect->define_match)
2556 free(expect->define_match);
2557 if (expect->define_no_match)
2558 free(expect->define_no_match);
2559 if (expect->define_value)
2560 free(expect->define_value);
2561 }
2562
2563 for (i = 0; i < num_displayed; i ++)
2564 free(displayed[i]);
2565
2566 return (pass);
2567 }
2568
2569
2570 /*
2571 * 'expand_variables()' - Expand variables in a string.
2572 */
2573
2574 static void
2575 expand_variables(_cups_vars_t *vars, /* I - Variables */
2576 char *dst, /* I - Destination string buffer */
2577 const char *src, /* I - Source string */
2578 size_t dstsize) /* I - Size of destination buffer */
2579 {
2580 char *dstptr, /* Pointer into destination */
2581 *dstend, /* End of destination */
2582 temp[256], /* Temporary string */
2583 *tempptr; /* Pointer into temporary string */
2584 const char *value; /* Value to substitute */
2585
2586
2587 dstptr = dst;
2588 dstend = dst + dstsize - 1;
2589
2590 while (*src && dstptr < dstend)
2591 {
2592 if (*src == '$')
2593 {
2594 /*
2595 * Substitute a string/number...
2596 */
2597
2598 if (!strncmp(src, "$$", 2))
2599 {
2600 value = "$";
2601 src += 2;
2602 }
2603 else if (!strncmp(src, "$ENV[", 5))
2604 {
2605 strlcpy(temp, src + 5, sizeof(temp));
2606
2607 for (tempptr = temp; *tempptr; tempptr ++)
2608 if (*tempptr == ']')
2609 break;
2610
2611 if (*tempptr)
2612 *tempptr++ = '\0';
2613
2614 value = getenv(temp);
2615 src += tempptr - temp + 5;
2616 }
2617 else if (vars)
2618 {
2619 strlcpy(temp, src + 1, sizeof(temp));
2620
2621 for (tempptr = temp; *tempptr; tempptr ++)
2622 if (!isalnum(*tempptr & 255) && *tempptr != '-' && *tempptr != '_')
2623 break;
2624
2625 if (*tempptr)
2626 *tempptr = '\0';
2627
2628 if (!strcmp(temp, "uri"))
2629 value = vars->uri;
2630 else if (!strcmp(temp, "filename"))
2631 value = vars->filename;
2632 else if (!strcmp(temp, "scheme") || !strcmp(temp, "method"))
2633 value = vars->scheme;
2634 else if (!strcmp(temp, "username"))
2635 value = vars->userpass;
2636 else if (!strcmp(temp, "hostname"))
2637 value = vars->hostname;
2638 else if (!strcmp(temp, "port"))
2639 {
2640 snprintf(temp, sizeof(temp), "%d", vars->port);
2641 value = temp;
2642 }
2643 else if (!strcmp(temp, "resource"))
2644 value = vars->resource;
2645 else if (!strcmp(temp, "user"))
2646 value = cupsUser();
2647 else
2648 value = get_variable(vars, temp);
2649
2650 src += tempptr - temp + 1;
2651 }
2652 else
2653 {
2654 value = "$";
2655 src ++;
2656 }
2657
2658 if (value)
2659 {
2660 strlcpy(dstptr, value, dstend - dstptr + 1);
2661 dstptr += strlen(dstptr);
2662 }
2663 }
2664 else
2665 *dstptr++ = *src++;
2666 }
2667
2668 *dstptr = '\0';
2669 }
2670
2671
2672 /*
2673 * 'expect_matches()' - Return true if the tag matches the specification.
2674 */
2675
2676 static int /* O - 1 if matches, 0 otherwise */
2677 expect_matches(
2678 _cups_expect_t *expect, /* I - Expected attribute */
2679 ipp_tag_t value_tag) /* I - Value tag for attribute */
2680 {
2681 int match; /* Match? */
2682 char *of_type, /* Type name to match */
2683 *next, /* Next name to match */
2684 sep; /* Separator character */
2685
2686
2687 /*
2688 * If we don't expect a particular type, return immediately...
2689 */
2690
2691 if (!expect->of_type)
2692 return (1);
2693
2694 /*
2695 * Parse the "of_type" value since the string can contain multiple attribute
2696 * types separated by "," or "|"...
2697 */
2698
2699 for (of_type = expect->of_type, match = 0; !match && *of_type; of_type = next)
2700 {
2701 /*
2702 * Find the next separator, and set it (temporarily) to nul if present.
2703 */
2704
2705 for (next = of_type; *next && *next != '|' && *next != ','; next ++);
2706
2707 if ((sep = *next) != '\0')
2708 *next = '\0';
2709
2710 /*
2711 * Support some meta-types to make it easier to write the test file.
2712 */
2713
2714 if (!strcmp(of_type, "text"))
2715 match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT;
2716 else if (!strcmp(of_type, "name"))
2717 match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME;
2718 else if (!strcmp(of_type, "collection"))
2719 match = value_tag == IPP_TAG_BEGIN_COLLECTION;
2720 else
2721 match = value_tag == ippTagValue(of_type);
2722
2723 /*
2724 * Restore the separator if we have one...
2725 */
2726
2727 if (sep)
2728 *next++ = sep;
2729 }
2730
2731 return (match);
2732 }
2733
2734
2735 /*
2736 * 'get_collection()' - Get a collection value from the current test file.
2737 */
2738
2739 static ipp_t * /* O - Collection value */
2740 get_collection(_cups_vars_t *vars, /* I - Variables */
2741 FILE *fp, /* I - File to read from */
2742 int *linenum) /* IO - Line number */
2743 {
2744 char token[1024], /* Token from file */
2745 temp[1024], /* Temporary string */
2746 attr[128]; /* Attribute name */
2747 ipp_tag_t value; /* Current value type */
2748 ipp_t *col = ippNew(); /* Collection value */
2749 ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
2750
2751
2752 while (get_token(fp, token, sizeof(token), linenum) != NULL)
2753 {
2754 if (!strcmp(token, "}"))
2755 break;
2756 else if (!strcmp(token, "{") && lastcol)
2757 {
2758 /*
2759 * Another collection value
2760 */
2761
2762 ipp_t *subcol = get_collection(vars, fp, linenum);
2763 /* Collection value */
2764
2765 if (subcol)
2766 {
2767 ipp_attribute_t *tempcol; /* Pointer to new buffer */
2768
2769
2770 /*
2771 * Reallocate memory...
2772 */
2773
2774 if ((tempcol = realloc(lastcol, sizeof(ipp_attribute_t) +
2775 (lastcol->num_values + 1) *
2776 sizeof(ipp_value_t))) == NULL)
2777 {
2778 print_fatal_error("Unable to allocate memory on line %d.", *linenum);
2779 goto col_error;
2780 }
2781
2782 if (tempcol != lastcol)
2783 {
2784 /*
2785 * Reset pointers in the list...
2786 */
2787
2788 if (col->prev)
2789 col->prev->next = tempcol;
2790 else
2791 col->attrs = tempcol;
2792
2793 lastcol = col->current = col->last = tempcol;
2794 }
2795
2796 lastcol->values[lastcol->num_values].collection = subcol;
2797 lastcol->num_values ++;
2798 }
2799 else
2800 goto col_error;
2801 }
2802 else if (!strcasecmp(token, "MEMBER"))
2803 {
2804 /*
2805 * Attribute...
2806 */
2807
2808 lastcol = NULL;
2809
2810 if (!get_token(fp, token, sizeof(token), linenum))
2811 {
2812 print_fatal_error("Missing MEMBER value tag on line %d.", *linenum);
2813 goto col_error;
2814 }
2815
2816 if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
2817 {
2818 print_fatal_error("Bad MEMBER value tag \"%s\" on line %d.", token,
2819 *linenum);
2820 goto col_error;
2821 }
2822
2823 if (!get_token(fp, attr, sizeof(attr), linenum))
2824 {
2825 print_fatal_error("Missing MEMBER name on line %d.", *linenum);
2826 goto col_error;
2827 }
2828
2829 if (!get_token(fp, temp, sizeof(temp), linenum))
2830 {
2831 print_fatal_error("Missing MEMBER value on line %d.", *linenum);
2832 goto col_error;
2833 }
2834
2835 expand_variables(vars, token, temp, sizeof(token));
2836
2837 switch (value)
2838 {
2839 case IPP_TAG_BOOLEAN :
2840 if (!strcasecmp(token, "true"))
2841 ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
2842 else
2843 ippAddBoolean(col, IPP_TAG_ZERO, attr, atoi(token));
2844 break;
2845
2846 case IPP_TAG_INTEGER :
2847 case IPP_TAG_ENUM :
2848 ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
2849 break;
2850
2851 case IPP_TAG_RESOLUTION :
2852 {
2853 int xres, /* X resolution */
2854 yres; /* Y resolution */
2855 char units[6]; /* Units */
2856
2857 if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
2858 (strcasecmp(units, "dpi") && strcasecmp(units, "dpc") &&
2859 strcasecmp(units, "other")))
2860 {
2861 print_fatal_error("Bad resolution value \"%s\" on line %d.",
2862 token, *linenum);
2863 goto col_error;
2864 }
2865
2866 if (!strcasecmp(units, "dpi"))
2867 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2868 IPP_RES_PER_INCH);
2869 else if (!strcasecmp(units, "dpc"))
2870 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2871 IPP_RES_PER_CM);
2872 else
2873 ippAddResolution(col, IPP_TAG_ZERO, attr, xres, yres,
2874 (ipp_res_t)0);
2875 }
2876 break;
2877
2878 case IPP_TAG_RANGE :
2879 {
2880 int lowers[4], /* Lower value */
2881 uppers[4], /* Upper values */
2882 num_vals; /* Number of values */
2883
2884
2885 num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
2886 lowers + 0, uppers + 0,
2887 lowers + 1, uppers + 1,
2888 lowers + 2, uppers + 2,
2889 lowers + 3, uppers + 3);
2890
2891 if ((num_vals & 1) || num_vals == 0)
2892 {
2893 print_fatal_error("Bad rangeOfInteger value \"%s\" on line %d.",
2894 token, *linenum);
2895 goto col_error;
2896 }
2897
2898 ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
2899 uppers);
2900 }
2901 break;
2902
2903 case IPP_TAG_BEGIN_COLLECTION :
2904 if (!strcmp(token, "{"))
2905 {
2906 ipp_t *subcol = get_collection(vars, fp, linenum);
2907 /* Collection value */
2908
2909 if (subcol)
2910 {
2911 lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
2912 ippDelete(subcol);
2913 }
2914 else
2915 goto col_error;
2916 }
2917 else
2918 {
2919 print_fatal_error("Bad collection value on line %d.", *linenum);
2920 goto col_error;
2921 }
2922 break;
2923
2924 default :
2925 if (!strchr(token, ','))
2926 ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
2927 else
2928 {
2929 /*
2930 * Multiple string values...
2931 */
2932
2933 int num_values; /* Number of values */
2934 char *values[100], /* Values */
2935 *ptr; /* Pointer to next value */
2936
2937
2938 values[0] = token;
2939 num_values = 1;
2940
2941 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
2942 {
2943 *ptr++ = '\0';
2944 values[num_values] = ptr;
2945 num_values ++;
2946 }
2947
2948 ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
2949 NULL, (const char **)values);
2950 }
2951 break;
2952 }
2953 }
2954 }
2955
2956 return (col);
2957
2958 /*
2959 * If we get here there was a parse error; free memory and return.
2960 */
2961
2962 col_error:
2963
2964 ippDelete(col);
2965
2966 return (NULL);
2967 }
2968
2969
2970 /*
2971 * 'get_filename()' - Get a filename based on the current test file.
2972 */
2973
2974 static char * /* O - Filename */
2975 get_filename(const char *testfile, /* I - Current test file */
2976 char *dst, /* I - Destination filename */
2977 const char *src, /* I - Source filename */
2978 size_t dstsize) /* I - Size of destination buffer */
2979 {
2980 char *dstptr; /* Pointer into destination */
2981 _cups_globals_t *cg = _cupsGlobals();
2982 /* Global data */
2983
2984
2985 if (*src == '<' && src[strlen(src) - 1] == '>')
2986 {
2987 /*
2988 * Map <filename> to CUPS_DATADIR/ipptool/filename...
2989 */
2990
2991 snprintf(dst, dstsize, "%s/ipptool/%s", cg->cups_datadir, src + 1);
2992 dstptr = dst + strlen(dst) - 1;
2993 if (*dstptr == '>')
2994 *dstptr = '\0';
2995 }
2996 else if (*src == '/' || !strchr(testfile, '/'))
2997 {
2998 /*
2999 * Use the path as-is...
3000 */
3001
3002 strlcpy(dst, src, dstsize);
3003 }
3004 else
3005 {
3006 /*
3007 * Make path relative to testfile...
3008 */
3009
3010 strlcpy(dst, testfile, dstsize);
3011 if ((dstptr = strrchr(dst, '/')) != NULL)
3012 dstptr ++;
3013 else
3014 dstptr = dst; /* Should never happen */
3015
3016 strlcpy(dstptr, src, dstsize - (dstptr - dst));
3017 }
3018
3019 return (dst);
3020 }
3021
3022
3023 /*
3024 * 'get_token()' - Get a token from a file.
3025 */
3026
3027 static char * /* O - Token from file or NULL on EOF */
3028 get_token(FILE *fp, /* I - File to read from */
3029 char *buf, /* I - Buffer to read into */
3030 int buflen, /* I - Length of buffer */
3031 int *linenum) /* IO - Current line number */
3032 {
3033 int ch, /* Character from file */
3034 quote; /* Quoting character */
3035 char *bufptr, /* Pointer into buffer */
3036 *bufend; /* End of buffer */
3037
3038
3039 for (;;)
3040 {
3041 /*
3042 * Skip whitespace...
3043 */
3044
3045 while (isspace(ch = getc(fp)))
3046 {
3047 if (ch == '\n')
3048 (*linenum) ++;
3049 }
3050
3051 /*
3052 * Read a token...
3053 */
3054
3055 if (ch == EOF)
3056 return (NULL);
3057 else if (ch == '\'' || ch == '\"')
3058 {
3059 /*
3060 * Quoted text or regular expression...
3061 */
3062
3063 quote = ch;
3064 bufptr = buf;
3065 bufend = buf + buflen - 1;
3066
3067 while ((ch = getc(fp)) != EOF)
3068 {
3069 if (ch == '\\')
3070 {
3071 /*
3072 * Escape next character...
3073 */
3074
3075 if (bufptr < bufend)
3076 *bufptr++ = ch;
3077
3078 if ((ch = getc(fp)) != EOF && bufptr < bufend)
3079 *bufptr++ = ch;
3080 }
3081 else if (ch == quote)
3082 break;
3083 else if (bufptr < bufend)
3084 *bufptr++ = ch;
3085 }
3086
3087 *bufptr = '\0';
3088
3089 return (buf);
3090 }
3091 else if (ch == '#')
3092 {
3093 /*
3094 * Comment...
3095 */
3096
3097 while ((ch = getc(fp)) != EOF)
3098 if (ch == '\n')
3099 break;
3100
3101 (*linenum) ++;
3102 }
3103 else
3104 {
3105 /*
3106 * Whitespace delimited text...
3107 */
3108
3109 ungetc(ch, fp);
3110
3111 bufptr = buf;
3112 bufend = buf + buflen - 1;
3113
3114 while ((ch = getc(fp)) != EOF)
3115 if (isspace(ch) || ch == '#')
3116 break;
3117 else if (bufptr < bufend)
3118 *bufptr++ = ch;
3119
3120 if (ch == '#')
3121 ungetc(ch, fp);
3122 else if (ch == '\n')
3123 (*linenum) ++;
3124
3125 *bufptr = '\0';
3126
3127 return (buf);
3128 }
3129 }
3130 }
3131
3132
3133 /*
3134 * 'get_variable()' - Get the value of a variable.
3135 */
3136
3137 static char * /* O - Value or NULL */
3138 get_variable(_cups_vars_t *vars, /* I - Variables */
3139 const char *name) /* I - Variable name */
3140 {
3141 _cups_var_t key, /* Search key */
3142 *match; /* Matching variable, if any */
3143
3144
3145 key.name = (char *)name;
3146 match = cupsArrayFind(vars->vars, &key);
3147
3148 return (match ? match->value : NULL);
3149 }
3150
3151
3152 /*
3153 * 'iso_date()' - Return an ISO 8601 date/time string for the given IPP dateTime
3154 * value.
3155 */
3156
3157 static char * /* O - ISO 8601 date/time string */
3158 iso_date(ipp_uchar_t *date) /* I - IPP (RFC 1903) date/time value */
3159 {
3160 unsigned year = (date[0] << 8) + date[1];
3161 /* Year */
3162 static char buffer[255]; /* String buffer */
3163
3164
3165 if (date[9] == 0 && date[10] == 0)
3166 snprintf(buffer, sizeof(buffer), "%04u-%02u-%02uT%02u:%02u:%02uZ",
3167 year, date[2], date[3], date[4], date[5], date[6]);
3168 else
3169 snprintf(buffer, sizeof(buffer), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u",
3170 year, date[2], date[3], date[4], date[5], date[6],
3171 date[8], date[9], date[10]);
3172
3173 return (buffer);
3174 }
3175
3176
3177 /*
3178 * 'password_cb()' - Password callback for authenticated tests.
3179 */
3180
3181 static const char * /* O - Password */
3182 password_cb(const char *prompt) /* I - Prompt (unused) */
3183 {
3184 (void)prompt;
3185
3186 return (Password);
3187 }
3188
3189
3190 /*
3191 * 'print_attr()' - Print an attribute on the screen.
3192 */
3193
3194 static void
3195 print_attr(ipp_attribute_t *attr, /* I - Attribute to print */
3196 ipp_tag_t *group) /* IO - Current group */
3197 {
3198 int i; /* Looping var */
3199 ipp_attribute_t *colattr; /* Collection attribute */
3200
3201
3202 if (Output == _CUPS_OUTPUT_PLIST)
3203 {
3204 if (!attr->name || (group && *group != attr->group_tag))
3205 {
3206 puts("</dict>");
3207 puts("<dict>");
3208
3209 if (group)
3210 *group = attr->group_tag;
3211 }
3212
3213 if (!attr->name)
3214 return;
3215
3216 print_xml_string("key", attr->name);
3217 if (attr->num_values > 1)
3218 puts("<array>");
3219 }
3220 else if (Output == _CUPS_OUTPUT_TEST)
3221 {
3222 if (!attr->name)
3223 {
3224 puts(" -- separator --");
3225 return;
3226 }
3227
3228 printf(" %s (%s%s) = ", attr->name,
3229 attr->num_values > 1 ? "1setOf " : "",
3230 ippTagString(attr->value_tag));
3231 }
3232
3233 switch (attr->value_tag)
3234 {
3235 case IPP_TAG_INTEGER :
3236 case IPP_TAG_ENUM :
3237 for (i = 0; i < attr->num_values; i ++)
3238 if (Output == _CUPS_OUTPUT_PLIST)
3239 printf("<integer>%d</integer>\n", attr->values[i].integer);
3240 else
3241 printf("%d ", attr->values[i].integer);
3242 break;
3243
3244 case IPP_TAG_BOOLEAN :
3245 for (i = 0; i < attr->num_values; i ++)
3246 if (Output == _CUPS_OUTPUT_PLIST)
3247 puts(attr->values[i].boolean ? "<true />" : "<false />");
3248 else if (attr->values[i].boolean)
3249 fputs("true ", stdout);
3250 else
3251 fputs("false ", stdout);
3252 break;
3253
3254 case IPP_TAG_RANGE :
3255 for (i = 0; i < attr->num_values; i ++)
3256 if (Output == _CUPS_OUTPUT_PLIST)
3257 printf("<dict><key>lower</key><integer>%d</integer>"
3258 "<key>upper</key><integer>%d</integer></dict>\n",
3259 attr->values[i].range.lower, attr->values[i].range.upper);
3260 else
3261 printf("%d-%d ", attr->values[i].range.lower,
3262 attr->values[i].range.upper);
3263 break;
3264
3265 case IPP_TAG_RESOLUTION :
3266 for (i = 0; i < attr->num_values; i ++)
3267 if (Output == _CUPS_OUTPUT_PLIST)
3268 printf("<dict><key>xres</key><integer>%d</integer>"
3269 "<key>yres</key><integer>%d</integer>"
3270 "<key>units</key><string>%s</string></dict>\n",
3271 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 else
3276 printf("%dx%d%s ", attr->values[i].resolution.xres,
3277 attr->values[i].resolution.yres,
3278 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3279 "dpi" : "dpc");
3280 break;
3281
3282 case IPP_TAG_DATE :
3283 for (i = 0; i < attr->num_values; i ++)
3284 if (Output == _CUPS_OUTPUT_PLIST)
3285 printf("<date>%s</date>\n", iso_date(attr->values[i].date));
3286 else
3287 printf("%s ", iso_date(attr->values[i].date));
3288 break;
3289
3290 case IPP_TAG_STRING :
3291 case IPP_TAG_TEXT :
3292 case IPP_TAG_NAME :
3293 case IPP_TAG_KEYWORD :
3294 case IPP_TAG_CHARSET :
3295 case IPP_TAG_URI :
3296 case IPP_TAG_MIMETYPE :
3297 case IPP_TAG_LANGUAGE :
3298 for (i = 0; i < attr->num_values; i ++)
3299 if (Output == _CUPS_OUTPUT_PLIST)
3300 print_xml_string("string", attr->values[i].string.text);
3301 else
3302 printf("\"%s\" ", attr->values[i].string.text);
3303 break;
3304
3305 case IPP_TAG_TEXTLANG :
3306 case IPP_TAG_NAMELANG :
3307 for (i = 0; i < attr->num_values; i ++)
3308 if (Output == _CUPS_OUTPUT_PLIST)
3309 {
3310 fputs("<dict><key>language</key><string>", stdout);
3311 print_xml_string(NULL, attr->values[i].string.charset);
3312 fputs("</string><key>string</key><string>", stdout);
3313 print_xml_string(NULL, attr->values[i].string.text);
3314 puts("</string></dict>");
3315 }
3316 else
3317 printf("\"%s\"(%s) ", attr->values[i].string.text,
3318 attr->values[i].string.charset);
3319 break;
3320
3321 case IPP_TAG_BEGIN_COLLECTION :
3322 for (i = 0; i < attr->num_values; i ++)
3323 {
3324 if (Output == _CUPS_OUTPUT_PLIST)
3325 {
3326 puts("<dict>");
3327 for (colattr = attr->values[i].collection->attrs;
3328 colattr;
3329 colattr = colattr->next)
3330 print_attr(colattr, NULL);
3331 puts("</dict>");
3332 }
3333 else
3334 {
3335 if (i)
3336 putchar(' ');
3337
3338 print_col(attr->values[i].collection);
3339 }
3340 }
3341 break;
3342
3343 default :
3344 if (Output == _CUPS_OUTPUT_PLIST)
3345 printf("<string>&lt;&lt;%s&gt;&gt;</string>\n",
3346 ippTagString(attr->value_tag));
3347 else
3348 fputs(ippTagString(attr->value_tag), stdout);
3349 break;
3350 }
3351
3352 if (Output == _CUPS_OUTPUT_PLIST)
3353 {
3354 if (attr->num_values > 1)
3355 puts("</array>");
3356 }
3357 else
3358 putchar('\n');
3359 }
3360
3361
3362 /*
3363 * 'print_col()' - Print a collection attribute on the screen.
3364 */
3365
3366 static void
3367 print_col(ipp_t *col) /* I - Collection attribute to print */
3368 {
3369 int i; /* Looping var */
3370 ipp_attribute_t *attr; /* Current attribute in collection */
3371
3372
3373 fputs("{ ", stdout);
3374 for (attr = col->attrs; attr; attr = attr->next)
3375 {
3376 printf("%s (%s%s) = ", attr->name, attr->num_values > 1 ? "1setOf " : "",
3377 ippTagString(attr->value_tag));
3378
3379 switch (attr->value_tag)
3380 {
3381 case IPP_TAG_INTEGER :
3382 case IPP_TAG_ENUM :
3383 for (i = 0; i < attr->num_values; i ++)
3384 printf("%d ", attr->values[i].integer);
3385 break;
3386
3387 case IPP_TAG_BOOLEAN :
3388 for (i = 0; i < attr->num_values; i ++)
3389 if (attr->values[i].boolean)
3390 printf("true ");
3391 else
3392 printf("false ");
3393 break;
3394
3395 case IPP_TAG_NOVALUE :
3396 printf("novalue");
3397 break;
3398
3399 case IPP_TAG_RANGE :
3400 for (i = 0; i < attr->num_values; i ++)
3401 printf("%d-%d ", attr->values[i].range.lower,
3402 attr->values[i].range.upper);
3403 break;
3404
3405 case IPP_TAG_RESOLUTION :
3406 for (i = 0; i < attr->num_values; i ++)
3407 printf("%dx%d%s ", attr->values[i].resolution.xres,
3408 attr->values[i].resolution.yres,
3409 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3410 "dpi" : "dpc");
3411 break;
3412
3413 case IPP_TAG_STRING :
3414 case IPP_TAG_TEXT :
3415 case IPP_TAG_NAME :
3416 case IPP_TAG_KEYWORD :
3417 case IPP_TAG_CHARSET :
3418 case IPP_TAG_URI :
3419 case IPP_TAG_MIMETYPE :
3420 case IPP_TAG_LANGUAGE :
3421 for (i = 0; i < attr->num_values; i ++)
3422 printf("\"%s\" ", attr->values[i].string.text);
3423 break;
3424
3425 case IPP_TAG_TEXTLANG :
3426 case IPP_TAG_NAMELANG :
3427 for (i = 0; i < attr->num_values; i ++)
3428 printf("\"%s\",%s ", attr->values[i].string.text,
3429 attr->values[i].string.charset);
3430 break;
3431
3432 case IPP_TAG_BEGIN_COLLECTION :
3433 for (i = 0; i < attr->num_values; i ++)
3434 {
3435 print_col(attr->values[i].collection);
3436 putchar(' ');
3437 }
3438 break;
3439
3440 default :
3441 break; /* anti-compiler-warning-code */
3442 }
3443 }
3444
3445 putchar('}');
3446 }
3447
3448
3449 /*
3450 * 'print_csv()' - Print a line of CSV text.
3451 */
3452
3453 static void
3454 print_csv(
3455 ipp_attribute_t *attr, /* I - First attribute for line */
3456 int num_displayed, /* I - Number of attributes to display */
3457 char **displayed, /* I - Attributes to display */
3458 size_t *widths) /* I - Column widths */
3459 {
3460 int i; /* Looping var */
3461 size_t maxlength; /* Max length of all columns */
3462 char *buffer, /* String buffer */
3463 *bufptr; /* Pointer into buffer */
3464 ipp_attribute_t *current; /* Current attribute */
3465
3466
3467 /*
3468 * Get the maximum string length we have to show and allocate...
3469 */
3470
3471 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3472 if (widths[i] > maxlength)
3473 maxlength = widths[i];
3474
3475 maxlength += 2;
3476
3477 if ((buffer = malloc(maxlength)) == NULL)
3478 return;
3479
3480 /*
3481 * Loop through the attributes to display...
3482 */
3483
3484 if (attr)
3485 {
3486 for (i = 0; i < num_displayed; i ++)
3487 {
3488 if (i)
3489 putchar(',');
3490
3491 buffer[0] = '\0';
3492
3493 for (current = attr; current; current = current->next)
3494 {
3495 if (!current->name)
3496 break;
3497 else if (!strcmp(current->name, displayed[i]))
3498 {
3499 _ippAttrString(current, buffer, maxlength);
3500 break;
3501 }
3502 }
3503
3504 if (strchr(buffer, ',') != NULL || strchr(buffer, '\"') != NULL ||
3505 strchr(buffer, '\\') != NULL)
3506 {
3507 putchar('\"');
3508 for (bufptr = buffer; *bufptr; bufptr ++)
3509 {
3510 if (*bufptr == '\\' || *bufptr == '\"')
3511 putchar('\\');
3512 putchar(*bufptr);
3513 }
3514 putchar('\"');
3515 }
3516 else
3517 fputs(buffer, stdout);
3518 }
3519 putchar('\n');
3520 }
3521 else
3522 {
3523 for (i = 0; i < num_displayed; i ++)
3524 {
3525 if (i)
3526 putchar(',');
3527
3528 fputs(displayed[i], stdout);
3529 }
3530 putchar('\n');
3531 }
3532
3533 free(buffer);
3534 }
3535
3536
3537 /*
3538 * 'print_fatal_error()' - Print a fatal error message.
3539 */
3540
3541 static void
3542 print_fatal_error(const char *s, /* I - Printf-style format string */
3543 ...) /* I - Additional arguments as needed */
3544 {
3545 char buffer[10240]; /* Format buffer */
3546 va_list ap; /* Pointer to arguments */
3547
3548
3549 /*
3550 * Format the error message...
3551 */
3552
3553 va_start(ap, s);
3554 vsnprintf(buffer, sizeof(buffer), s, ap);
3555 va_end(ap);
3556
3557 /*
3558 * Then output it...
3559 */
3560
3561 if (Output == _CUPS_OUTPUT_PLIST)
3562 {
3563 print_xml_header();
3564 print_xml_trailer(0, buffer);
3565 }
3566 else
3567 _cupsLangPrintf(stderr, "ipptool: %s", buffer);
3568 }
3569
3570
3571 /*
3572 * 'print_line()' - Print a line of formatted or CSV text.
3573 */
3574
3575 static void
3576 print_line(
3577 ipp_attribute_t *attr, /* I - First attribute for line */
3578 int num_displayed, /* I - Number of attributes to display */
3579 char **displayed, /* I - Attributes to display */
3580 size_t *widths) /* I - Column widths */
3581 {
3582 int i; /* Looping var */
3583 size_t maxlength; /* Max length of all columns */
3584 char *buffer; /* String buffer */
3585 ipp_attribute_t *current; /* Current attribute */
3586
3587
3588 /*
3589 * Get the maximum string length we have to show and allocate...
3590 */
3591
3592 for (i = 1, maxlength = widths[0]; i < num_displayed; i ++)
3593 if (widths[i] > maxlength)
3594 maxlength = widths[i];
3595
3596 maxlength += 2;
3597
3598 if ((buffer = malloc(maxlength)) == NULL)
3599 return;
3600
3601 /*
3602 * Loop through the attributes to display...
3603 */
3604
3605 if (attr)
3606 {
3607 for (i = 0; i < num_displayed; i ++)
3608 {
3609 if (i)
3610 putchar(' ');
3611
3612 buffer[0] = '\0';
3613
3614 for (current = attr; current; current = current->next)
3615 {
3616 if (!current->name)
3617 break;
3618 else if (!strcmp(current->name, displayed[i]))
3619 {
3620 _ippAttrString(current, buffer, maxlength);
3621 break;
3622 }
3623 }
3624
3625 printf("%*s", (int)-widths[i], buffer);
3626 }
3627 putchar('\n');
3628 }
3629 else
3630 {
3631 for (i = 0; i < num_displayed; i ++)
3632 {
3633 if (i)
3634 putchar(' ');
3635
3636 printf("%*s", (int)-widths[i], displayed[i]);
3637 }
3638 putchar('\n');
3639
3640 for (i = 0; i < num_displayed; i ++)
3641 {
3642 if (i)
3643 putchar(' ');
3644
3645 memset(buffer, '-', widths[i]);
3646 buffer[widths[i]] = '\0';
3647 fputs(buffer, stdout);
3648 }
3649 putchar('\n');
3650 }
3651
3652 free(buffer);
3653 }
3654
3655
3656 /*
3657 * 'print_test_error()' - Print a test error message.
3658 */
3659
3660 static void
3661 print_test_error(const char *s, /* I - Printf-style format string */
3662 ...) /* I - Additional arguments as needed */
3663 {
3664 char buffer[10240]; /* Format buffer */
3665 va_list ap; /* Pointer to arguments */
3666
3667
3668 /*
3669 * Format the error message...
3670 */
3671
3672 va_start(ap, s);
3673 vsnprintf(buffer, sizeof(buffer), s, ap);
3674 va_end(ap);
3675
3676 /*
3677 * Then output it...
3678 */
3679
3680 if (Output == _CUPS_OUTPUT_PLIST)
3681 print_xml_string("string", buffer);
3682 else
3683 printf(" %s\n", buffer);
3684 }
3685
3686
3687 /*
3688 * 'print_xml_header()' - Print a standard XML plist header.
3689 */
3690
3691 static void
3692 print_xml_header(void)
3693 {
3694 if (!XMLHeader)
3695 {
3696 puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
3697 puts("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
3698 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
3699 puts("<plist version=\"1.0\">");
3700 puts("<dict>");
3701 puts("<key>Transfer</key>");
3702 printf("<string>%s</string>\n",
3703 Transfer == _CUPS_TRANSFER_AUTO ? "auto" :
3704 Transfer == _CUPS_TRANSFER_CHUNKED ? "chunked" : "length");
3705 puts("<key>Tests</key>");
3706 puts("<array>");
3707
3708 XMLHeader = 1;
3709 }
3710 }
3711
3712
3713 /*
3714 * 'print_xml_string()' - Print an XML string with escaping.
3715 */
3716
3717 static void
3718 print_xml_string(const char *element, /* I - Element name or NULL */
3719 const char *s) /* I - String to print */
3720 {
3721 if (element)
3722 printf("<%s>", element);
3723
3724 while (*s)
3725 {
3726 if (*s == '&')
3727 fputs("&amp;", stdout);
3728 else if (*s == '<')
3729 fputs("&lt;", stdout);
3730 else if (*s == '>')
3731 fputs("&gt;", stdout);
3732 else
3733 putchar(*s);
3734
3735 s ++;
3736 }
3737
3738 if (element)
3739 printf("</%s>\n", element);
3740 }
3741
3742
3743 /*
3744 * 'print_xml_trailer()' - Print the XML trailer with success/fail value.
3745 */
3746
3747 static void
3748 print_xml_trailer(int success, /* I - 1 on success, 0 on failure */
3749 const char *message) /* I - Error message or NULL */
3750 {
3751 if (XMLHeader)
3752 {
3753 puts("</array>");
3754 puts("<key>Successful</key>");
3755 puts(success ? "<true />" : "<false />");
3756 if (message)
3757 {
3758 puts("<key>ErrorMessage</key>");
3759 print_xml_string("string", message);
3760 }
3761 puts("</dict>");
3762 puts("</plist>");
3763
3764 XMLHeader = 0;
3765 }
3766 }
3767
3768
3769 /*
3770 * 'set_variable()' - Set a variable value.
3771 */
3772
3773 static void
3774 set_variable(_cups_vars_t *vars, /* I - Variables */
3775 const char *name, /* I - Variable name */
3776 const char *value) /* I - Value string */
3777 {
3778 _cups_var_t key, /* Search key */
3779 *var; /* New variable */
3780
3781
3782 key.name = (char *)name;
3783 if ((var = cupsArrayFind(vars->vars, &key)) != NULL)
3784 {
3785 free(var->value);
3786 var->value = strdup(value);
3787 }
3788 else if ((var = malloc(sizeof(_cups_var_t))) == NULL)
3789 {
3790 print_fatal_error("Unable to allocate memory for variable \"%s\".", name);
3791 exit(1);
3792 }
3793 else
3794 {
3795 var->name = strdup(name);
3796 var->value = strdup(value);
3797
3798 cupsArrayAdd(vars->vars, var);
3799 }
3800 }
3801
3802
3803 /*
3804 * 'timeout_cb()' - Handle HTTP timeouts.
3805 */
3806
3807 static int /* O - 1 to continue, 0 to cancel */
3808 timeout_cb(http_t *http, /* I - Connection to server (unused) */
3809 void *user_data) /* I - User data (unused) */
3810 {
3811 (void)http;
3812 (void)user_data;
3813
3814 return (0);
3815 }
3816
3817
3818 /*
3819 * 'usage()' - Show program usage.
3820 */
3821
3822 static void
3823 usage(void)
3824 {
3825 _cupsLangPuts(stderr, _("Usage: ipptool [options] URI filename [ ... "
3826 "filenameN ]"));
3827 _cupsLangPuts(stderr, _("Options:"));
3828 _cupsLangPuts(stderr, _(" -4 Connect using IPv4."));
3829 _cupsLangPuts(stderr, _(" -6 Connect using IPv6."));
3830 _cupsLangPuts(stderr, _(" -C Send requests using "
3831 "chunking (default)."));
3832 _cupsLangPuts(stderr, _(" -E Test with TLS "
3833 "encryption."));
3834 _cupsLangPuts(stderr, _(" -I Ignore errors."));
3835 _cupsLangPuts(stderr, _(" -L Send requests using "
3836 "content-length."));
3837 _cupsLangPuts(stderr, _(" -S Test with SSL "
3838 "encryption."));
3839 _cupsLangPuts(stderr, _(" -T Set the receive/send "
3840 "timeout in seconds."));
3841 _cupsLangPuts(stderr, _(" -V version Set default IPP "
3842 "version."));
3843 _cupsLangPuts(stderr, _(" -X Produce XML plist instead "
3844 "of plain text."));
3845 _cupsLangPuts(stderr, _(" -d name=value Set named variable to "
3846 "value."));
3847 _cupsLangPuts(stderr, _(" -f filename Set default request "
3848 "filename."));
3849 _cupsLangPuts(stderr, _(" -i seconds Repeat the last file with "
3850 "the given time interval."));
3851 _cupsLangPuts(stderr, _(" -n count Repeat the last file the "
3852 "given number of times."));
3853 _cupsLangPuts(stderr, _(" -q Be quiet - no output "
3854 "except errors."));
3855 _cupsLangPuts(stderr, _(" -t Produce a test report."));
3856 _cupsLangPuts(stderr, _(" -v Show all attributes sent "
3857 "and received."));
3858
3859 exit(1);
3860 }
3861
3862
3863 /*
3864 * 'validate_attr()' - Determine whether an attribute is valid.
3865 */
3866
3867 static int /* O - 1 if valid, 0 otherwise */
3868 validate_attr(ipp_attribute_t *attr, /* I - Attribute to validate */
3869 int print) /* I - 1 = report issues to stdout */
3870 {
3871 int i; /* Looping var */
3872 char scheme[64], /* Scheme from URI */
3873 userpass[256], /* Username/password from URI */
3874 hostname[256], /* Hostname from URI */
3875 resource[1024]; /* Resource from URI */
3876 int port, /* Port number from URI */
3877 uri_status, /* URI separation status */
3878 valid = 1; /* Is the attribute valid? */
3879 const char *ptr; /* Pointer into string */
3880 ipp_attribute_t *colattr; /* Collection attribute */
3881 regex_t re; /* Regular expression */
3882 ipp_uchar_t *date; /* Current date value */
3883
3884
3885 /*
3886 * Skip separators.
3887 */
3888
3889 if (!attr->name)
3890 return (1);
3891
3892 /*
3893 * Validate the attribute name.
3894 */
3895
3896 for (ptr = attr->name; *ptr; ptr ++)
3897 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
3898 break;
3899
3900 if (*ptr || ptr == attr->name)
3901 {
3902 valid = 0;
3903
3904 if (print)
3905 print_test_error("\"%s\": Bad attribute name - invalid character (RFC "
3906 "2911 section 4.1.3).", attr->name);
3907 }
3908
3909 if ((ptr - attr->name) > 255)
3910 {
3911 valid = 0;
3912
3913 if (print)
3914 print_test_error("\"%s\": Bad attribute name - bad length (RFC 2911 "
3915 "section 4.1.3).", attr->name);
3916 }
3917
3918 switch (attr->value_tag)
3919 {
3920 case IPP_TAG_INTEGER :
3921 break;
3922
3923 case IPP_TAG_BOOLEAN :
3924 for (i = 0; i < attr->num_values; i ++)
3925 {
3926 if (attr->values[i].boolean != 0 &&
3927 attr->values[i].boolean != 1)
3928 {
3929 valid = 0;
3930
3931 if (print)
3932 print_test_error("\"%s\": Bad boolen value %d (RFC 2911 section "
3933 "4.1.10).", attr->name, attr->values[i].boolean);
3934 else
3935 break;
3936 }
3937 }
3938 break;
3939
3940 case IPP_TAG_ENUM :
3941 for (i = 0; i < attr->num_values; i ++)
3942 {
3943 if (attr->values[i].integer < 1)
3944 {
3945 valid = 0;
3946
3947 if (print)
3948 print_test_error("\"%s\": Bad enum value %d - out of range "
3949 "(RFC 2911 section 4.1.4).", attr->name,
3950 attr->values[i].integer);
3951 else
3952 break;
3953 }
3954 }
3955 break;
3956
3957 case IPP_TAG_STRING :
3958 for (i = 0; i < attr->num_values; i ++)
3959 {
3960 if (attr->values[i].unknown.length > 1023)
3961 {
3962 valid = 0;
3963
3964 if (print)
3965 print_test_error("\"%s\": Bad octetString value - bad length %d "
3966 "(RFC 2911 section 4.1.10).", attr->name,
3967 attr->values[i].unknown.length);
3968 else
3969 break;
3970 }
3971 }
3972 break;
3973
3974 case IPP_TAG_DATE :
3975 for (i = 0; i < attr->num_values; i ++)
3976 {
3977 date = attr->values[i].date;
3978
3979 if (date[2] < 1 || date[2] > 12)
3980 {
3981 valid = 0;
3982
3983 if (print)
3984 print_test_error("\"%s\": Bad dateTime month %u (RFC 2911 "
3985 "section 4.1.13).", attr->name, date[2]);
3986 else
3987 break;
3988 }
3989
3990 if (date[3] < 1 || date[3] > 31)
3991 {
3992 valid = 0;
3993
3994 if (print)
3995 print_test_error("\"%s\": Bad dateTime day %u (RFC 2911 "
3996 "section 4.1.13).", attr->name, date[3]);
3997 else
3998 break;
3999 }
4000
4001 if (date[4] > 23)
4002 {
4003 valid = 0;
4004
4005 if (print)
4006 print_test_error("\"%s\": Bad dateTime hours %u (RFC 2911 "
4007 "section 4.1.13).", attr->name, date[4]);
4008 else
4009 break;
4010 }
4011
4012 if (date[5] > 59)
4013 {
4014 valid = 0;
4015
4016 if (print)
4017 print_test_error("\"%s\": Bad dateTime minutes %u (RFC 2911 "
4018 "section 4.1.13).", attr->name, date[5]);
4019 else
4020 break;
4021 }
4022
4023 if (date[6] > 60)
4024 {
4025 valid = 0;
4026
4027 if (print)
4028 print_test_error("\"%s\": Bad dateTime seconds %u (RFC 2911 "
4029 "section 4.1.13).", attr->name, date[6]);
4030 else
4031 break;
4032 }
4033
4034 if (date[7] > 9)
4035 {
4036 valid = 0;
4037
4038 if (print)
4039 print_test_error("\"%s\": Bad dateTime deciseconds %u (RFC 2911 "
4040 "section 4.1.13).", attr->name, date[7]);
4041 else
4042 break;
4043 }
4044
4045 if (date[8] != '-' && date[8] != '+')
4046 {
4047 valid = 0;
4048
4049 if (print)
4050 print_test_error("\"%s\": Bad dateTime UTC sign '%c' (RFC 2911 "
4051 "section 4.1.13).", attr->name, date[8]);
4052 else
4053 break;
4054 }
4055
4056 if (date[9] > 11)
4057 {
4058 valid = 0;
4059
4060 if (print)
4061 print_test_error("\"%s\": Bad dateTime UTC hours %u (RFC 2911 "
4062 "section 4.1.13).", attr->name, date[9]);
4063 else
4064 break;
4065 }
4066
4067 if (date[10] > 59)
4068 {
4069 valid = 0;
4070
4071 if (print)
4072 print_test_error("\"%s\": Bad dateTime UTC minutes %u (RFC 2911 "
4073 "section 4.1.13).", attr->name, date[10]);
4074 else
4075 break;
4076 }
4077 }
4078 break;
4079
4080 case IPP_TAG_RESOLUTION :
4081 for (i = 0; i < attr->num_values; i ++)
4082 {
4083 if (attr->values[i].resolution.xres <= 0)
4084 {
4085 valid = 0;
4086
4087 if (print)
4088 print_test_error("\"%s\": Bad resolution value %dx%d%s - cross "
4089 "feed resolution must be positive (RFC 2911 "
4090 "section 4.1.13).", attr->name,
4091 attr->values[i].resolution.xres,
4092 attr->values[i].resolution.yres,
4093 attr->values[i].resolution.units ==
4094 IPP_RES_PER_INCH ? "dpi" :
4095 attr->values[i].resolution.units ==
4096 IPP_RES_PER_CM ? "dpc" : "unknown");
4097 else
4098 break;
4099 }
4100
4101 if (attr->values[i].resolution.yres <= 0)
4102 {
4103 valid = 0;
4104
4105 if (print)
4106 print_test_error("\"%s\": Bad resolution value %dx%d%s - feed "
4107 "resolution must be positive (RFC 2911 section "
4108 "4.1.13).", attr->name,
4109 attr->values[i].resolution.xres,
4110 attr->values[i].resolution.yres,
4111 attr->values[i].resolution.units ==
4112 IPP_RES_PER_INCH ? "dpi" :
4113 attr->values[i].resolution.units ==
4114 IPP_RES_PER_CM ? "dpc" : "unknown");
4115 else
4116 break;
4117 }
4118
4119 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
4120 attr->values[i].resolution.units != IPP_RES_PER_CM)
4121 {
4122 valid = 0;
4123
4124 if (print)
4125 print_test_error("\"%s\": Bad resolution value %dx%d%s - bad "
4126 "units value (RFC 2911 section 4.1.13).",
4127 attr->name, attr->values[i].resolution.xres,
4128 attr->values[i].resolution.yres,
4129 attr->values[i].resolution.units ==
4130 IPP_RES_PER_INCH ? "dpi" :
4131 attr->values[i].resolution.units ==
4132 IPP_RES_PER_CM ? "dpc" : "unknown");
4133 else
4134 break;
4135 }
4136 }
4137 break;
4138
4139 case IPP_TAG_RANGE :
4140 for (i = 0; i < attr->num_values; i ++)
4141 {
4142 if (attr->values[i].range.lower > attr->values[i].range.upper)
4143 {
4144 valid = 0;
4145
4146 if (print)
4147 print_test_error("\"%s\": Bad rangeOfInteger value %d-%d - lower "
4148 "greater than upper (RFC 2911 section 4.1.13).",
4149 attr->name, attr->values[i].range.lower,
4150 attr->values[i].range.upper);
4151 else
4152 break;
4153 }
4154 }
4155 break;
4156
4157 case IPP_TAG_BEGIN_COLLECTION :
4158 for (i = 0; i < attr->num_values; i ++)
4159 {
4160 for (colattr = attr->values[i].collection->attrs;
4161 colattr;
4162 colattr = colattr->next)
4163 {
4164 if (!validate_attr(colattr, 0))
4165 {
4166 valid = 0;
4167 break;
4168 }
4169 }
4170
4171 if (colattr && print)
4172 {
4173 print_test_error("\"%s\": Bad collection value.", attr->name);
4174
4175 while (colattr)
4176 {
4177 validate_attr(colattr, print);
4178 colattr = colattr->next;
4179 }
4180 }
4181 }
4182 break;
4183
4184 case IPP_TAG_TEXT :
4185 case IPP_TAG_TEXTLANG :
4186 for (i = 0; i < attr->num_values; i ++)
4187 {
4188 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4189 {
4190 if ((*ptr & 0xe0) == 0xc0)
4191 {
4192 ptr ++;
4193 if ((*ptr & 0xc0) != 0x80)
4194 break;
4195 }
4196 else if ((*ptr & 0xf0) == 0xe0)
4197 {
4198 ptr ++;
4199 if ((*ptr & 0xc0) != 0x80)
4200 break;
4201 ptr ++;
4202 if ((*ptr & 0xc0) != 0x80)
4203 break;
4204 }
4205 else if ((*ptr & 0xf8) == 0xf0)
4206 {
4207 ptr ++;
4208 if ((*ptr & 0xc0) != 0x80)
4209 break;
4210 ptr ++;
4211 if ((*ptr & 0xc0) != 0x80)
4212 break;
4213 ptr ++;
4214 if ((*ptr & 0xc0) != 0x80)
4215 break;
4216 }
4217 else if (*ptr & 0x80)
4218 break;
4219 }
4220
4221 if (*ptr)
4222 {
4223 valid = 0;
4224
4225 if (print)
4226 print_test_error("\"%s\": Bad text value \"%s\" - bad UTF-8 "
4227 "sequence (RFC 2911 section 4.1.1).", attr->name,
4228 attr->values[i].string.text);
4229 else
4230 break;
4231 }
4232
4233 if ((ptr - attr->values[i].string.text) > 1023)
4234 {
4235 valid = 0;
4236
4237 if (print)
4238 print_test_error("\"%s\": Bad text value \"%s\" - bad length %d "
4239 "(RFC 2911 section 4.1.1).", attr->name,
4240 attr->values[i].string.text,
4241 (int)strlen(attr->values[i].string.text));
4242 else
4243 break;
4244 }
4245 }
4246 break;
4247
4248 case IPP_TAG_NAME :
4249 case IPP_TAG_NAMELANG :
4250 for (i = 0; i < attr->num_values; i ++)
4251 {
4252 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4253 {
4254 if ((*ptr & 0xe0) == 0xc0)
4255 {
4256 ptr ++;
4257 if ((*ptr & 0xc0) != 0x80)
4258 break;
4259 }
4260 else if ((*ptr & 0xf0) == 0xe0)
4261 {
4262 ptr ++;
4263 if ((*ptr & 0xc0) != 0x80)
4264 break;
4265 ptr ++;
4266 if ((*ptr & 0xc0) != 0x80)
4267 break;
4268 }
4269 else if ((*ptr & 0xf8) == 0xf0)
4270 {
4271 ptr ++;
4272 if ((*ptr & 0xc0) != 0x80)
4273 break;
4274 ptr ++;
4275 if ((*ptr & 0xc0) != 0x80)
4276 break;
4277 ptr ++;
4278 if ((*ptr & 0xc0) != 0x80)
4279 break;
4280 }
4281 else if (*ptr & 0x80)
4282 break;
4283 }
4284
4285 if (*ptr)
4286 {
4287 valid = 0;
4288
4289 if (print)
4290 print_test_error("\"%s\": Bad name value \"%s\" - bad UTF-8 "
4291 "sequence (RFC 2911 section 4.1.2).", attr->name,
4292 attr->values[i].string.text);
4293 else
4294 break;
4295 }
4296
4297 if ((ptr - attr->values[i].string.text) > 1023)
4298 {
4299 valid = 0;
4300
4301 if (print)
4302 print_test_error("\"%s\": Bad name value \"%s\" - bad length %d "
4303 "(RFC 2911 section 4.1.2).", attr->name,
4304 attr->values[i].string.text,
4305 (int)strlen(attr->values[i].string.text));
4306 else
4307 break;
4308 }
4309 }
4310 break;
4311
4312 case IPP_TAG_KEYWORD :
4313 for (i = 0; i < attr->num_values; i ++)
4314 {
4315 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4316 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
4317 *ptr != '_')
4318 break;
4319
4320 if (*ptr || ptr == attr->values[i].string.text)
4321 {
4322 valid = 0;
4323
4324 if (print)
4325 print_test_error("\"%s\": Bad keyword value \"%s\" - invalid "
4326 "character (RFC 2911 section 4.1.3).",
4327 attr->name, attr->values[i].string.text);
4328 else
4329 break;
4330 }
4331
4332 if ((ptr - attr->values[i].string.text) > 255)
4333 {
4334 valid = 0;
4335
4336 if (print)
4337 print_test_error("\"%s\": Bad keyword value \"%s\" - bad "
4338 "length %d (RFC 2911 section 4.1.3).",
4339 attr->name, attr->values[i].string.text,
4340 (int)strlen(attr->values[i].string.text));
4341 else
4342 break;
4343 }
4344 }
4345 break;
4346
4347 case IPP_TAG_URI :
4348 for (i = 0; i < attr->num_values; i ++)
4349 {
4350 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
4351 attr->values[i].string.text,
4352 scheme, sizeof(scheme),
4353 userpass, sizeof(userpass),
4354 hostname, sizeof(hostname),
4355 &port, resource, sizeof(resource));
4356
4357 if (uri_status < HTTP_URI_OK)
4358 {
4359 valid = 0;
4360
4361 if (print)
4362 print_test_error("\"%s\": Bad URI value \"%s\" - %s "
4363 "(RFC 2911 section 4.1.5).", attr->name,
4364 attr->values[i].string.text,
4365 URIStatusStrings[uri_status -
4366 HTTP_URI_OVERFLOW]);
4367 else
4368 break;
4369 }
4370
4371 if (strlen(attr->values[i].string.text) > 1023)
4372 {
4373 valid = 0;
4374
4375 if (print)
4376 print_test_error("\"%s\": Bad URI value \"%s\" - bad length %d "
4377 "(RFC 2911 section 4.1.5).", attr->name,
4378 attr->values[i].string.text,
4379 (int)strlen(attr->values[i].string.text));
4380 else
4381 break;
4382 }
4383 }
4384 break;
4385
4386 case IPP_TAG_URISCHEME :
4387 for (i = 0; i < attr->num_values; i ++)
4388 {
4389 ptr = attr->values[i].string.text;
4390 if (islower(*ptr & 255))
4391 {
4392 for (ptr ++; *ptr; ptr ++)
4393 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
4394 *ptr != '+' && *ptr != '-' && *ptr != '.')
4395 break;
4396 }
4397
4398 if (*ptr || ptr == attr->values[i].string.text)
4399 {
4400 valid = 0;
4401
4402 if (print)
4403 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4404 "characters (RFC 2911 section 4.1.6).",
4405 attr->name, attr->values[i].string.text);
4406 else
4407 break;
4408 }
4409
4410 if ((ptr - attr->values[i].string.text) > 63)
4411 {
4412 valid = 0;
4413
4414 if (print)
4415 print_test_error("\"%s\": Bad uriScheme value \"%s\" - bad "
4416 "length %d (RFC 2911 section 4.1.6).",
4417 attr->name, attr->values[i].string.text,
4418 (int)strlen(attr->values[i].string.text));
4419 else
4420 break;
4421 }
4422 }
4423 break;
4424
4425 case IPP_TAG_CHARSET :
4426 for (i = 0; i < attr->num_values; i ++)
4427 {
4428 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4429 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
4430 isspace(*ptr & 255))
4431 break;
4432
4433 if (*ptr || ptr == attr->values[i].string.text)
4434 {
4435 valid = 0;
4436
4437 if (print)
4438 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4439 "characters (RFC 2911 section 4.1.7).",
4440 attr->name, attr->values[i].string.text);
4441 else
4442 break;
4443 }
4444
4445 if ((ptr - attr->values[i].string.text) > 40)
4446 {
4447 valid = 0;
4448
4449 if (print)
4450 print_test_error("\"%s\": Bad charset value \"%s\" - bad "
4451 "length %d (RFC 2911 section 4.1.7).",
4452 attr->name, attr->values[i].string.text,
4453 (int)strlen(attr->values[i].string.text));
4454 else
4455 break;
4456 }
4457 }
4458 break;
4459
4460 case IPP_TAG_LANGUAGE :
4461 /*
4462 * The following regular expression is derived from the ABNF for
4463 * language tags in RFC 4646. All I can say is that this is the
4464 * easiest way to check the values...
4465 */
4466
4467 if ((i = regcomp(&re,
4468 "^("
4469 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4470 /* language */
4471 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
4472 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
4473 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
4474 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
4475 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
4476 "|"
4477 "x(-[a-z0-9]{1,8})+" /* privateuse */
4478 "|"
4479 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
4480 ")$",
4481 REG_NOSUB | REG_EXTENDED)) != 0)
4482 {
4483 char temp[256]; /* Temporary error string */
4484
4485 regerror(i, &re, temp, sizeof(temp));
4486 print_fatal_error("Unable to compile naturalLanguage regular "
4487 "expression: %s.", temp);
4488 }
4489
4490 for (i = 0; i < attr->num_values; i ++)
4491 {
4492 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4493 {
4494 valid = 0;
4495
4496 if (print)
4497 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4498 "characters (RFC 2911 section 4.1.8).",
4499 attr->name, attr->values[i].string.text);
4500 else
4501 break;
4502 }
4503
4504 if (strlen(attr->values[i].string.text) > 63)
4505 {
4506 valid = 0;
4507
4508 if (print)
4509 print_test_error("\"%s\": Bad naturalLanguage value \"%s\" - bad "
4510 "length %d (RFC 2911 section 4.1.8).",
4511 attr->name, attr->values[i].string.text,
4512 (int)strlen(attr->values[i].string.text));
4513 else
4514 break;
4515 }
4516 }
4517
4518 regfree(&re);
4519 break;
4520
4521 case IPP_TAG_MIMETYPE :
4522 /*
4523 * The following regular expression is derived from the ABNF for
4524 * language tags in RFC 2045 and 4288. All I can say is that this is
4525 * the easiest way to check the values...
4526 */
4527
4528 if ((i = regcomp(&re,
4529 "^"
4530 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
4531 "/"
4532 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
4533 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
4534 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4535 /* value */
4536 "$",
4537 REG_NOSUB | REG_EXTENDED)) != 0)
4538 {
4539 char temp[256]; /* Temporary error string */
4540
4541 regerror(i, &re, temp, sizeof(temp));
4542 print_fatal_error("Unable to compile mimeMediaType regular "
4543 "expression: %s.", temp);
4544 }
4545
4546 for (i = 0; i < attr->num_values; i ++)
4547 {
4548 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4549 {
4550 valid = 0;
4551
4552 if (print)
4553 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4554 "characters (RFC 2911 section 4.1.9).",
4555 attr->name, attr->values[i].string.text);
4556 else
4557 break;
4558 }
4559
4560 if (strlen(attr->values[i].string.text) > 255)
4561 {
4562 valid = 0;
4563
4564 if (print)
4565 print_test_error("\"%s\": Bad mimeMediaType value \"%s\" - bad "
4566 "length %d (RFC 2911 section 4.1.9).",
4567 attr->name, attr->values[i].string.text,
4568 (int)strlen(attr->values[i].string.text));
4569 else
4570 break;
4571 }
4572 }
4573 break;
4574
4575 default :
4576 break;
4577 }
4578
4579 return (valid);
4580 }
4581
4582
4583 /*
4584 * 'with_value()' - Test a WITH-VALUE predicate.
4585 */
4586
4587 static int /* O - 1 on match, 0 on non-match */
4588 with_value(char *value, /* I - Value string */
4589 int regex, /* I - Value is a regular expression */
4590 ipp_attribute_t *attr, /* I - Attribute to compare */
4591 int report) /* I - 1 = report failures */
4592 {
4593 int i; /* Looping var */
4594 char *valptr; /* Pointer into value */
4595
4596
4597 /*
4598 * NULL matches everything.
4599 */
4600
4601 if (!value || !*value)
4602 return (1);
4603
4604 /*
4605 * Compare the value string to the attribute value.
4606 */
4607
4608 switch (attr->value_tag)
4609 {
4610 case IPP_TAG_INTEGER :
4611 case IPP_TAG_ENUM :
4612 for (i = 0; i < attr->num_values; i ++)
4613 {
4614 char op, /* Comparison operator */
4615 *nextptr; /* Next pointer */
4616 int intvalue; /* Integer value */
4617
4618
4619 valptr = value;
4620 if (!strncmp(valptr, "no-value,", 9))
4621 valptr += 9;
4622
4623 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
4624 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
4625 *valptr == '=' || *valptr == '>')
4626 {
4627 op = '=';
4628 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
4629 {
4630 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
4631 op = *valptr;
4632 valptr ++;
4633 }
4634
4635 if (!*valptr)
4636 break;
4637
4638 intvalue = strtol(valptr, &nextptr, 0);
4639 if (nextptr == valptr)
4640 break;
4641 valptr = nextptr;
4642
4643 switch (op)
4644 {
4645 case '=' :
4646 if (attr->values[i].integer == intvalue)
4647 return (1);
4648 break;
4649 case '<' :
4650 if (attr->values[i].integer < intvalue)
4651 return (1);
4652 break;
4653 case '>' :
4654 if (attr->values[i].integer > intvalue)
4655 return (1);
4656 break;
4657 }
4658 }
4659 }
4660
4661 if (report)
4662 {
4663 for (i = 0; i < attr->num_values; i ++)
4664 print_test_error("GOT: %s=%d", attr->name, attr->values[i].integer);
4665 }
4666 break;
4667
4668 case IPP_TAG_RANGE :
4669 for (i = 0; i < attr->num_values; i ++)
4670 {
4671 char op, /* Comparison operator */
4672 *nextptr; /* Next pointer */
4673 int intvalue; /* Integer value */
4674
4675
4676 valptr = value;
4677 if (!strncmp(valptr, "no-value,", 9))
4678 valptr += 9;
4679
4680 while (isspace(*valptr & 255) || isdigit(*valptr & 255) ||
4681 *valptr == '-' || *valptr == ',' || *valptr == '<' ||
4682 *valptr == '=' || *valptr == '>')
4683 {
4684 op = '=';
4685 while (*valptr && !isdigit(*valptr & 255) && *valptr != '-')
4686 {
4687 if (*valptr == '<' || *valptr == '>' || *valptr == '=')
4688 op = *valptr;
4689 valptr ++;
4690 }
4691
4692 if (!*valptr)
4693 break;
4694
4695 intvalue = strtol(valptr, &nextptr, 0);
4696 if (nextptr == valptr)
4697 break;
4698 valptr = nextptr;
4699
4700 switch (op)
4701 {
4702 case '=' :
4703 if (attr->values[i].range.lower == intvalue ||
4704 attr->values[i].range.upper == intvalue)
4705 return (1);
4706 break;
4707 case '<' :
4708 if (attr->values[i].range.upper < intvalue)
4709 return (1);
4710 break;
4711 case '>' :
4712 if (attr->values[i].range.upper > intvalue)
4713 return (1);
4714 break;
4715 }
4716 }
4717 }
4718
4719 if (report)
4720 {
4721 for (i = 0; i < attr->num_values; i ++)
4722 print_test_error("GOT: %s=%d-%d", attr->name,
4723 attr->values[i].range.lower,
4724 attr->values[i].range.upper);
4725 }
4726 break;
4727
4728 case IPP_TAG_BOOLEAN :
4729 for (i = 0; i < attr->num_values; i ++)
4730 {
4731 if (!strcmp(value, "true") == attr->values[i].boolean)
4732 return (1);
4733 }
4734
4735 if (report)
4736 {
4737 for (i = 0; i < attr->num_values; i ++)
4738 print_test_error("GOT: %s=%s", attr->name,
4739 attr->values[i].boolean ? "true" : "false");
4740 }
4741 break;
4742
4743 case IPP_TAG_NOVALUE :
4744 return (!strcmp(value, "no-value") || !strncmp(value, "no-value,", 9));
4745
4746 case IPP_TAG_CHARSET :
4747 case IPP_TAG_KEYWORD :
4748 case IPP_TAG_LANGUAGE :
4749 case IPP_TAG_MIMETYPE :
4750 case IPP_TAG_NAME :
4751 case IPP_TAG_NAMELANG :
4752 case IPP_TAG_TEXT :
4753 case IPP_TAG_TEXTLANG :
4754 case IPP_TAG_URI :
4755 case IPP_TAG_URISCHEME :
4756 if (regex)
4757 {
4758 /*
4759 * Value is an extended, case-sensitive POSIX regular expression...
4760 */
4761
4762 regex_t re; /* Regular expression */
4763
4764 if ((i = regcomp(&re, value, REG_EXTENDED | REG_NOSUB)) != 0)
4765 {
4766 char temp[256]; /* Temporary string */
4767
4768 regerror(i, &re, temp, sizeof(temp));
4769
4770 print_fatal_error("Unable to compile WITH-VALUE regular expression "
4771 "\"%s\" - %s", value, temp);
4772 return (0);
4773 }
4774
4775 /*
4776 * See if ALL of the values match the given regular expression.
4777 */
4778
4779 for (i = 0; i < attr->num_values; i ++)
4780 {
4781 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4782 {
4783 if (report)
4784 print_test_error("GOT: %s=\"%s\"", attr->name,
4785 attr->values[i].string.text);
4786 else
4787 break;
4788 }
4789 }
4790
4791 regfree(&re);
4792
4793 return (i == attr->num_values);
4794 }
4795 else
4796 {
4797 /*
4798 * Value is a literal string, see if at least one value matches the
4799 * literal string...
4800 */
4801
4802 for (i = 0; i < attr->num_values; i ++)
4803 {
4804 if (!strcmp(value, attr->values[i].string.text))
4805 return (1);
4806 }
4807
4808 if (report)
4809 {
4810 for (i = 0; i < attr->num_values; i ++)
4811 print_test_error("GOT: %s=\"%s\"", attr->name,
4812 attr->values[i].string.text);
4813 }
4814 }
4815 break;
4816
4817 default :
4818 break;
4819 }
4820
4821 return (0);
4822 }
4823
4824
4825 /*
4826 * End of "$Id$".
4827 */