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