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   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
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