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