Home | History | Annotate | Download | only in cups
      1 /*
      2  * CUPS destination API test program for CUPS.
      3  *
      4  * Copyright 2012-2016 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) __attribute__((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
    109     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[1], NULL);
    110 
    111   if (!dest)
    112   {
    113     printf("testdest: Unable to get destination \"%s\": %s\n", argv[1], cupsLastErrorString());
    114     return (1);
    115   }
    116 
    117   if ((http = cupsConnectDest(dest, CUPS_DEST_FLAGS_NONE, 30000, NULL, NULL, 0, NULL, NULL)) == NULL)
    118   {
    119     printf("testdest: Unable to connect to destination \"%s\": %s\n", argv[1], cupsLastErrorString());
    120     return (1);
    121   }
    122 
    123   if ((dinfo = cupsCopyDestInfo(http, dest)) == NULL)
    124   {
    125     printf("testdest: Unable to get information for destination \"%s\": %s\n", argv[1], cupsLastErrorString());
    126     return (1);
    127   }
    128 
    129   if (argc == 2 || (!strcmp(argv[2], "supported") && argc < 6))
    130   {
    131     if (argc > 3)
    132       show_supported(http, dest, dinfo, argv[3], argv[4]);
    133     else if (argc > 2)
    134       show_supported(http, dest, dinfo, argv[3], NULL);
    135     else
    136       show_supported(http, dest, dinfo, NULL, NULL);
    137   }
    138   else if (!strcmp(argv[2], "conflicts") && argc > 3)
    139   {
    140     int			i,		/* Looping var */
    141 			num_options = 0;/* Number of options */
    142     cups_option_t	*options = NULL;/* Options */
    143 
    144     for (i = 3; i < argc; i ++)
    145       num_options = cupsParseOptions(argv[i], num_options, &options);
    146 
    147     show_conflicts(http, dest, dinfo, num_options, options);
    148   }
    149   else if (!strcmp(argv[2], "default") && argc == 4)
    150   {
    151     show_default(http, dest, dinfo, argv[3]);
    152   }
    153   else if (!strcmp(argv[2], "localize") && argc < 6)
    154   {
    155     if (argc > 3)
    156       localize(http, dest, dinfo, argv[3], argv[4]);
    157     else if (argc > 2)
    158       localize(http, dest, dinfo, argv[3], NULL);
    159     else
    160       localize(http, dest, dinfo, NULL, NULL);
    161   }
    162   else if (!strcmp(argv[2], "media"))
    163   {
    164     int		i;			/* Looping var */
    165     const char	*name = NULL;		/* Media name, if any */
    166     unsigned	flags = CUPS_MEDIA_FLAGS_DEFAULT;
    167 					/* Media selection flags */
    168 
    169     for (i = 3; i < argc; i ++)
    170     {
    171       if (!strcmp(argv[i], "borderless"))
    172 	flags = CUPS_MEDIA_FLAGS_BORDERLESS;
    173       else if (!strcmp(argv[i], "duplex"))
    174 	flags = CUPS_MEDIA_FLAGS_DUPLEX;
    175       else if (!strcmp(argv[i], "exact"))
    176 	flags = CUPS_MEDIA_FLAGS_EXACT;
    177       else if (!strcmp(argv[i], "ready"))
    178 	flags = CUPS_MEDIA_FLAGS_READY;
    179       else if (name)
    180         usage(argv[i]);
    181       else
    182         name = argv[i];
    183     }
    184 
    185     show_media(http, dest, dinfo, flags, name);
    186   }
    187   else if (!strcmp(argv[2], "print") && argc > 3)
    188   {
    189     int			i,		/* Looping var */
    190 			num_options = 0;/* Number of options */
    191     cups_option_t	*options = NULL;/* Options */
    192 
    193     for (i = 4; i < argc; i ++)
    194       num_options = cupsParseOptions(argv[i], num_options, &options);
    195 
    196     print_file(http, dest, dinfo, argv[3], num_options, options);
    197   }
    198   else
    199     usage(argv[2]);
    200 
    201   return (0);
    202 }
    203 
    204 
    205 /*
    206  * 'enum_cb()' - Print the results from the enumeration of destinations.
    207  */
    208 
    209 static int				/* O - 1 to continue */
    210 enum_cb(void        *user_data,		/* I - User data (unused) */
    211         unsigned    flags,		/* I - Flags */
    212 	cups_dest_t *dest)		/* I - Destination */
    213 {
    214   int	i;				/* Looping var */
    215 
    216 
    217   (void)user_data;
    218   (void)flags;
    219 
    220   if (dest->instance)
    221     printf("%s/%s:\n", dest->name, dest->instance);
    222   else
    223     printf("%s:\n", dest->name);
    224 
    225   for (i = 0; i < dest->num_options; i ++)
    226     printf("    %s=\"%s\"\n", dest->options[i].name, dest->options[i].value);
    227 
    228   return (1);
    229 }
    230 
    231 
    232 /*
    233  * 'localize()' - Localize an option and value.
    234  */
    235 
    236 static void
    237 localize(http_t       *http,		/* I - Connection to destination */
    238          cups_dest_t  *dest,		/* I - Destination */
    239 	 cups_dinfo_t *dinfo,		/* I - Destination information */
    240          const char   *option,		/* I - Option */
    241 	 const char   *value)		/* I - Value, if any */
    242 {
    243   ipp_attribute_t	*attr;		/* Attribute */
    244   int			i,		/* Looping var */
    245 			count;		/* Number of values */
    246 
    247 
    248   if (!option)
    249   {
    250     attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes");
    251     if (attr)
    252     {
    253       count = ippGetCount(attr);
    254       for (i = 0; i < count; i ++)
    255         localize(http, dest, dinfo, ippGetString(attr, i, NULL), NULL);
    256     }
    257     else
    258     {
    259       static const char * const options[] =
    260       {					/* List of standard options */
    261         CUPS_COPIES,
    262 	CUPS_FINISHINGS,
    263 	CUPS_MEDIA,
    264 	CUPS_NUMBER_UP,
    265 	CUPS_ORIENTATION,
    266 	CUPS_PRINT_COLOR_MODE,
    267 	CUPS_PRINT_QUALITY,
    268 	CUPS_SIDES
    269       };
    270 
    271       puts("No job-creation-attributes-supported attribute, probing instead.");
    272 
    273       for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++)
    274         if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL))
    275 	  localize(http, dest, dinfo, options[i], NULL);
    276     }
    277   }
    278   else if (!value)
    279   {
    280     printf("%s (%s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option));
    281 
    282     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
    283     {
    284       count = ippGetCount(attr);
    285 
    286       switch (ippGetValueTag(attr))
    287       {
    288         case IPP_TAG_INTEGER :
    289 	    for (i = 0; i < count; i ++)
    290               printf("  %d\n", ippGetInteger(attr, i));
    291 	    break;
    292 
    293         case IPP_TAG_ENUM :
    294 	    for (i = 0; i < count; i ++)
    295               printf("  %s\n", ippEnumString(option, ippGetInteger(attr, i)));
    296 	    break;
    297 
    298         case IPP_TAG_RANGE :
    299 	    for (i = 0; i < count; i ++)
    300 	    {
    301 	      int upper, lower = ippGetRange(attr, i, &upper);
    302 
    303               printf("  %d-%d\n", lower, upper);
    304 	    }
    305 	    break;
    306 
    307         case IPP_TAG_RESOLUTION :
    308 	    for (i = 0; i < count; i ++)
    309 	    {
    310 	      int xres, yres;
    311 	      ipp_res_t units;
    312 	      xres = ippGetResolution(attr, i, &yres, &units);
    313 
    314               if (xres == yres)
    315                 printf("  %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
    316 	      else
    317                 printf("  %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
    318 	    }
    319 	    break;
    320 
    321 	case IPP_TAG_TEXTLANG :
    322 	case IPP_TAG_NAMELANG :
    323 	case IPP_TAG_TEXT :
    324 	case IPP_TAG_NAME :
    325 	case IPP_TAG_KEYWORD :
    326 	case IPP_TAG_URI :
    327 	case IPP_TAG_URISCHEME :
    328 	case IPP_TAG_CHARSET :
    329 	case IPP_TAG_LANGUAGE :
    330 	case IPP_TAG_MIMETYPE :
    331 	    for (i = 0; i < count; i ++)
    332               printf("  %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
    333 	    break;
    334 
    335         case IPP_TAG_STRING :
    336 	    for (i = 0; i < count; i ++)
    337 	    {
    338 	      int j, len;
    339 	      unsigned char *data = ippGetOctetString(attr, i, &len);
    340 
    341               fputs("  ", stdout);
    342 	      for (j = 0; j < len; j ++)
    343 	      {
    344 	        if (data[j] < ' ' || data[j] >= 0x7f)
    345 		  printf("<%02X>", data[j]);
    346 		else
    347 		  putchar(data[j]);
    348               }
    349               putchar('\n');
    350 	    }
    351 	    break;
    352 
    353         case IPP_TAG_BOOLEAN :
    354 	    break;
    355 
    356         default :
    357 	    printf("  %s\n", ippTagString(ippGetValueTag(attr)));
    358 	    break;
    359       }
    360     }
    361 
    362   }
    363   else
    364     puts(cupsLocalizeDestValue(http, dest, dinfo, option, value));
    365 }
    366 
    367 
    368 /*
    369  * 'print_file()' - Print a file.
    370  */
    371 
    372 static void
    373 print_file(http_t        *http,		/* I - Connection to destination */
    374            cups_dest_t   *dest,		/* I - Destination */
    375 	   cups_dinfo_t  *dinfo,	/* I - Destination information */
    376            const char    *filename,	/* I - File to print */
    377 	   int           num_options,	/* I - Number of options */
    378 	   cups_option_t *options)	/* I - Options */
    379 {
    380   cups_file_t	*fp;			/* File to print */
    381   int		job_id;			/* Job ID */
    382   ipp_status_t	status;			/* Submission status */
    383   const char	*title;			/* Title of job */
    384   char		buffer[32768];		/* File buffer */
    385   ssize_t	bytes;			/* Bytes read/to write */
    386 
    387 
    388   if ((fp = cupsFileOpen(filename, "r")) == NULL)
    389   {
    390     printf("Unable to open \"%s\": %s\n", filename, strerror(errno));
    391     return;
    392   }
    393 
    394   if ((title = strrchr(filename, '/')) != NULL)
    395     title ++;
    396   else
    397     title = filename;
    398 
    399   if ((status = cupsCreateDestJob(http, dest, dinfo, &job_id, title, num_options, options)) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
    400   {
    401     printf("Unable to create job: %s\n", cupsLastErrorString());
    402     cupsFileClose(fp);
    403     return;
    404   }
    405 
    406   printf("Created job ID: %d\n", job_id);
    407 
    408   if (cupsStartDestDocument(http, dest, dinfo, job_id, title, CUPS_FORMAT_AUTO, 0, NULL, 1) != HTTP_STATUS_CONTINUE)
    409   {
    410     printf("Unable to send document: %s\n", cupsLastErrorString());
    411     cupsFileClose(fp);
    412     return;
    413   }
    414 
    415   while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
    416   {
    417     if (cupsWriteRequestData(http, buffer, (size_t)bytes) != HTTP_STATUS_CONTINUE)
    418     {
    419       printf("Unable to write document data: %s\n", cupsLastErrorString());
    420       break;
    421     }
    422   }
    423 
    424   cupsFileClose(fp);
    425 
    426   if ((status = cupsFinishDestDocument(http, dest, dinfo)) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
    427   {
    428     printf("Unable to send document: %s\n", cupsLastErrorString());
    429     return;
    430   }
    431 
    432   puts("Job queued.");
    433 }
    434 
    435 
    436 /*
    437  * 'show_conflicts()' - Show conflicts for selected options.
    438  */
    439 
    440 static void
    441 show_conflicts(
    442     http_t        *http,		/* I - Connection to destination */
    443     cups_dest_t   *dest,		/* I - Destination */
    444     cups_dinfo_t  *dinfo,		/* I - Destination information */
    445     int           num_options,		/* I - Number of options */
    446     cups_option_t *options)		/* I - Options */
    447 {
    448   (void)http;
    449   (void)dest;
    450   (void)dinfo;
    451   (void)num_options;
    452   (void)options;
    453 }
    454 
    455 
    456 /*
    457  * 'show_default()' - Show default value for option.
    458  */
    459 
    460 static void
    461 show_default(http_t       *http,	/* I - Connection to destination */
    462 	     cups_dest_t  *dest,	/* I - Destination */
    463 	     cups_dinfo_t *dinfo,	/* I - Destination information */
    464 	     const char  *option)	/* I - Option */
    465 {
    466   (void)http;
    467   (void)dest;
    468   (void)dinfo;
    469   (void)option;
    470 }
    471 
    472 
    473 /*
    474  * 'show_media()' - Show available media.
    475  */
    476 
    477 static void
    478 show_media(http_t       *http,		/* I - Connection to destination */
    479 	   cups_dest_t  *dest,		/* I - Destination */
    480 	   cups_dinfo_t *dinfo,		/* I - Destination information */
    481 	   unsigned     flags,		/* I - Media flags */
    482 	   const char   *name)		/* I - Size name */
    483 {
    484   int		i,			/* Looping var */
    485 		count;			/* Number of sizes */
    486   cups_size_t	size;			/* Media size info */
    487 
    488 
    489   if (name)
    490   {
    491     double	dw, dl;			/* Width and length from name */
    492     char	units[32];		/* Units */
    493     int		width,			/* Width in 100ths of millimeters */
    494 		length;			/* Length in 100ths of millimeters */
    495 
    496 
    497     if (sscanf(name, "%lfx%lf%31s", &dw, &dl, units) == 3)
    498     {
    499       if (!strcmp(units, "in"))
    500       {
    501         width  = (int)(dw * 2540.0);
    502 	length = (int)(dl * 2540.0);
    503       }
    504       else if (!strcmp(units, "mm"))
    505       {
    506         width  = (int)(dw * 100.0);
    507         length = (int)(dl * 100.0);
    508       }
    509       else
    510       {
    511         puts("  bad units in size");
    512 	return;
    513       }
    514 
    515       if (cupsGetDestMediaBySize(http, dest, dinfo, width, length, flags, &size))
    516       {
    517 	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);
    518       }
    519       else
    520       {
    521 	puts("  not supported");
    522       }
    523     }
    524     else if (cupsGetDestMediaByName(http, dest, dinfo, name, flags, &size))
    525     {
    526       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);
    527     }
    528     else
    529     {
    530       puts("  not supported");
    531     }
    532   }
    533   else
    534   {
    535     count = cupsGetDestMediaCount(http, dest, dinfo, flags);
    536     printf("%d size%s:\n", count, count == 1 ? "" : "s");
    537 
    538     for (i = 0; i < count; i ++)
    539     {
    540       if (cupsGetDestMediaByIndex(http, dest, dinfo, i, flags, &size))
    541         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);
    542       else
    543         puts("  error");
    544     }
    545   }
    546 }
    547 
    548 
    549 /*
    550  * 'show_supported()' - Show supported options, values, etc.
    551  */
    552 
    553 static void
    554 show_supported(http_t       *http,	/* I - Connection to destination */
    555 	       cups_dest_t  *dest,	/* I - Destination */
    556 	       cups_dinfo_t *dinfo,	/* I - Destination information */
    557 	       const char   *option,	/* I - Option, if any */
    558 	       const char   *value)	/* I - Value, if any */
    559 {
    560   ipp_attribute_t	*attr;		/* Attribute */
    561   int			i,		/* Looping var */
    562 			count;		/* Number of values */
    563 
    564 
    565   if (!option)
    566   {
    567     attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes");
    568     if (attr)
    569     {
    570       count = ippGetCount(attr);
    571       for (i = 0; i < count; i ++)
    572         show_supported(http, dest, dinfo, ippGetString(attr, i, NULL), NULL);
    573     }
    574     else
    575     {
    576       static const char * const options[] =
    577       {					/* List of standard options */
    578         CUPS_COPIES,
    579 	CUPS_FINISHINGS,
    580 	CUPS_MEDIA,
    581 	CUPS_NUMBER_UP,
    582 	CUPS_ORIENTATION,
    583 	CUPS_PRINT_COLOR_MODE,
    584 	CUPS_PRINT_QUALITY,
    585 	CUPS_SIDES
    586       };
    587 
    588       puts("No job-creation-attributes-supported attribute, probing instead.");
    589 
    590       for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++)
    591         if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL))
    592 	  show_supported(http, dest, dinfo, options[i], NULL);
    593     }
    594   }
    595   else if (!value)
    596   {
    597     puts(option);
    598     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
    599     {
    600       count = ippGetCount(attr);
    601 
    602       switch (ippGetValueTag(attr))
    603       {
    604         case IPP_TAG_INTEGER :
    605 	    for (i = 0; i < count; i ++)
    606               printf("  %d\n", ippGetInteger(attr, i));
    607 	    break;
    608 
    609         case IPP_TAG_ENUM :
    610 	    for (i = 0; i < count; i ++)
    611               printf("  %s\n", ippEnumString(option, ippGetInteger(attr, i)));
    612 	    break;
    613 
    614         case IPP_TAG_RANGE :
    615 	    for (i = 0; i < count; i ++)
    616 	    {
    617 	      int upper, lower = ippGetRange(attr, i, &upper);
    618 
    619               printf("  %d-%d\n", lower, upper);
    620 	    }
    621 	    break;
    622 
    623         case IPP_TAG_RESOLUTION :
    624 	    for (i = 0; i < count; i ++)
    625 	    {
    626 	      int xres, yres;
    627 	      ipp_res_t units;
    628 	      xres = ippGetResolution(attr, i, &yres, &units);
    629 
    630               if (xres == yres)
    631                 printf("  %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
    632 	      else
    633                 printf("  %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
    634 	    }
    635 	    break;
    636 
    637 	case IPP_TAG_TEXTLANG :
    638 	case IPP_TAG_NAMELANG :
    639 	case IPP_TAG_TEXT :
    640 	case IPP_TAG_NAME :
    641 	case IPP_TAG_KEYWORD :
    642 	case IPP_TAG_URI :
    643 	case IPP_TAG_URISCHEME :
    644 	case IPP_TAG_CHARSET :
    645 	case IPP_TAG_LANGUAGE :
    646 	case IPP_TAG_MIMETYPE :
    647 	    for (i = 0; i < count; i ++)
    648               printf("  %s\n", ippGetString(attr, i, NULL));
    649 	    break;
    650 
    651         case IPP_TAG_STRING :
    652 	    for (i = 0; i < count; i ++)
    653 	    {
    654 	      int j, len;
    655 	      unsigned char *data = ippGetOctetString(attr, i, &len);
    656 
    657               fputs("  ", stdout);
    658 	      for (j = 0; j < len; j ++)
    659 	      {
    660 	        if (data[j] < ' ' || data[j] >= 0x7f)
    661 		  printf("<%02X>", data[j]);
    662 		else
    663 		  putchar(data[j]);
    664               }
    665               putchar('\n');
    666 	    }
    667 	    break;
    668 
    669         case IPP_TAG_BOOLEAN :
    670 	    break;
    671 
    672         default :
    673 	    printf("  %s\n", ippTagString(ippGetValueTag(attr)));
    674 	    break;
    675       }
    676     }
    677 
    678   }
    679   else if (cupsCheckDestSupported(http, dest, dinfo, option, value))
    680     puts("YES");
    681   else
    682     puts("NO");
    683 }
    684 
    685 
    686 /*
    687  * 'usage()' - Show program usage.
    688  */
    689 
    690 static void
    691 usage(const char *arg)			/* I - Argument for usage message */
    692 {
    693   if (arg)
    694     printf("testdest: Unknown option \"%s\".\n", arg);
    695 
    696   puts("Usage:");
    697   puts("  ./testdest name [operation ...]");
    698   puts("  ./testdest ipp://... [operation ...]");
    699   puts("  ./testdest ipps://... [operation ...]");
    700   puts("  ./testdest --enum [grayscale] [color] [duplex] [staple] [small]\n"
    701        "                    [medium] [large]");
    702   puts("");
    703   puts("Operations:");
    704   puts("  conflicts options");
    705   puts("  default option");
    706   puts("  localize option [value]");
    707   puts("  media [borderless] [duplex] [exact] [ready] [name or size]");
    708   puts("  print filename [options]");
    709   puts("  supported [option [value]]");
    710 
    711   exit(arg != NULL);
    712 }
    713