2 * Simulated client test program for CUPS.
4 * Copyright © 2017-2018 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * Include necessary headers...
16 #include <cups/cups.h>
17 #include <cups/raster.h>
18 #include <cups/string-private.h>
19 #include <cups/thread-private.h>
26 typedef struct _client_monitor_s
28 const char *uri
, /* Printer URI */
29 *hostname
, /* Hostname */
31 *resource
; /* Resource path */
32 int port
; /* Port number */
33 http_encryption_t encryption
; /* Use encryption? */
34 ipp_pstate_t printer_state
; /* Current printer state */
35 char printer_state_reasons
[1024];
36 /* Current printer-state-reasons */
37 int job_id
; /* Job ID for submitted job */
38 ipp_jstate_t job_state
; /* Current job state */
39 char job_state_reasons
[1024];
40 /* Current job-state-reasons */
48 static const char *make_raster_file(ipp_t
*response
, int grayscale
, char *tempname
, size_t tempsize
, const char **format
);
49 static void *monitor_printer(_client_monitor_t
*monitor
);
50 static void show_attributes(const char *title
, int request
, ipp_t
*ipp
);
51 static void show_capabilities(ipp_t
*response
);
52 static void usage(void);
56 * 'main()' - Main entry.
59 int /* O - Exit status */
60 main(int argc
, /* I - Number of command-line arguments */
61 char *argv
[]) /* I - Command-line arguments */
63 int i
; /* Looping var */
64 const char *opt
, /* Current option */
65 *uri
= NULL
, /* Printer URI */
70 int keepfile
= 0, /* Keep temp file? */
71 grayscale
= 0, /* Force grayscale? */
72 verbosity
= 0; /* Verbosity */
73 char tempfile
[1024] = "",
74 /* Temporary file (if any) */
75 scheme
[32], /* URI scheme */
76 userpass
[256], /* Username:password */
77 hostname
[256], /* Hostname */
78 resource
[256]; /* Resource path */
79 int port
; /* Port number */
80 http_encryption_t encryption
; /* Encryption mode */
81 _client_monitor_t monitor
; /* Monitoring data */
82 http_t
*http
; /* HTTP connection */
83 ipp_t
*request
, /* IPP request */
84 *response
; /* IPP response */
85 ipp_attribute_t
*attr
; /* IPP attribute */
86 static const char * const pattrs
[] = /* Printer attributes we are interested in */
90 "printer-description",
97 * Parse command-line options...
103 for (i
= 1; i
< argc
; i
++)
105 if (argv
[i
][0] == '-')
107 for (opt
= argv
[i
] + 1; *opt
; opt
++)
111 case 'd' : /* -d document-format */
114 puts("Document format can only be specified once.");
122 puts("Expected document format after '-d'.");
127 printformat
= argv
[i
];
130 case 'f' : /* -f print-file */
133 puts("Print file can only be specified once.");
141 puts("Expected print file after '-f'.");
162 printf("Unknown option '-%c'.\n", *opt
);
168 else if (uri
|| (strncmp(argv
[i
], "ipp://", 6) && strncmp(argv
[i
], "ipps://", 7)))
170 printf("Unknown command-line argument '%s'.\n", argv
[i
]);
179 * Make sure we have everything we need.
184 puts("Expected printer URI.");
190 * Connect to the printer...
193 if (httpSeparateURI(HTTP_URI_CODING_ALL
, uri
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
195 printf("Bad printer URI '%s'.\n", uri
);
202 if (!strcmp(scheme
, "https") || !strcmp(scheme
, "ipps"))
203 encryption
= HTTP_ENCRYPTION_ALWAYS
;
205 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
207 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 0, NULL
)) == NULL
)
209 printf("Unable to connect to '%s' on port %d: %s\n", hostname
, port
, cupsLastErrorString());
214 * Query printer status and capabilities...
217 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
218 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, uri
);
219 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
220 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
222 response
= cupsDoRequest(http
, request
, resource
);
225 show_capabilities(response
);
228 * Now figure out what we will be printing...
234 * User specified a print file, figure out the format...
237 if ((opt
= strrchr(printfile
, '.')) != NULL
)
240 * Guess the format from the extension...
243 if (!strcmp(opt
, ".jpg"))
244 printformat
= "image/jpeg";
245 else if (!strcmp(opt
, ".pdf"))
246 printformat
= "application/pdf";
247 else if (!strcmp(opt
, ".ps"))
248 printformat
= "application/postscript";
249 else if (!strcmp(opt
, ".pwg"))
250 printformat
= "image/pwg-raster";
251 else if (!strcmp(opt
, ".urf"))
252 printformat
= "image/urf";
254 printformat
= "application/octet-stream";
259 * Tell the printer to auto-detect...
262 printformat
= "application/octet-stream";
268 * No file specified, make something to test with...
271 if ((printfile
= make_raster_file(response
, grayscale
, tempfile
, sizeof(tempfile
), &printformat
)) == NULL
)
278 * Start monitoring the printer in the background...
281 memset(&monitor
, 0, sizeof(monitor
));
284 monitor
.hostname
= hostname
;
285 monitor
.resource
= resource
;
287 monitor
.encryption
= encryption
;
289 _cupsThreadCreate((_cups_thread_func_t
)monitor_printer
, &monitor
);
292 * Create the job and wait for completion...
295 request
= ippNewRequest(IPP_OP_CREATE_JOB
);
296 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, uri
);
297 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
299 if ((opt
= strrchr(printfile
, '/')) != NULL
)
304 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
, opt
);
307 show_attributes("Create-Job request", 1, request
);
309 response
= cupsDoRequest(http
, request
, resource
);
312 show_attributes("Create-Job response", 0, response
);
314 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE
)
316 printf("Unable to create print job: %s\n", cupsLastErrorString());
318 monitor
.job_state
= IPP_JSTATE_ABORTED
;
323 if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
325 puts("No job-id returned in Create-Job request.");
327 monitor
.job_state
= IPP_JSTATE_ABORTED
;
332 monitor
.job_id
= ippGetInteger(attr
, 0);
334 printf("CREATED JOB %d, sending %s of type %s\n", monitor
.job_id
, printfile
, printformat
);
338 request
= ippNewRequest(IPP_OP_SEND_DOCUMENT
);
339 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, uri
);
340 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", monitor
.job_id
);
341 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
342 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, printformat
);
343 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
346 show_attributes("Send-Document request", 1, request
);
348 response
= cupsDoFileRequest(http
, request
, resource
, printfile
);
351 show_attributes("Send-Document response", 0, response
);
353 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE
)
355 printf("Unable to print file: %s\n", cupsLastErrorString());
357 monitor
.job_state
= IPP_JSTATE_ABORTED
;
362 puts("WAITING FOR JOB TO COMPLETE");
364 while (monitor
.job_state
< IPP_JSTATE_CANCELED
)
368 * Cleanup after ourselves...
375 if (tempfile
[0] && !keepfile
)
378 return (monitor
.job_state
== IPP_JSTATE_COMPLETED
);
383 * 'make_raster_file()' - Create a temporary raster file.
386 static const char * /* O - Print filename */
387 make_raster_file(ipp_t
*response
, /* I - Printer attributes */
388 int grayscale
, /* I - Force grayscale? */
389 char *tempname
, /* I - Temporary filename buffer */
390 size_t tempsize
, /* I - Size of temp file buffer */
391 const char **format
) /* O - Print format */
393 int i
, /* Looping var */
394 count
; /* Number of values */
395 ipp_attribute_t
*attr
; /* Printer attribute */
396 const char *type
= NULL
; /* Raster type (colorspace + bits) */
397 pwg_media_t
*media
= NULL
; /* Media size */
398 int xdpi
= 0, /* Horizontal resolution */
399 ydpi
= 0; /* Vertical resolution */
400 int fd
; /* Temporary file */
401 cups_mode_t mode
; /* Raster mode */
402 cups_raster_t
*ras
; /* Raster stream */
403 cups_page_header2_t header
; /* Page header */
404 unsigned char *line
, /* Line of raster data */
405 *lineptr
; /* Pointer into line */
406 unsigned y
, /* Current position on page */
407 xcount
, ycount
, /* Current count for X and Y */
408 xrep
, yrep
, /* Repeat count for X and Y */
409 xoff
, yoff
, /* Offsets for X and Y */
410 yend
; /* End Y value */
411 int temprow
, /* Row in template */
412 tempcolor
; /* Template color */
413 const char *template; /* Pointer into template */
414 const unsigned char *color
; /* Current color */
415 static const unsigned char colors
[][3] =
416 { /* Colors for test */
433 static const char * const templates
[] =
434 { /* Raster template */
435 " CCC U U PPPP SSS TTTTT EEEEE SSS TTTTT 000 1 222 333 4 55555 66 77777 888 999 ",
436 "C C U U P P S S T E S S T 0 0 11 2 2 3 3 4 4 5 6 7 8 8 9 9 ",
437 "C U U P P S T E S T 0 0 1 2 3 4 4 5 6 7 8 8 9 9 ",
438 "C U U PPPP SSS ----- T EEEE SSS T 0 0 0 1 22 333 44444 555 6666 7 888 9999 ",
439 "C U U P S T E S T 0 0 1 2 3 4 5 6 6 7 8 8 9 ",
440 "C C U U P S S T E S S T 0 0 1 2 3 3 4 5 5 6 6 7 8 8 9 ",
441 " CCC UUU P SSS T EEEEE SSS T 000 111 22222 333 4 555 666 7 888 99 ",
447 * Figure out the output format...
450 if ((attr
= ippFindAttribute(response
, "document-format-supported", IPP_TAG_MIMETYPE
)) == NULL
)
452 puts("No supported document formats, aborting.");
458 if (!ippContainsString(attr
, *format
))
460 printf("Printer does not support document-format '%s'.\n", *format
);
464 if (!strcmp(*format
, "image/urf"))
465 mode
= CUPS_RASTER_WRITE_APPLE
;
466 else if (!strcmp(*format
, "image/pwg-raster"))
467 mode
= CUPS_RASTER_WRITE_PWG
;
470 printf("Unable to generate document-format '%s'.\n", *format
);
474 else if (ippContainsString(attr
, "image/urf"))
477 * Apple Raster format...
480 *format
= "image/urf";
481 mode
= CUPS_RASTER_WRITE_APPLE
;
483 else if (ippContainsString(attr
, "image/pwg-raster"))
486 * PWG Raster format...
489 *format
= "image/pwg-raster";
490 mode
= CUPS_RASTER_WRITE_PWG
;
495 * No supported raster format...
498 puts("Printer does not support Apple or PWG raster files, aborting.");
503 * Figure out the the media, resolution, and color mode...
506 if ((attr
= ippFindAttribute(response
, "media-default", IPP_TAG_KEYWORD
)) != NULL
)
509 * Use default media...
512 media
= pwgMediaForPWG(ippGetString(attr
, 0, NULL
));
514 else if ((attr
= ippFindAttribute(response
, "media-ready", IPP_TAG_KEYWORD
)) != NULL
)
520 if (ippContainsString(attr
, "na_letter_8.5x11in"))
521 media
= pwgMediaForPWG("na_letter_8.5x11in");
522 else if (ippContainsString(attr
, "iso_a4_210x297mm"))
523 media
= pwgMediaForPWG("iso_a4_210x297mm");
525 media
= pwgMediaForPWG(ippGetString(attr
, 0, NULL
));
529 puts("No default or ready media reported by printer, aborting.");
533 if (mode
== CUPS_RASTER_WRITE_APPLE
&& (attr
= ippFindAttribute(response
, "urf-supported", IPP_TAG_KEYWORD
)) != NULL
)
535 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
537 const char *val
= ippGetString(attr
, i
, NULL
);
539 if (!strncmp(val
, "RS", 2))
540 xdpi
= ydpi
= atoi(val
+ 2);
541 else if (!strncmp(val
, "W8", 2) && !type
)
543 else if (!strncmp(val
, "SRGB24", 6) && !grayscale
)
547 else if (mode
== CUPS_RASTER_WRITE_PWG
&& (attr
= ippFindAttribute(response
, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION
)) != NULL
)
549 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
551 int tempxdpi
, tempydpi
;
554 tempxdpi
= ippGetResolution(attr
, 0, &tempydpi
, &tempunits
);
556 if (i
== 0 || tempxdpi
< xdpi
|| tempydpi
< ydpi
)
563 if ((attr
= ippFindAttribute(response
, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD
)) != NULL
)
565 if (!grayscale
&& ippContainsString(attr
, "srgb_8"))
567 else if (ippContainsString(attr
, "sgray_8"))
572 if (xdpi
< 72 || ydpi
< 72)
574 puts("No supported raster resolutions, aborting.");
580 puts("No supported color spaces or bit depths, aborting.");
585 * Make the raster context and details...
588 if (!cupsRasterInitPWGHeader(&header
, media
, type
, xdpi
, ydpi
, "one-sided", NULL
))
590 printf("Unable to initialize raster context: %s\n", cupsRasterErrorString());
594 header
.cupsInteger
[CUPS_RASTER_PWG_TotalPageCount
] = 1;
596 if (header
.cupsWidth
> (4 * header
.HWResolution
[0]))
598 xoff
= header
.HWResolution
[0] / 2;
599 yoff
= header
.HWResolution
[1] / 2;
607 xrep
= (header
.cupsWidth
- 2 * xoff
) / 140;
608 yrep
= xrep
* header
.HWResolution
[1] / header
.HWResolution
[0];
609 yend
= header
.cupsHeight
- yoff
;
612 * Prepare the raster file...
615 if ((line
= malloc(header
.cupsBytesPerLine
)) == NULL
)
617 printf("Unable to allocate %u bytes for raster output: %s\n", header
.cupsBytesPerLine
, strerror(errno
));
621 if ((fd
= cupsTempFd(tempname
, (int)tempsize
)) < 0)
623 printf("Unable to create temporary print file: %s\n", strerror(errno
));
628 if ((ras
= cupsRasterOpen(fd
, mode
)) == NULL
)
630 printf("Unable to open raster stream: %s\n", cupsRasterErrorString());
637 * Write a single page consisting of the template dots repeated over the page.
640 cupsRasterWriteHeader2(ras
, &header
);
642 memset(line
, 0xff, header
.cupsBytesPerLine
);
644 for (y
= 0; y
< yoff
; y
++)
645 cupsRasterWritePixels(ras
, line
, header
.cupsBytesPerLine
);
647 for (temprow
= 0, tempcolor
= 0; y
< yend
;)
649 template = templates
[temprow
];
650 color
= colors
[tempcolor
];
653 if (temprow
>= (int)(sizeof(templates
) / sizeof(templates
[0])))
657 if (tempcolor
>= (int)(sizeof(colors
) / sizeof(colors
[0])))
659 else if (tempcolor
> 3 && header
.cupsColorSpace
== CUPS_CSPACE_SW
)
663 memset(line
, 0xff, header
.cupsBytesPerLine
);
665 if (header
.cupsColorSpace
== CUPS_CSPACE_SW
)
668 * Do grayscale output...
671 for (lineptr
= line
+ xoff
; *template; template ++)
673 if (*template != ' ')
675 for (xcount
= xrep
; xcount
> 0; xcount
--)
690 for (lineptr
= line
+ 3 * xoff
; *template; template ++)
692 if (*template != ' ')
694 for (xcount
= xrep
; xcount
> 0; xcount
--, lineptr
+= 3)
695 memcpy(lineptr
, color
, 3);
704 for (ycount
= yrep
; ycount
> 0 && y
< yend
; ycount
--, y
++)
705 cupsRasterWritePixels(ras
, line
, header
.cupsBytesPerLine
);
708 memset(line
, 0xff, header
.cupsBytesPerLine
);
710 for (y
= 0; y
< header
.cupsHeight
; y
++)
711 cupsRasterWritePixels(ras
, line
, header
.cupsBytesPerLine
);
713 cupsRasterClose(ras
);
717 printf("PRINT FILE: %s\n", tempname
);
724 * 'monitor_printer()' - Monitor the job and printer states.
727 static void * /* O - Thread exit code */
729 _client_monitor_t
*monitor
) /* I - Monitoring data */
731 http_t
*http
; /* Connection to printer */
732 ipp_t
*request
, /* IPP request */
733 *response
; /* IPP response */
734 ipp_attribute_t
*attr
; /* Attribute in response */
735 ipp_pstate_t printer_state
; /* Printer state */
736 char printer_state_reasons
[1024];
737 /* Printer state reasons */
738 ipp_jstate_t job_state
; /* Job state */
739 char job_state_reasons
[1024];/* Printer state reasons */
740 static const char * const jattrs
[] = /* Job attributes we want */
745 static const char * const pattrs
[] = /* Printer attributes we want */
748 "printer-state-reasons"
753 * Open a connection to the printer...
756 http
= httpConnect2(monitor
->hostname
, monitor
->port
, NULL
, AF_UNSPEC
, monitor
->encryption
, 1, 0, NULL
);
759 * Loop until the job is canceled, aborted, or completed.
762 printer_state
= (ipp_pstate_t
)0;
763 printer_state_reasons
[0] = '\0';
765 job_state
= (ipp_jstate_t
)0;
766 job_state_reasons
[0] = '\0';
768 while (monitor
->job_state
< IPP_JSTATE_CANCELED
)
771 * Reconnect to the printer as needed...
774 if (httpGetFd(http
) < 0)
775 httpReconnect2(http
, 30000, NULL
);
777 if (httpGetFd(http
) >= 0)
780 * Connected, so check on the printer state...
783 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
784 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, monitor
->uri
);
785 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
786 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
788 response
= cupsDoRequest(http
, request
, monitor
->resource
);
790 if ((attr
= ippFindAttribute(response
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
791 printer_state
= (ipp_pstate_t
)ippGetInteger(attr
, 0);
793 if ((attr
= ippFindAttribute(response
, "printer-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
794 ippAttributeString(attr
, printer_state_reasons
, sizeof(printer_state_reasons
));
796 if (printer_state
!= monitor
->printer_state
|| strcmp(printer_state_reasons
, monitor
->printer_state_reasons
))
798 printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state
), printer_state_reasons
);
800 monitor
->printer_state
= printer_state
;
801 strlcpy(monitor
->printer_state_reasons
, printer_state_reasons
, sizeof(monitor
->printer_state_reasons
));
806 if (monitor
->job_id
> 0)
809 * Check the status of the job itself...
812 request
= ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES
);
813 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, monitor
->uri
);
814 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", monitor
->job_id
);
815 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
816 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", (int)(sizeof(jattrs
) / sizeof(jattrs
[0])), NULL
, jattrs
);
818 response
= cupsDoRequest(http
, request
, monitor
->resource
);
820 if ((attr
= ippFindAttribute(response
, "job-state", IPP_TAG_ENUM
)) != NULL
)
821 job_state
= (ipp_jstate_t
)ippGetInteger(attr
, 0);
823 if ((attr
= ippFindAttribute(response
, "job-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
824 ippAttributeString(attr
, job_state_reasons
, sizeof(job_state_reasons
));
826 if (job_state
!= monitor
->job_state
|| strcmp(job_state_reasons
, monitor
->job_state_reasons
))
828 printf("JOB %d: %s (%s)\n", monitor
->job_id
, ippEnumString("job-state", job_state
), job_state_reasons
);
830 monitor
->job_state
= job_state
;
831 strlcpy(monitor
->job_state_reasons
, job_state_reasons
, sizeof(monitor
->job_state_reasons
));
838 if (monitor
->job_state
< IPP_JSTATE_CANCELED
)
841 * Sleep for 5 seconds...
849 * Cleanup and return...
859 * 'show_attributes()' - Show attributes in a request or response.
863 show_attributes(const char *title
, /* I - Title */
864 int request
, /* I - 1 for request, 0 for response */
865 ipp_t
*ipp
) /* I - IPP request/response */
867 int minor
, major
= ippGetVersion(ipp
, &minor
);
868 /* IPP version number */
869 ipp_tag_t group
= IPP_TAG_ZERO
;
870 /* Current group tag */
871 ipp_attribute_t
*attr
; /* Current attribute */
872 const char *name
; /* Attribute name */
873 char buffer
[1024]; /* Value */
876 printf("%s:\n", title
);
877 printf(" version=%d.%d\n", major
, minor
);
878 printf(" request-id=%d\n", ippGetRequestId(ipp
));
880 printf(" status-code=%s\n", ippErrorString(ippGetStatusCode(ipp
)));
882 for (attr
= ippFirstAttribute(ipp
); attr
; attr
= ippNextAttribute(ipp
))
884 if (group
!= ippGetGroupTag(attr
))
886 group
= ippGetGroupTag(attr
);
888 printf(" %s:\n", ippTagString(group
));
891 if ((name
= ippGetName(attr
)) != NULL
)
893 ippAttributeString(attr
, buffer
, sizeof(buffer
));
894 printf(" %s(%s%s)=%s\n", name
, ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)), buffer
);
901 * 'show_capabilities()' - Show printer capabilities.
905 show_capabilities(ipp_t
*response
) /* I - Printer attributes */
907 int i
; /* Looping var */
908 ipp_attribute_t
*attr
; /* Attribute */
909 char buffer
[1024]; /* Attribute value buffer */
910 static const char * const pattrs
[] = /* Attributes we want to show */
914 "finishings-default",
916 "finishings-supported",
920 "output-bin-default",
921 "output-bin-supported",
922 "print-color-mode-default",
923 "print-color-mode-supported",
926 "document-format-default",
927 "document-format-supported",
928 "pwg-raster-document-resolution-supported",
929 "pwg-raster-document-type-supported",
934 puts("CAPABILITIES:");
935 for (i
= 0; i
< (int)(sizeof(pattrs
) / sizeof(pattrs
[0])); i
++)
937 if ((attr
= ippFindAttribute(response
, pattrs
[i
], IPP_TAG_ZERO
)) != NULL
)
939 ippAttributeString(attr
, buffer
, sizeof(buffer
));
940 printf(" %s=%s\n", pattrs
[i
], buffer
);
947 * 'usage()' - Show program usage...
953 puts("Usage: ./testclient printer-uri [options]");
955 puts(" -d document-format Generate the specified format");
956 puts(" -f print-file Print the named file");
957 puts(" -g Force grayscale printing");
958 puts(" -k Keep temporary files");
959 puts(" -v Be more verbose");