• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * CUPS destination API test program for CUPS.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright © 2012-2018 by Apple Inc.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
8  */
9 
10 /*
11  * Include necessary headers...
12  */
13 
14 #include <stdio.h>
15 #include <errno.h>
16 #include "cups.h"
17 
18 
19 /*
20  * Local functions...
21  */
22 
23 static int	enum_cb(void *user_data, unsigned flags, cups_dest_t *dest);
24 static void	localize(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value);
25 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);
26 static void	show_conflicts(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, int num_options, cups_option_t *options);
27 static void	show_default(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option);
28 static void	show_media(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, unsigned flags, const char *name);
29 static void	show_supported(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value);
30 static void	usage(const char *arg) _CUPS_NORETURN;
31 
32 
33 /*
34  * 'main()' - Main entry.
35  */
36 
37 int					/* O - Exit status */
main(int argc,char * argv[])38 main(int  argc,				/* I - Number of command-line arguments */
39      char *argv[])			/* I - Command-line arguments */
40 {
41   int		i;			/* Looping var */
42   http_t	*http;			/* Connection to destination */
43   cups_dest_t	*dest = NULL;		/* Destination */
44   cups_dinfo_t	*dinfo;			/* Destination info */
45   unsigned	dflags = CUPS_DEST_FLAGS_NONE;
46 					/* Destination flags */
47 
48 
49   if (argc < 2)
50     return (0);
51 
52   if (!strcmp(argv[1], "--get"))
53   {
54     cups_dest_t	*dests;			/* Destinations */
55     int		num_dests = cupsGetDests2(CUPS_HTTP_DEFAULT, &dests);
56 					/* Number of destinations */
57 
58     for (i = 0; i < num_dests; i ++)
59       enum_cb(NULL, 0, dests + i);
60 
61     cupsFreeDests(num_dests, dests);
62     return (0);
63   }
64   else if (!strcmp(argv[1], "--enum"))
65   {
66     cups_ptype_t	type = 0,	/* Printer type filter */
67 			mask = 0;	/* Printer type mask */
68 
69 
70     for (i = 2; i < argc; i ++)
71     {
72       if (!strcmp(argv[i], "grayscale"))
73       {
74         type |= CUPS_PRINTER_BW;
75 	mask |= CUPS_PRINTER_BW;
76       }
77       else if (!strcmp(argv[i], "color"))
78       {
79         type |= CUPS_PRINTER_COLOR;
80 	mask |= CUPS_PRINTER_COLOR;
81       }
82       else if (!strcmp(argv[i], "duplex"))
83       {
84         type |= CUPS_PRINTER_DUPLEX;
85 	mask |= CUPS_PRINTER_DUPLEX;
86       }
87       else if (!strcmp(argv[i], "staple"))
88       {
89         type |= CUPS_PRINTER_STAPLE;
90 	mask |= CUPS_PRINTER_STAPLE;
91       }
92       else if (!strcmp(argv[i], "small"))
93       {
94         type |= CUPS_PRINTER_SMALL;
95 	mask |= CUPS_PRINTER_SMALL;
96       }
97       else if (!strcmp(argv[i], "medium"))
98       {
99         type |= CUPS_PRINTER_MEDIUM;
100 	mask |= CUPS_PRINTER_MEDIUM;
101       }
102       else if (!strcmp(argv[i], "large"))
103       {
104         type |= CUPS_PRINTER_LARGE;
105 	mask |= CUPS_PRINTER_LARGE;
106       }
107       else
108         usage(argv[i]);
109     }
110 
111     cupsEnumDests(CUPS_DEST_FLAGS_NONE, 5000, NULL, type, mask, enum_cb, NULL);
112 
113     return (0);
114   }
115 
116   i = 1;
117   if (!strcmp(argv[i], "--device"))
118   {
119     dflags = CUPS_DEST_FLAGS_DEVICE;
120     i ++;
121   }
122 
123   if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "ipps://", 7))
124     dest = cupsGetDestWithURI(NULL, argv[i]);
125   else if (!strcmp(argv[i], "default"))
126   {
127     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
128     if (dest && dest->instance)
129       printf("default is \"%s/%s\".\n", dest->name, dest->instance);
130     else if (dest)
131       printf("default is \"%s\".\n", dest->name);
132     else
133       puts("no default destination.");
134   }
135   else
136     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[i], NULL);
137 
138   if (!dest)
139   {
140     printf("testdest: Unable to get destination \"%s\": %s\n", argv[i], cupsLastErrorString());
141     return (1);
142   }
143 
144   i ++;
145 
146   if ((http = cupsConnectDest(dest, dflags, 30000, NULL, NULL, 0, NULL, NULL)) == NULL)
147   {
148     printf("testdest: Unable to connect to destination \"%s\": %s\n", dest->name, cupsLastErrorString());
149     return (1);
150   }
151 
152   if ((dinfo = cupsCopyDestInfo(http, dest)) == NULL)
153   {
154     printf("testdest: Unable to get information for destination \"%s\": %s\n", dest->name, cupsLastErrorString());
155     return (1);
156   }
157 
158   if (i == argc || !strcmp(argv[i], "supported"))
159   {
160     i ++;
161 
162     if ((i + 1) < argc)
163       show_supported(http, dest, dinfo, argv[i], argv[i + 1]);
164     else if (argc > 2)
165       show_supported(http, dest, dinfo, argv[i], NULL);
166     else
167       show_supported(http, dest, dinfo, NULL, NULL);
168   }
169   else if (!strcmp(argv[i], "conflicts") && (i + 1) < argc)
170   {
171     int			num_options = 0;/* Number of options */
172     cups_option_t	*options = NULL;/* Options */
173 
174     for (i ++; i < argc; i ++)
175       num_options = cupsParseOptions(argv[i], num_options, &options);
176 
177     show_conflicts(http, dest, dinfo, num_options, options);
178   }
179   else if (!strcmp(argv[i], "default") && (i + 1) < argc)
180   {
181     show_default(http, dest, dinfo, argv[i + 1]);
182   }
183   else if (!strcmp(argv[i], "localize"))
184   {
185     i ++;
186     if ((i + 1) < argc)
187       localize(http, dest, dinfo, argv[i], argv[i + 1]);
188     else if (argc > 2)
189       localize(http, dest, dinfo, argv[i], NULL);
190     else
191       localize(http, dest, dinfo, NULL, NULL);
192   }
193   else if (!strcmp(argv[i], "media"))
194   {
195     const char	*name = NULL;		/* Media name, if any */
196     unsigned	flags = CUPS_MEDIA_FLAGS_DEFAULT;
197 					/* Media selection flags */
198 
199     for (i ++; i < argc; i ++)
200     {
201       if (!strcmp(argv[i], "borderless"))
202 	flags = CUPS_MEDIA_FLAGS_BORDERLESS;
203       else if (!strcmp(argv[i], "duplex"))
204 	flags = CUPS_MEDIA_FLAGS_DUPLEX;
205       else if (!strcmp(argv[i], "exact"))
206 	flags = CUPS_MEDIA_FLAGS_EXACT;
207       else if (!strcmp(argv[i], "ready"))
208 	flags = CUPS_MEDIA_FLAGS_READY;
209       else if (name)
210         usage(argv[i]);
211       else
212         name = argv[i];
213     }
214 
215     show_media(http, dest, dinfo, flags, name);
216   }
217   else if (!strcmp(argv[i], "print") && (i + 1) < argc)
218   {
219     int			num_options = 0;/* Number of options */
220     cups_option_t	*options = NULL;/* Options */
221     const char		*filename = argv[i + 1];
222 
223     for (i += 2; i < argc; i ++)
224       num_options = cupsParseOptions(argv[i], num_options, &options);
225 
226     print_file(http, dest, dinfo, filename, num_options, options);
227   }
228   else
229     usage(argv[i]);
230 
231   return (0);
232 }
233 
234 
235 /*
236  * 'enum_cb()' - Print the results from the enumeration of destinations.
237  */
238 
239 static int				/* O - 1 to continue */
enum_cb(void * user_data,unsigned flags,cups_dest_t * dest)240 enum_cb(void        *user_data,		/* I - User data (unused) */
241         unsigned    flags,		/* I - Flags */
242 	cups_dest_t *dest)		/* I - Destination */
243 {
244   int	i;				/* Looping var */
245 
246 
247   (void)user_data;
248   (void)flags;
249 
250   if (dest->instance)
251     printf("%s%s/%s%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name, dest->instance, dest->is_default ? " (Default)" : "");
252   else
253     printf("%s%s%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name, dest->is_default ? " (Default)" : "");
254 
255   for (i = 0; i < dest->num_options; i ++)
256     printf("    %s=\"%s\"\n", dest->options[i].name, dest->options[i].value);
257 
258   puts("");
259 
260   return (1);
261 }
262 
263 
264 /*
265  * 'localize()' - Localize an option and value.
266  */
267 
268 static void
localize(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option,const char * value)269 localize(http_t       *http,		/* I - Connection to destination */
270          cups_dest_t  *dest,		/* I - Destination */
271 	 cups_dinfo_t *dinfo,		/* I - Destination information */
272          const char   *option,		/* I - Option */
273 	 const char   *value)		/* I - Value, if any */
274 {
275   ipp_attribute_t	*attr;		/* Attribute */
276   int			i,		/* Looping var */
277 			count;		/* Number of values */
278 
279 
280   if (!option)
281   {
282     attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes");
283     if (attr)
284     {
285       count = ippGetCount(attr);
286       for (i = 0; i < count; i ++)
287         localize(http, dest, dinfo, ippGetString(attr, i, NULL), NULL);
288     }
289     else
290     {
291       static const char * const options[] =
292       {					/* List of standard options */
293         CUPS_COPIES,
294 	CUPS_FINISHINGS,
295 	CUPS_MEDIA,
296 	CUPS_NUMBER_UP,
297 	CUPS_ORIENTATION,
298 	CUPS_PRINT_COLOR_MODE,
299 	CUPS_PRINT_QUALITY,
300 	CUPS_SIDES
301       };
302 
303       puts("No job-creation-attributes-supported attribute, probing instead.");
304 
305       for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++)
306         if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL))
307 	  localize(http, dest, dinfo, options[i], NULL);
308     }
309   }
310   else if (!value)
311   {
312     printf("%s (%s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option));
313 
314     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
315     {
316       count = ippGetCount(attr);
317 
318       switch (ippGetValueTag(attr))
319       {
320         case IPP_TAG_INTEGER :
321 	    for (i = 0; i < count; i ++)
322               printf("  %d\n", ippGetInteger(attr, i));
323 	    break;
324 
325         case IPP_TAG_ENUM :
326 	    for (i = 0; i < count; i ++)
327               printf("  %s\n", ippEnumString(option, ippGetInteger(attr, i)));
328 	    break;
329 
330         case IPP_TAG_RANGE :
331 	    for (i = 0; i < count; i ++)
332 	    {
333 	      int upper, lower = ippGetRange(attr, i, &upper);
334 
335               printf("  %d-%d\n", lower, upper);
336 	    }
337 	    break;
338 
339         case IPP_TAG_RESOLUTION :
340 	    for (i = 0; i < count; i ++)
341 	    {
342 	      int xres, yres;
343 	      ipp_res_t units;
344 	      xres = ippGetResolution(attr, i, &yres, &units);
345 
346               if (xres == yres)
347                 printf("  %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
348 	      else
349                 printf("  %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
350 	    }
351 	    break;
352 
353 	case IPP_TAG_TEXTLANG :
354 	case IPP_TAG_NAMELANG :
355 	case IPP_TAG_TEXT :
356 	case IPP_TAG_NAME :
357 	case IPP_TAG_KEYWORD :
358 	case IPP_TAG_URI :
359 	case IPP_TAG_URISCHEME :
360 	case IPP_TAG_CHARSET :
361 	case IPP_TAG_LANGUAGE :
362 	case IPP_TAG_MIMETYPE :
363 	    for (i = 0; i < count; i ++)
364               printf("  %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
365 	    break;
366 
367         case IPP_TAG_STRING :
368 	    for (i = 0; i < count; i ++)
369 	    {
370 	      int j, len;
371 	      unsigned char *data = ippGetOctetString(attr, i, &len);
372 
373               fputs("  ", stdout);
374 	      for (j = 0; j < len; j ++)
375 	      {
376 	        if (data[j] < ' ' || data[j] >= 0x7f)
377 		  printf("<%02X>", data[j]);
378 		else
379 		  putchar(data[j]);
380               }
381               putchar('\n');
382 	    }
383 	    break;
384 
385         case IPP_TAG_BOOLEAN :
386 	    break;
387 
388         default :
389 	    printf("  %s\n", ippTagString(ippGetValueTag(attr)));
390 	    break;
391       }
392     }
393 
394   }
395   else
396     puts(cupsLocalizeDestValue(http, dest, dinfo, option, value));
397 }
398 
399 
400 /*
401  * 'print_file()' - Print a file.
402  */
403 
404 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)405 print_file(http_t        *http,		/* I - Connection to destination */
406            cups_dest_t   *dest,		/* I - Destination */
407 	   cups_dinfo_t  *dinfo,	/* I - Destination information */
408            const char    *filename,	/* I - File to print */
409 	   int           num_options,	/* I - Number of options */
410 	   cups_option_t *options)	/* I - Options */
411 {
412   cups_file_t	*fp;			/* File to print */
413   int		job_id;			/* Job ID */
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 (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 (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
show_conflicts(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,int num_options,cups_option_t * options)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
show_default(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option)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
show_media(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,unsigned flags,const char * name)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
show_supported(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option,const char * value)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
usage(const char * arg)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 }
783