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