Imported Upstream version 2.2.9
[platform/upstream/cups.git] / cups / testdest.c
1 /*
2  * CUPS destination API test program for CUPS.
3  *
4  * Copyright 2012-2018 by Apple Inc.
5  *
6  * These coded instructions, statements, and computer programs are the
7  * property of Apple Inc. and are protected by Federal copyright
8  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
9  * which should have been included with this file.  If this file is
10  * missing or damaged, see the license at "http://www.cups.org/".
11  *
12  * This file is subject to the Apple OS-Developed Software exception.
13  */
14
15 /*
16  * Include necessary headers...
17  */
18
19 #include <stdio.h>
20 #include <errno.h>
21 #include "cups.h"
22
23
24 /*
25  * Local functions...
26  */
27
28 static int      enum_cb(void *user_data, unsigned flags, cups_dest_t *dest);
29 static void     localize(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value);
30 static void     print_file(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *filename, int num_options, cups_option_t *options);
31 static void     show_conflicts(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, int num_options, cups_option_t *options);
32 static void     show_default(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option);
33 static void     show_media(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, unsigned flags, const char *name);
34 static void     show_supported(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value);
35 static void     usage(const char *arg) _CUPS_NORETURN;
36
37
38 /*
39  * 'main()' - Main entry.
40  */
41
42 int                                     /* O - Exit status */
43 main(int  argc,                         /* I - Number of command-line arguments */
44      char *argv[])                      /* I - Command-line arguments */
45 {
46   http_t        *http;                  /* Connection to destination */
47   cups_dest_t   *dest = NULL;           /* Destination */
48   cups_dinfo_t  *dinfo;                 /* Destination info */
49
50
51   if (argc < 2)
52     usage(NULL);
53
54   if (!strcmp(argv[1], "--enum"))
55   {
56     int                 i;              /* Looping var */
57     cups_ptype_t        type = 0,       /* Printer type filter */
58                         mask = 0;       /* Printer type mask */
59
60
61     for (i = 2; i < argc; i ++)
62     {
63       if (!strcmp(argv[i], "grayscale"))
64       {
65         type |= CUPS_PRINTER_BW;
66         mask |= CUPS_PRINTER_BW;
67       }
68       else if (!strcmp(argv[i], "color"))
69       {
70         type |= CUPS_PRINTER_COLOR;
71         mask |= CUPS_PRINTER_COLOR;
72       }
73       else if (!strcmp(argv[i], "duplex"))
74       {
75         type |= CUPS_PRINTER_DUPLEX;
76         mask |= CUPS_PRINTER_DUPLEX;
77       }
78       else if (!strcmp(argv[i], "staple"))
79       {
80         type |= CUPS_PRINTER_STAPLE;
81         mask |= CUPS_PRINTER_STAPLE;
82       }
83       else if (!strcmp(argv[i], "small"))
84       {
85         type |= CUPS_PRINTER_SMALL;
86         mask |= CUPS_PRINTER_SMALL;
87       }
88       else if (!strcmp(argv[i], "medium"))
89       {
90         type |= CUPS_PRINTER_MEDIUM;
91         mask |= CUPS_PRINTER_MEDIUM;
92       }
93       else if (!strcmp(argv[i], "large"))
94       {
95         type |= CUPS_PRINTER_LARGE;
96         mask |= CUPS_PRINTER_LARGE;
97       }
98       else
99         usage(argv[i]);
100     }
101
102     cupsEnumDests(CUPS_DEST_FLAGS_NONE, 5000, NULL, type, mask, enum_cb, NULL);
103
104     return (0);
105   }
106   else if (!strncmp(argv[1], "ipp://", 6) || !strncmp(argv[1], "ipps://", 7))
107     dest = cupsGetDestWithURI(NULL, argv[1]);
108   else if (!strcmp(argv[1], "default"))
109   {
110     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
111     if (dest && dest->instance)
112       printf("default is \"%s/%s\".\n", dest->name, dest->instance);
113     else
114       printf("default is \"%s\".\n", dest->name);
115   }
116   else
117     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[1], NULL);
118
119   if (!dest)
120   {
121     printf("testdest: Unable to get destination \"%s\": %s\n", argv[1], cupsLastErrorString());
122     return (1);
123   }
124
125   if ((http = cupsConnectDest(dest, CUPS_DEST_FLAGS_NONE, 30000, NULL, NULL, 0, NULL, NULL)) == NULL)
126   {
127     printf("testdest: Unable to connect to destination \"%s\": %s\n", argv[1], cupsLastErrorString());
128     return (1);
129   }
130
131   if ((dinfo = cupsCopyDestInfo(http, dest)) == NULL)
132   {
133     printf("testdest: Unable to get information for destination \"%s\": %s\n", argv[1], cupsLastErrorString());
134     return (1);
135   }
136
137   if (argc == 2 || (!strcmp(argv[2], "supported") && argc < 6))
138   {
139     if (argc > 3)
140       show_supported(http, dest, dinfo, argv[3], argv[4]);
141     else if (argc > 2)
142       show_supported(http, dest, dinfo, argv[3], NULL);
143     else
144       show_supported(http, dest, dinfo, NULL, NULL);
145   }
146   else if (!strcmp(argv[2], "conflicts") && argc > 3)
147   {
148     int                 i,              /* Looping var */
149                         num_options = 0;/* Number of options */
150     cups_option_t       *options = NULL;/* Options */
151
152     for (i = 3; i < argc; i ++)
153       num_options = cupsParseOptions(argv[i], num_options, &options);
154
155     show_conflicts(http, dest, dinfo, num_options, options);
156   }
157   else if (!strcmp(argv[2], "default") && argc == 4)
158   {
159     show_default(http, dest, dinfo, argv[3]);
160   }
161   else if (!strcmp(argv[2], "localize") && argc < 6)
162   {
163     if (argc > 3)
164       localize(http, dest, dinfo, argv[3], argv[4]);
165     else if (argc > 2)
166       localize(http, dest, dinfo, argv[3], NULL);
167     else
168       localize(http, dest, dinfo, NULL, NULL);
169   }
170   else if (!strcmp(argv[2], "media"))
171   {
172     int         i;                      /* Looping var */
173     const char  *name = NULL;           /* Media name, if any */
174     unsigned    flags = CUPS_MEDIA_FLAGS_DEFAULT;
175                                         /* Media selection flags */
176
177     for (i = 3; i < argc; i ++)
178     {
179       if (!strcmp(argv[i], "borderless"))
180         flags = CUPS_MEDIA_FLAGS_BORDERLESS;
181       else if (!strcmp(argv[i], "duplex"))
182         flags = CUPS_MEDIA_FLAGS_DUPLEX;
183       else if (!strcmp(argv[i], "exact"))
184         flags = CUPS_MEDIA_FLAGS_EXACT;
185       else if (!strcmp(argv[i], "ready"))
186         flags = CUPS_MEDIA_FLAGS_READY;
187       else if (name)
188         usage(argv[i]);
189       else
190         name = argv[i];
191     }
192
193     show_media(http, dest, dinfo, flags, name);
194   }
195   else if (!strcmp(argv[2], "print") && argc > 3)
196   {
197     int                 i,              /* Looping var */
198                         num_options = 0;/* Number of options */
199     cups_option_t       *options = NULL;/* Options */
200
201     for (i = 4; i < argc; i ++)
202       num_options = cupsParseOptions(argv[i], num_options, &options);
203
204     print_file(http, dest, dinfo, argv[3], num_options, options);
205   }
206   else
207     usage(argv[2]);
208
209   return (0);
210 }
211
212
213 /*
214  * 'enum_cb()' - Print the results from the enumeration of destinations.
215  */
216
217 static int                              /* O - 1 to continue */
218 enum_cb(void        *user_data,         /* I - User data (unused) */
219         unsigned    flags,              /* I - Flags */
220         cups_dest_t *dest)              /* I - Destination */
221 {
222   int   i;                              /* Looping var */
223
224
225   (void)user_data;
226   (void)flags;
227
228   if (dest->instance)
229     printf("%s%s/%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name, dest->instance);
230   else
231     printf("%s%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name);
232
233   for (i = 0; i < dest->num_options; i ++)
234     printf("    %s=\"%s\"\n", dest->options[i].name, dest->options[i].value);
235
236   return (1);
237 }
238
239
240 /*
241  * 'localize()' - Localize an option and value.
242  */
243
244 static void
245 localize(http_t       *http,            /* I - Connection to destination */
246          cups_dest_t  *dest,            /* I - Destination */
247          cups_dinfo_t *dinfo,           /* I - Destination information */
248          const char   *option,          /* I - Option */
249          const char   *value)           /* I - Value, if any */
250 {
251   ipp_attribute_t       *attr;          /* Attribute */
252   int                   i,              /* Looping var */
253                         count;          /* Number of values */
254
255
256   if (!option)
257   {
258     attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes");
259     if (attr)
260     {
261       count = ippGetCount(attr);
262       for (i = 0; i < count; i ++)
263         localize(http, dest, dinfo, ippGetString(attr, i, NULL), NULL);
264     }
265     else
266     {
267       static const char * const options[] =
268       {                                 /* List of standard options */
269         CUPS_COPIES,
270         CUPS_FINISHINGS,
271         CUPS_MEDIA,
272         CUPS_NUMBER_UP,
273         CUPS_ORIENTATION,
274         CUPS_PRINT_COLOR_MODE,
275         CUPS_PRINT_QUALITY,
276         CUPS_SIDES
277       };
278
279       puts("No job-creation-attributes-supported attribute, probing instead.");
280
281       for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++)
282         if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL))
283           localize(http, dest, dinfo, options[i], NULL);
284     }
285   }
286   else if (!value)
287   {
288     printf("%s (%s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option));
289
290     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
291     {
292       count = ippGetCount(attr);
293
294       switch (ippGetValueTag(attr))
295       {
296         case IPP_TAG_INTEGER :
297             for (i = 0; i < count; i ++)
298               printf("  %d\n", ippGetInteger(attr, i));
299             break;
300
301         case IPP_TAG_ENUM :
302             for (i = 0; i < count; i ++)
303               printf("  %s\n", ippEnumString(option, ippGetInteger(attr, i)));
304             break;
305
306         case IPP_TAG_RANGE :
307             for (i = 0; i < count; i ++)
308             {
309               int upper, lower = ippGetRange(attr, i, &upper);
310
311               printf("  %d-%d\n", lower, upper);
312             }
313             break;
314
315         case IPP_TAG_RESOLUTION :
316             for (i = 0; i < count; i ++)
317             {
318               int xres, yres;
319               ipp_res_t units;
320               xres = ippGetResolution(attr, i, &yres, &units);
321
322               if (xres == yres)
323                 printf("  %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
324               else
325                 printf("  %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
326             }
327             break;
328
329         case IPP_TAG_TEXTLANG :
330         case IPP_TAG_NAMELANG :
331         case IPP_TAG_TEXT :
332         case IPP_TAG_NAME :
333         case IPP_TAG_KEYWORD :
334         case IPP_TAG_URI :
335         case IPP_TAG_URISCHEME :
336         case IPP_TAG_CHARSET :
337         case IPP_TAG_LANGUAGE :
338         case IPP_TAG_MIMETYPE :
339             for (i = 0; i < count; i ++)
340               printf("  %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
341             break;
342
343         case IPP_TAG_STRING :
344             for (i = 0; i < count; i ++)
345             {
346               int j, len;
347               unsigned char *data = ippGetOctetString(attr, i, &len);
348
349               fputs("  ", stdout);
350               for (j = 0; j < len; j ++)
351               {
352                 if (data[j] < ' ' || data[j] >= 0x7f)
353                   printf("<%02X>", data[j]);
354                 else
355                   putchar(data[j]);
356               }
357               putchar('\n');
358             }
359             break;
360
361         case IPP_TAG_BOOLEAN :
362             break;
363
364         default :
365             printf("  %s\n", ippTagString(ippGetValueTag(attr)));
366             break;
367       }
368     }
369
370   }
371   else
372     puts(cupsLocalizeDestValue(http, dest, dinfo, option, value));
373 }
374
375
376 /*
377  * 'print_file()' - Print a file.
378  */
379
380 static void
381 print_file(http_t        *http,         /* I - Connection to destination */
382            cups_dest_t   *dest,         /* I - Destination */
383            cups_dinfo_t  *dinfo,        /* I - Destination information */
384            const char    *filename,     /* I - File to print */
385            int           num_options,   /* I - Number of options */
386            cups_option_t *options)      /* I - Options */
387 {
388   cups_file_t   *fp;                    /* File to print */
389   int           job_id;                 /* Job ID */
390   ipp_status_t  status;                 /* Submission status */
391   const char    *title;                 /* Title of job */
392   char          buffer[32768];          /* File buffer */
393   ssize_t       bytes;                  /* Bytes read/to write */
394
395
396   if ((fp = cupsFileOpen(filename, "r")) == NULL)
397   {
398     printf("Unable to open \"%s\": %s\n", filename, strerror(errno));
399     return;
400   }
401
402   if ((title = strrchr(filename, '/')) != NULL)
403     title ++;
404   else
405     title = filename;
406
407   if ((status = cupsCreateDestJob(http, dest, dinfo, &job_id, title, num_options, options)) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
408   {
409     printf("Unable to create job: %s\n", cupsLastErrorString());
410     cupsFileClose(fp);
411     return;
412   }
413
414   printf("Created job ID: %d\n", job_id);
415
416   if (cupsStartDestDocument(http, dest, dinfo, job_id, title, CUPS_FORMAT_AUTO, 0, NULL, 1) != HTTP_STATUS_CONTINUE)
417   {
418     printf("Unable to send document: %s\n", cupsLastErrorString());
419     cupsFileClose(fp);
420     return;
421   }
422
423   while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
424   {
425     if (cupsWriteRequestData(http, buffer, (size_t)bytes) != HTTP_STATUS_CONTINUE)
426     {
427       printf("Unable to write document data: %s\n", cupsLastErrorString());
428       break;
429     }
430   }
431
432   cupsFileClose(fp);
433
434   if ((status = cupsFinishDestDocument(http, dest, dinfo)) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
435   {
436     printf("Unable to send document: %s\n", cupsLastErrorString());
437     return;
438   }
439
440   puts("Job queued.");
441 }
442
443
444 /*
445  * 'show_conflicts()' - Show conflicts for selected options.
446  */
447
448 static void
449 show_conflicts(
450     http_t        *http,                /* I - Connection to destination */
451     cups_dest_t   *dest,                /* I - Destination */
452     cups_dinfo_t  *dinfo,               /* I - Destination information */
453     int           num_options,          /* I - Number of options */
454     cups_option_t *options)             /* I - Options */
455 {
456   (void)http;
457   (void)dest;
458   (void)dinfo;
459   (void)num_options;
460   (void)options;
461 }
462
463
464 /*
465  * 'show_default()' - Show default value for option.
466  */
467
468 static void
469 show_default(http_t       *http,        /* I - Connection to destination */
470              cups_dest_t  *dest,        /* I - Destination */
471              cups_dinfo_t *dinfo,       /* I - Destination information */
472              const char  *option)       /* I - Option */
473 {
474   if (!strcmp(option, "media"))
475   {
476    /*
477     * Show default media option...
478     */
479
480     cups_size_t size;                   /* Media size information */
481
482     if (cupsGetDestMediaDefault(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size))
483       printf("%s (%.2fx%.2fmm, margins=[%.2f %.2f %.2f %.2f])\n", size.media, size.width * 0.01, size.length * 0.01, size.left * 0.01, size.bottom * 0.01, size.right * 0.01, size.top * 0.01);
484      else
485        puts("FAILED");
486   }
487   else
488   {
489    /*
490     * Show default other option...
491     */
492
493     ipp_attribute_t *defattr;           /* Default attribute */
494
495     if ((defattr = cupsFindDestDefault(http, dest, dinfo, option)) != NULL)
496     {
497       char value[1024];                 /* Value of default attribute */
498
499       ippAttributeString(defattr, value, sizeof(value));
500       puts(value);
501     }
502     else
503       puts("FAILED");
504   }
505 }
506
507
508 /*
509  * 'show_media()' - Show available media.
510  */
511
512 static void
513 show_media(http_t       *http,          /* I - Connection to destination */
514            cups_dest_t  *dest,          /* I - Destination */
515            cups_dinfo_t *dinfo,         /* I - Destination information */
516            unsigned     flags,          /* I - Media flags */
517            const char   *name)          /* I - Size name */
518 {
519   int           i,                      /* Looping var */
520                 count;                  /* Number of sizes */
521   cups_size_t   size;                   /* Media size info */
522
523
524   if (name)
525   {
526     double      dw, dl;                 /* Width and length from name */
527     char        units[32];              /* Units */
528     int         width,                  /* Width in 100ths of millimeters */
529                 length;                 /* Length in 100ths of millimeters */
530
531
532     if (sscanf(name, "%lfx%lf%31s", &dw, &dl, units) == 3)
533     {
534       if (!strcmp(units, "in"))
535       {
536         width  = (int)(dw * 2540.0);
537         length = (int)(dl * 2540.0);
538       }
539       else if (!strcmp(units, "mm"))
540       {
541         width  = (int)(dw * 100.0);
542         length = (int)(dl * 100.0);
543       }
544       else
545       {
546         puts("  bad units in size");
547         return;
548       }
549
550       if (cupsGetDestMediaBySize(http, dest, dinfo, width, length, flags, &size))
551       {
552         printf("  %s (%s) %dx%d B%d L%d R%d T%d\n", size.media, cupsLocalizeDestMedia(http, dest, dinfo, flags, &size), size.width, size.length, size.bottom, size.left, size.right, size.top);
553       }
554       else
555       {
556         puts("  not supported");
557       }
558     }
559     else if (cupsGetDestMediaByName(http, dest, dinfo, name, flags, &size))
560     {
561       printf("  %s (%s) %dx%d B%d L%d R%d T%d\n", size.media, cupsLocalizeDestMedia(http, dest, dinfo, flags, &size), size.width, size.length, size.bottom, size.left, size.right, size.top);
562     }
563     else
564     {
565       puts("  not supported");
566     }
567   }
568   else
569   {
570     count = cupsGetDestMediaCount(http, dest, dinfo, flags);
571     printf("%d size%s:\n", count, count == 1 ? "" : "s");
572
573     for (i = 0; i < count; i ++)
574     {
575       if (cupsGetDestMediaByIndex(http, dest, dinfo, i, flags, &size))
576         printf("  %s (%s) %dx%d B%d L%d R%d T%d\n", size.media, cupsLocalizeDestMedia(http, dest, dinfo, flags, &size), size.width, size.length, size.bottom, size.left, size.right, size.top);
577       else
578         puts("  error");
579     }
580   }
581 }
582
583
584 /*
585  * 'show_supported()' - Show supported options, values, etc.
586  */
587
588 static void
589 show_supported(http_t       *http,      /* I - Connection to destination */
590                cups_dest_t  *dest,      /* I - Destination */
591                cups_dinfo_t *dinfo,     /* I - Destination information */
592                const char   *option,    /* I - Option, if any */
593                const char   *value)     /* I - Value, if any */
594 {
595   ipp_attribute_t       *attr;          /* Attribute */
596   int                   i,              /* Looping var */
597                         count;          /* Number of values */
598
599
600   if (!option)
601   {
602     attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes");
603     if (attr)
604     {
605       count = ippGetCount(attr);
606       for (i = 0; i < count; i ++)
607         show_supported(http, dest, dinfo, ippGetString(attr, i, NULL), NULL);
608     }
609     else
610     {
611       static const char * const options[] =
612       {                                 /* List of standard options */
613         CUPS_COPIES,
614         CUPS_FINISHINGS,
615         CUPS_MEDIA,
616         CUPS_NUMBER_UP,
617         CUPS_ORIENTATION,
618         CUPS_PRINT_COLOR_MODE,
619         CUPS_PRINT_QUALITY,
620         CUPS_SIDES
621       };
622
623       puts("No job-creation-attributes-supported attribute, probing instead.");
624
625       for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++)
626         if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL))
627           show_supported(http, dest, dinfo, options[i], NULL);
628     }
629   }
630   else if (!value)
631   {
632     printf("%s (%s - %s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option), cupsCheckDestSupported(http, dest, dinfo, option, NULL) ? "supported" : "not-supported");
633
634     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
635     {
636       count = ippGetCount(attr);
637
638       switch (ippGetValueTag(attr))
639       {
640         case IPP_TAG_INTEGER :
641             for (i = 0; i < count; i ++)
642               printf("  %d\n", ippGetInteger(attr, i));
643             break;
644
645         case IPP_TAG_ENUM :
646             for (i = 0; i < count; i ++)
647             {
648               int val = ippGetInteger(attr, i);
649               char valstr[256];
650
651               snprintf(valstr, sizeof(valstr), "%d", val);
652               printf("  %s (%s)\n", ippEnumString(option, ippGetInteger(attr, i)), cupsLocalizeDestValue(http, dest, dinfo, option, valstr));
653             }
654             break;
655
656         case IPP_TAG_RANGE :
657             for (i = 0; i < count; i ++)
658             {
659               int upper, lower = ippGetRange(attr, i, &upper);
660
661               printf("  %d-%d\n", lower, upper);
662             }
663             break;
664
665         case IPP_TAG_RESOLUTION :
666             for (i = 0; i < count; i ++)
667             {
668               int xres, yres;
669               ipp_res_t units;
670               xres = ippGetResolution(attr, i, &yres, &units);
671
672               if (xres == yres)
673                 printf("  %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
674               else
675                 printf("  %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
676             }
677             break;
678
679         case IPP_TAG_KEYWORD :
680             for (i = 0; i < count; i ++)
681               printf("  %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
682             break;
683
684         case IPP_TAG_TEXTLANG :
685         case IPP_TAG_NAMELANG :
686         case IPP_TAG_TEXT :
687         case IPP_TAG_NAME :
688         case IPP_TAG_URI :
689         case IPP_TAG_URISCHEME :
690         case IPP_TAG_CHARSET :
691         case IPP_TAG_LANGUAGE :
692         case IPP_TAG_MIMETYPE :
693             for (i = 0; i < count; i ++)
694               printf("  %s\n", ippGetString(attr, i, NULL));
695             break;
696
697         case IPP_TAG_STRING :
698             for (i = 0; i < count; i ++)
699             {
700               int j, len;
701               unsigned char *data = ippGetOctetString(attr, i, &len);
702
703               fputs("  ", stdout);
704               for (j = 0; j < len; j ++)
705               {
706                 if (data[j] < ' ' || data[j] >= 0x7f)
707                   printf("<%02X>", data[j]);
708                 else
709                   putchar(data[j]);
710               }
711               putchar('\n');
712             }
713             break;
714
715         case IPP_TAG_BOOLEAN :
716             break;
717
718         default :
719             printf("  %s\n", ippTagString(ippGetValueTag(attr)));
720             break;
721       }
722     }
723
724   }
725   else if (cupsCheckDestSupported(http, dest, dinfo, option, value))
726     puts("YES");
727   else
728     puts("NO");
729 }
730
731
732 /*
733  * 'usage()' - Show program usage.
734  */
735
736 static void
737 usage(const char *arg)                  /* I - Argument for usage message */
738 {
739   if (arg)
740     printf("testdest: Unknown option \"%s\".\n", arg);
741
742   puts("Usage:");
743   puts("  ./testdest name [operation ...]");
744   puts("  ./testdest ipp://... [operation ...]");
745   puts("  ./testdest ipps://... [operation ...]");
746   puts("  ./testdest --enum [grayscale] [color] [duplex] [staple] [small]\n"
747        "                    [medium] [large]");
748   puts("");
749   puts("Operations:");
750   puts("  conflicts options");
751   puts("  default option");
752   puts("  localize option [value]");
753   puts("  media [borderless] [duplex] [exact] [ready] [name or size]");
754   puts("  print filename [options]");
755   puts("  supported [option [value]]");
756
757   exit(arg != NULL);
758 }