xref: /aosp_15_r20/external/libcups/cups/testdest.c (revision 5e7646d21f1134fb0638875d812ef646c12ab91e)
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 */
main(int argc,char * argv[])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 */
enum_cb(void * user_data,unsigned flags,cups_dest_t * dest)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
localize(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option,const char * value)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
print_file(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * filename,int num_options,cups_option_t * options)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   const char	*title;			/* Title of job */
414   char		buffer[32768];		/* File buffer */
415   ssize_t	bytes;			/* Bytes read/to write */
416 
417 
418   if ((fp = cupsFileOpen(filename, "r")) == NULL)
419   {
420     printf("Unable to open \"%s\": %s\n", filename, strerror(errno));
421     return;
422   }
423 
424   if ((title = strrchr(filename, '/')) != NULL)
425     title ++;
426   else
427     title = filename;
428 
429   if (cupsCreateDestJob(http, dest, dinfo, &job_id, title, num_options, options) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
430   {
431     printf("Unable to create job: %s\n", cupsLastErrorString());
432     cupsFileClose(fp);
433     return;
434   }
435 
436   printf("Created job ID: %d\n", job_id);
437 
438   if (cupsStartDestDocument(http, dest, dinfo, job_id, title, CUPS_FORMAT_AUTO, 0, NULL, 1) != HTTP_STATUS_CONTINUE)
439   {
440     printf("Unable to send document: %s\n", cupsLastErrorString());
441     cupsFileClose(fp);
442     return;
443   }
444 
445   while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
446   {
447     if (cupsWriteRequestData(http, buffer, (size_t)bytes) != HTTP_STATUS_CONTINUE)
448     {
449       printf("Unable to write document data: %s\n", cupsLastErrorString());
450       break;
451     }
452   }
453 
454   cupsFileClose(fp);
455 
456   if (cupsFinishDestDocument(http, dest, dinfo) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
457   {
458     printf("Unable to send document: %s\n", cupsLastErrorString());
459     return;
460   }
461 
462   puts("Job queued.");
463 }
464 
465 
466 /*
467  * 'show_conflicts()' - Show conflicts for selected options.
468  */
469 
470 static void
show_conflicts(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,int num_options,cups_option_t * options)471 show_conflicts(
472     http_t        *http,		/* I - Connection to destination */
473     cups_dest_t   *dest,		/* I - Destination */
474     cups_dinfo_t  *dinfo,		/* I - Destination information */
475     int           num_options,		/* I - Number of options */
476     cups_option_t *options)		/* I - Options */
477 {
478   (void)http;
479   (void)dest;
480   (void)dinfo;
481   (void)num_options;
482   (void)options;
483 }
484 
485 
486 /*
487  * 'show_default()' - Show default value for option.
488  */
489 
490 static void
show_default(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option)491 show_default(http_t       *http,	/* I - Connection to destination */
492 	     cups_dest_t  *dest,	/* I - Destination */
493 	     cups_dinfo_t *dinfo,	/* I - Destination information */
494 	     const char  *option)	/* I - Option */
495 {
496   if (!strcmp(option, "media"))
497   {
498    /*
499     * Show default media option...
500     */
501 
502     cups_size_t size;                   /* Media size information */
503 
504     if (cupsGetDestMediaDefault(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size))
505       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);
506      else
507        puts("FAILED");
508   }
509   else
510   {
511    /*
512     * Show default other option...
513     */
514 
515     ipp_attribute_t *defattr;           /* Default attribute */
516 
517     if ((defattr = cupsFindDestDefault(http, dest, dinfo, option)) != NULL)
518     {
519       char value[1024];                 /* Value of default attribute */
520 
521       ippAttributeString(defattr, value, sizeof(value));
522       puts(value);
523     }
524     else
525       puts("FAILED");
526   }
527 }
528 
529 
530 /*
531  * 'show_media()' - Show available media.
532  */
533 
534 static void
show_media(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,unsigned flags,const char * name)535 show_media(http_t       *http,		/* I - Connection to destination */
536 	   cups_dest_t  *dest,		/* I - Destination */
537 	   cups_dinfo_t *dinfo,		/* I - Destination information */
538 	   unsigned     flags,		/* I - Media flags */
539 	   const char   *name)		/* I - Size name */
540 {
541   int		i,			/* Looping var */
542 		count;			/* Number of sizes */
543   cups_size_t	size;			/* Media size info */
544 
545 
546   if (name)
547   {
548     double	dw, dl;			/* Width and length from name */
549     char	units[32];		/* Units */
550     int		width,			/* Width in 100ths of millimeters */
551 		length;			/* Length in 100ths of millimeters */
552 
553 
554     if (sscanf(name, "%lfx%lf%31s", &dw, &dl, units) == 3)
555     {
556       if (!strcmp(units, "in"))
557       {
558         width  = (int)(dw * 2540.0);
559 	length = (int)(dl * 2540.0);
560       }
561       else if (!strcmp(units, "mm"))
562       {
563         width  = (int)(dw * 100.0);
564         length = (int)(dl * 100.0);
565       }
566       else
567       {
568         puts("  bad units in size");
569 	return;
570       }
571 
572       if (cupsGetDestMediaBySize(http, dest, dinfo, width, length, flags, &size))
573       {
574 	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);
575       }
576       else
577       {
578 	puts("  not supported");
579       }
580     }
581     else if (cupsGetDestMediaByName(http, dest, dinfo, name, flags, &size))
582     {
583       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);
584     }
585     else
586     {
587       puts("  not supported");
588     }
589   }
590   else
591   {
592     count = cupsGetDestMediaCount(http, dest, dinfo, flags);
593     printf("%d size%s:\n", count, count == 1 ? "" : "s");
594 
595     for (i = 0; i < count; i ++)
596     {
597       if (cupsGetDestMediaByIndex(http, dest, dinfo, i, flags, &size))
598         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);
599       else
600         puts("  error");
601     }
602   }
603 }
604 
605 
606 /*
607  * 'show_supported()' - Show supported options, values, etc.
608  */
609 
610 static void
show_supported(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option,const char * value)611 show_supported(http_t       *http,	/* I - Connection to destination */
612 	       cups_dest_t  *dest,	/* I - Destination */
613 	       cups_dinfo_t *dinfo,	/* I - Destination information */
614 	       const char   *option,	/* I - Option, if any */
615 	       const char   *value)	/* I - Value, if any */
616 {
617   ipp_attribute_t	*attr;		/* Attribute */
618   int			i,		/* Looping var */
619 			count;		/* Number of values */
620 
621 
622   if (!option)
623   {
624     attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes");
625     if (attr)
626     {
627       count = ippGetCount(attr);
628       for (i = 0; i < count; i ++)
629         show_supported(http, dest, dinfo, ippGetString(attr, i, NULL), NULL);
630     }
631     else
632     {
633       static const char * const options[] =
634       {					/* List of standard options */
635         CUPS_COPIES,
636 	CUPS_FINISHINGS,
637 	CUPS_MEDIA,
638 	CUPS_NUMBER_UP,
639 	CUPS_ORIENTATION,
640 	CUPS_PRINT_COLOR_MODE,
641 	CUPS_PRINT_QUALITY,
642 	CUPS_SIDES
643       };
644 
645       puts("No job-creation-attributes-supported attribute, probing instead.");
646 
647       for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++)
648         if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL))
649 	  show_supported(http, dest, dinfo, options[i], NULL);
650     }
651   }
652   else if (!value)
653   {
654     printf("%s (%s - %s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option), cupsCheckDestSupported(http, dest, dinfo, option, NULL) ? "supported" : "not-supported");
655 
656     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
657     {
658       count = ippGetCount(attr);
659 
660       switch (ippGetValueTag(attr))
661       {
662         case IPP_TAG_INTEGER :
663 	    for (i = 0; i < count; i ++)
664               printf("  %d\n", ippGetInteger(attr, i));
665 	    break;
666 
667         case IPP_TAG_ENUM :
668 	    for (i = 0; i < count; i ++)
669 	    {
670 	      int val = ippGetInteger(attr, i);
671 	      char valstr[256];
672 
673               snprintf(valstr, sizeof(valstr), "%d", val);
674               printf("  %s (%s)\n", ippEnumString(option, ippGetInteger(attr, i)), cupsLocalizeDestValue(http, dest, dinfo, option, valstr));
675             }
676 	    break;
677 
678         case IPP_TAG_RANGE :
679 	    for (i = 0; i < count; i ++)
680 	    {
681 	      int upper, lower = ippGetRange(attr, i, &upper);
682 
683               printf("  %d-%d\n", lower, upper);
684 	    }
685 	    break;
686 
687         case IPP_TAG_RESOLUTION :
688 	    for (i = 0; i < count; i ++)
689 	    {
690 	      int xres, yres;
691 	      ipp_res_t units;
692 	      xres = ippGetResolution(attr, i, &yres, &units);
693 
694               if (xres == yres)
695                 printf("  %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
696 	      else
697                 printf("  %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
698 	    }
699 	    break;
700 
701 	case IPP_TAG_KEYWORD :
702 	    for (i = 0; i < count; i ++)
703               printf("  %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
704 	    break;
705 
706 	case IPP_TAG_TEXTLANG :
707 	case IPP_TAG_NAMELANG :
708 	case IPP_TAG_TEXT :
709 	case IPP_TAG_NAME :
710 	case IPP_TAG_URI :
711 	case IPP_TAG_URISCHEME :
712 	case IPP_TAG_CHARSET :
713 	case IPP_TAG_LANGUAGE :
714 	case IPP_TAG_MIMETYPE :
715 	    for (i = 0; i < count; i ++)
716               printf("  %s\n", ippGetString(attr, i, NULL));
717 	    break;
718 
719         case IPP_TAG_STRING :
720 	    for (i = 0; i < count; i ++)
721 	    {
722 	      int j, len;
723 	      unsigned char *data = ippGetOctetString(attr, i, &len);
724 
725               fputs("  ", stdout);
726 	      for (j = 0; j < len; j ++)
727 	      {
728 	        if (data[j] < ' ' || data[j] >= 0x7f)
729 		  printf("<%02X>", data[j]);
730 		else
731 		  putchar(data[j]);
732               }
733               putchar('\n');
734 	    }
735 	    break;
736 
737         case IPP_TAG_BOOLEAN :
738 	    break;
739 
740         default :
741 	    printf("  %s\n", ippTagString(ippGetValueTag(attr)));
742 	    break;
743       }
744     }
745 
746   }
747   else if (cupsCheckDestSupported(http, dest, dinfo, option, value))
748     puts("YES");
749   else
750     puts("NO");
751 }
752 
753 
754 /*
755  * 'usage()' - Show program usage.
756  */
757 
758 static void
usage(const char * arg)759 usage(const char *arg)			/* I - Argument for usage message */
760 {
761   if (arg)
762     printf("testdest: Unknown option \"%s\".\n", arg);
763 
764   puts("Usage:");
765   puts("  ./testdest [--device] name [operation ...]");
766   puts("  ./testdest [--device] ipp://... [operation ...]");
767   puts("  ./testdest [--device] ipps://... [operation ...]");
768   puts("  ./testdest --get");
769   puts("  ./testdest --enum [grayscale] [color] [duplex] [staple] [small]\n"
770        "                    [medium] [large]");
771   puts("");
772   puts("Operations:");
773   puts("  conflicts options");
774   puts("  default option");
775   puts("  localize option [value]");
776   puts("  media [borderless] [duplex] [exact] [ready] [name or size]");
777   puts("  print filename [options]");
778   puts("  supported [option [value]]");
779 
780   exit(arg != NULL);
781 }
782