• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
main(int argc,char * argv[])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 */
enum_cb(void * user_data,unsigned flags,cups_dest_t * dest)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
localize(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option,const char * value)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
print_file(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * filename,int num_options,cups_option_t * options)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
show_conflicts(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,int num_options,cups_option_t * options)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
show_default(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option)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
show_media(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,unsigned flags,const char * name)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
show_supported(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option,const char * value)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
usage(const char * arg)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