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