2 * Simulated client test program for CUPS.
4 * Copyright © 2017-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * Include necessary headers...
17 #include <cups/cups.h>
18 #include <cups/raster.h>
19 #include <cups/string-private.h>
20 #include <cups/thread-private.h>
27 #define MAX_CLIENTS 16 /* Maximum number of client threads */
34 typedef struct _client_data_s
36 const char *uri
, /* Printer URI */
37 *hostname
, /* Hostname */
39 *resource
; /* Resource path */
40 int port
; /* Port number */
41 http_encryption_t encryption
; /* Use encryption? */
42 const char *docfile
, /* Document file */
43 *docformat
; /* Document format */
44 int grayscale
, /* Force grayscale? */
45 keepfile
; /* Keep temporary file? */
46 ipp_pstate_t printer_state
; /* Current printer state */
47 char printer_state_reasons
[1024];
48 /* Current printer-state-reasons */
49 int job_id
; /* Job ID for submitted job */
50 ipp_jstate_t job_state
; /* Current job state */
51 char job_state_reasons
[1024];
52 /* Current job-state-reasons */
60 static int client_count
= 0;
61 static _cups_mutex_t client_mutex
= _CUPS_MUTEX_INITIALIZER
;
62 static int verbosity
= 0;
69 static const char *make_raster_file(ipp_t
*response
, int grayscale
, char *tempname
, size_t tempsize
, const char **format
);
70 static void *monitor_printer(_client_data_t
*data
);
71 static void *run_client(_client_data_t
*data
);
72 static void show_attributes(const char *title
, int request
, ipp_t
*ipp
);
73 static void show_capabilities(ipp_t
*response
);
74 static void usage(void);
78 * 'main()' - Main entry.
81 int /* O - Exit status */
82 main(int argc
, /* I - Number of command-line arguments */
83 char *argv
[]) /* I - Command-line arguments */
85 int i
; /* Looping var */
86 const char *opt
; /* Current option */
87 int num_clients
= 0,/* Number of clients to simulate */
89 /* Number of clients that have been started */
90 char scheme
[32], /* URI scheme */
91 userpass
[256], /* Username:password */
92 hostname
[256], /* Hostname */
93 resource
[256]; /* Resource path */
94 _client_data_t data
; /* Client data */
98 * Parse command-line options...
104 memset(&data
, 0, sizeof(data
));
106 for (i
= 1; i
< argc
; i
++)
108 if (argv
[i
][0] == '-')
110 for (opt
= argv
[i
] + 1; *opt
; opt
++)
114 case 'c' : /* -c num-clients */
117 puts("Number of clients can only be specified once.");
125 puts("Expected client count after '-c'.");
130 if ((num_clients
= atoi(argv
[i
])) < 1)
132 puts("Number of clients must be one or more.");
138 case 'd' : /* -d document-format */
141 puts("Document format can only be specified once.");
149 puts("Expected document format after '-d'.");
154 data
.docformat
= argv
[i
];
157 case 'f' : /* -f print-file */
160 puts("Print file can only be specified once.");
168 puts("Expected print file after '-f'.");
173 data
.docfile
= argv
[i
];
189 printf("Unknown option '-%c'.\n", *opt
);
195 else if (data
.uri
|| (strncmp(argv
[i
], "ipp://", 6) && strncmp(argv
[i
], "ipps://", 7)))
197 printf("Unknown command-line argument '%s'.\n", argv
[i
]);
206 * Make sure we have everything we need.
211 puts("Expected printer URI.");
220 * Connect to the printer...
223 if (httpSeparateURI(HTTP_URI_CODING_ALL
, data
.uri
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), hostname
, sizeof(hostname
), &data
.port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
225 printf("Bad printer URI '%s'.\n", data
.uri
);
230 data
.port
= IPP_PORT
;
232 if (!strcmp(scheme
, "https") || !strcmp(scheme
, "ipps"))
233 data
.encryption
= HTTP_ENCRYPTION_ALWAYS
;
235 data
.encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
238 * Start the client threads...
241 data
.hostname
= hostname
;
242 data
.resource
= resource
;
244 while (clients_started
< num_clients
)
246 _cupsMutexLock(&client_mutex
);
247 if (client_count
< MAX_CLIENTS
)
249 _cups_thread_t tid
; /* New thread */
252 _cupsMutexUnlock(&client_mutex
);
253 tid
= _cupsThreadCreate((_cups_thread_func_t
)run_client
, &data
);
254 _cupsThreadDetach(tid
);
258 _cupsMutexUnlock(&client_mutex
);
263 while (client_count
> 0)
265 _cupsMutexLock(&client_mutex
);
266 printf("%d RUNNING CLIENTS\n", client_count
);
267 _cupsMutexUnlock(&client_mutex
);
276 * 'make_raster_file()' - Create a temporary raster file.
279 static const char * /* O - Print filename */
280 make_raster_file(ipp_t
*response
, /* I - Printer attributes */
281 int grayscale
, /* I - Force grayscale? */
282 char *tempname
, /* I - Temporary filename buffer */
283 size_t tempsize
, /* I - Size of temp file buffer */
284 const char **format
) /* O - Print format */
286 int i
, /* Looping var */
287 count
; /* Number of values */
288 ipp_attribute_t
*attr
; /* Printer attribute */
289 const char *type
= NULL
; /* Raster type (colorspace + bits) */
290 pwg_media_t
*media
= NULL
; /* Media size */
291 int xdpi
= 0, /* Horizontal resolution */
292 ydpi
= 0; /* Vertical resolution */
293 int fd
; /* Temporary file */
294 cups_mode_t mode
; /* Raster mode */
295 cups_raster_t
*ras
; /* Raster stream */
296 cups_page_header2_t header
; /* Page header */
297 unsigned char *line
, /* Line of raster data */
298 *lineptr
; /* Pointer into line */
299 unsigned y
, /* Current position on page */
300 xcount
, ycount
, /* Current count for X and Y */
301 xrep
, yrep
, /* Repeat count for X and Y */
302 xoff
, yoff
, /* Offsets for X and Y */
303 yend
; /* End Y value */
304 int temprow
, /* Row in template */
305 tempcolor
; /* Template color */
306 const char *template; /* Pointer into template */
307 const unsigned char *color
; /* Current color */
308 static const unsigned char colors
[][3] =
309 { /* Colors for test */
326 static const char * const templates
[] =
327 { /* Raster template */
328 " CCC U U PPPP SSS TTTTT EEEEE SSS TTTTT 000 1 222 333 4 55555 66 77777 888 999 ",
329 "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 ",
330 "C U U P P S T E S T 0 0 1 2 3 4 4 5 6 7 8 8 9 9 ",
331 "C U U PPPP SSS ----- T EEEE SSS T 0 0 0 1 22 333 44444 555 6666 7 888 9999 ",
332 "C U U P S T E S T 0 0 1 2 3 4 5 6 6 7 8 8 9 ",
333 "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 ",
334 " CCC UUU P SSS T EEEEE SSS T 000 111 22222 333 4 555 666 7 888 99 ",
340 * Figure out the output format...
343 if ((attr
= ippFindAttribute(response
, "document-format-supported", IPP_TAG_MIMETYPE
)) == NULL
)
345 puts("No supported document formats, aborting.");
351 if (!ippContainsString(attr
, *format
))
353 printf("Printer does not support document-format '%s'.\n", *format
);
357 if (!strcmp(*format
, "image/urf"))
358 mode
= CUPS_RASTER_WRITE_APPLE
;
359 else if (!strcmp(*format
, "image/pwg-raster"))
360 mode
= CUPS_RASTER_WRITE_PWG
;
363 printf("Unable to generate document-format '%s'.\n", *format
);
367 else if (ippContainsString(attr
, "image/urf"))
370 * Apple Raster format...
373 *format
= "image/urf";
374 mode
= CUPS_RASTER_WRITE_APPLE
;
376 else if (ippContainsString(attr
, "image/pwg-raster"))
379 * PWG Raster format...
382 *format
= "image/pwg-raster";
383 mode
= CUPS_RASTER_WRITE_PWG
;
388 * No supported raster format...
391 puts("Printer does not support Apple or PWG raster files, aborting.");
396 * Figure out the the media, resolution, and color mode...
399 if ((attr
= ippFindAttribute(response
, "media-default", IPP_TAG_KEYWORD
)) != NULL
)
402 * Use default media...
405 media
= pwgMediaForPWG(ippGetString(attr
, 0, NULL
));
407 else if ((attr
= ippFindAttribute(response
, "media-ready", IPP_TAG_KEYWORD
)) != NULL
)
413 if (ippContainsString(attr
, "na_letter_8.5x11in"))
414 media
= pwgMediaForPWG("na_letter_8.5x11in");
415 else if (ippContainsString(attr
, "iso_a4_210x297mm"))
416 media
= pwgMediaForPWG("iso_a4_210x297mm");
418 media
= pwgMediaForPWG(ippGetString(attr
, 0, NULL
));
422 puts("No default or ready media reported by printer, aborting.");
426 if (mode
== CUPS_RASTER_WRITE_APPLE
&& (attr
= ippFindAttribute(response
, "urf-supported", IPP_TAG_KEYWORD
)) != NULL
)
428 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
430 const char *val
= ippGetString(attr
, i
, NULL
);
432 if (!strncmp(val
, "RS", 2))
433 xdpi
= ydpi
= atoi(val
+ 2);
434 else if (!strncmp(val
, "W8", 2) && !type
)
436 else if (!strncmp(val
, "SRGB24", 6) && !grayscale
)
440 else if (mode
== CUPS_RASTER_WRITE_PWG
&& (attr
= ippFindAttribute(response
, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION
)) != NULL
)
442 for (i
= 0, count
= ippGetCount(attr
); i
< count
; i
++)
444 int tempxdpi
, tempydpi
;
447 tempxdpi
= ippGetResolution(attr
, 0, &tempydpi
, &tempunits
);
449 if (i
== 0 || tempxdpi
< xdpi
|| tempydpi
< ydpi
)
456 if ((attr
= ippFindAttribute(response
, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD
)) != NULL
)
458 if (!grayscale
&& ippContainsString(attr
, "srgb_8"))
460 else if (ippContainsString(attr
, "sgray_8"))
465 if (xdpi
< 72 || ydpi
< 72)
467 puts("No supported raster resolutions, aborting.");
473 puts("No supported color spaces or bit depths, aborting.");
478 * Make the raster context and details...
481 if (!cupsRasterInitPWGHeader(&header
, media
, type
, xdpi
, ydpi
, "one-sided", NULL
))
483 printf("Unable to initialize raster context: %s\n", cupsRasterErrorString());
487 header
.cupsInteger
[CUPS_RASTER_PWG_TotalPageCount
] = 1;
489 if (header
.cupsWidth
> (4 * header
.HWResolution
[0]))
491 xoff
= header
.HWResolution
[0] / 2;
492 yoff
= header
.HWResolution
[1] / 2;
500 xrep
= (header
.cupsWidth
- 2 * xoff
) / 140;
501 yrep
= xrep
* header
.HWResolution
[1] / header
.HWResolution
[0];
502 yend
= header
.cupsHeight
- yoff
;
505 * Prepare the raster file...
508 if ((line
= malloc(header
.cupsBytesPerLine
)) == NULL
)
510 printf("Unable to allocate %u bytes for raster output: %s\n", header
.cupsBytesPerLine
, strerror(errno
));
514 if ((fd
= cupsTempFd(tempname
, (int)tempsize
)) < 0)
516 printf("Unable to create temporary print file: %s\n", strerror(errno
));
521 if ((ras
= cupsRasterOpen(fd
, mode
)) == NULL
)
523 printf("Unable to open raster stream: %s\n", cupsRasterErrorString());
530 * Write a single page consisting of the template dots repeated over the page.
533 cupsRasterWriteHeader2(ras
, &header
);
535 memset(line
, 0xff, header
.cupsBytesPerLine
);
537 for (y
= 0; y
< yoff
; y
++)
538 cupsRasterWritePixels(ras
, line
, header
.cupsBytesPerLine
);
540 for (temprow
= 0, tempcolor
= 0; y
< yend
;)
542 template = templates
[temprow
];
543 color
= colors
[tempcolor
];
546 if (temprow
>= (int)(sizeof(templates
) / sizeof(templates
[0])))
550 if (tempcolor
>= (int)(sizeof(colors
) / sizeof(colors
[0])))
552 else if (tempcolor
> 3 && header
.cupsColorSpace
== CUPS_CSPACE_SW
)
556 memset(line
, 0xff, header
.cupsBytesPerLine
);
558 if (header
.cupsColorSpace
== CUPS_CSPACE_SW
)
561 * Do grayscale output...
564 for (lineptr
= line
+ xoff
; *template; template ++)
566 if (*template != ' ')
568 for (xcount
= xrep
; xcount
> 0; xcount
--)
583 for (lineptr
= line
+ 3 * xoff
; *template; template ++)
585 if (*template != ' ')
587 for (xcount
= xrep
; xcount
> 0; xcount
--, lineptr
+= 3)
588 memcpy(lineptr
, color
, 3);
597 for (ycount
= yrep
; ycount
> 0 && y
< yend
; ycount
--, y
++)
598 cupsRasterWritePixels(ras
, line
, header
.cupsBytesPerLine
);
601 memset(line
, 0xff, header
.cupsBytesPerLine
);
603 for (y
= 0; y
< header
.cupsHeight
; y
++)
604 cupsRasterWritePixels(ras
, line
, header
.cupsBytesPerLine
);
606 cupsRasterClose(ras
);
610 printf("PRINT FILE: %s\n", tempname
);
617 * 'monitor_printer()' - Monitor the job and printer states.
620 static void * /* O - Thread exit code */
622 _client_data_t
*data
) /* I - Client data */
624 http_t
*http
; /* Connection to printer */
625 ipp_t
*request
, /* IPP request */
626 *response
; /* IPP response */
627 ipp_attribute_t
*attr
; /* Attribute in response */
628 ipp_pstate_t printer_state
; /* Printer state */
629 char printer_state_reasons
[1024];
630 /* Printer state reasons */
631 ipp_jstate_t job_state
; /* Job state */
632 char job_state_reasons
[1024];/* Printer state reasons */
633 static const char * const jattrs
[] = /* Job attributes we want */
638 static const char * const pattrs
[] = /* Printer attributes we want */
641 "printer-state-reasons"
646 * Open a connection to the printer...
649 http
= httpConnect2(data
->hostname
, data
->port
, NULL
, AF_UNSPEC
, data
->encryption
, 1, 0, NULL
);
652 * Loop until the job is canceled, aborted, or completed.
655 printer_state
= (ipp_pstate_t
)0;
656 printer_state_reasons
[0] = '\0';
658 job_state
= (ipp_jstate_t
)0;
659 job_state_reasons
[0] = '\0';
661 while (data
->job_state
< IPP_JSTATE_CANCELED
)
664 * Reconnect to the printer as needed...
667 if (httpGetFd(http
) < 0)
668 httpReconnect2(http
, 30000, NULL
);
670 if (httpGetFd(http
) >= 0)
673 * Connected, so check on the printer state...
676 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
677 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, data
->uri
);
678 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
679 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
681 response
= cupsDoRequest(http
, request
, data
->resource
);
683 if ((attr
= ippFindAttribute(response
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
684 printer_state
= (ipp_pstate_t
)ippGetInteger(attr
, 0);
686 if ((attr
= ippFindAttribute(response
, "printer-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
687 ippAttributeString(attr
, printer_state_reasons
, sizeof(printer_state_reasons
));
689 if (printer_state
!= data
->printer_state
|| strcmp(printer_state_reasons
, data
->printer_state_reasons
))
691 printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", (int)printer_state
), printer_state_reasons
);
693 data
->printer_state
= printer_state
;
694 strlcpy(data
->printer_state_reasons
, printer_state_reasons
, sizeof(data
->printer_state_reasons
));
699 if (data
->job_id
> 0)
702 * Check the status of the job itself...
705 request
= ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES
);
706 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, data
->uri
);
707 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", data
->job_id
);
708 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
709 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", (int)(sizeof(jattrs
) / sizeof(jattrs
[0])), NULL
, jattrs
);
711 response
= cupsDoRequest(http
, request
, data
->resource
);
713 if ((attr
= ippFindAttribute(response
, "job-state", IPP_TAG_ENUM
)) != NULL
)
714 job_state
= (ipp_jstate_t
)ippGetInteger(attr
, 0);
716 if ((attr
= ippFindAttribute(response
, "job-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
717 ippAttributeString(attr
, job_state_reasons
, sizeof(job_state_reasons
));
719 if (job_state
!= data
->job_state
|| strcmp(job_state_reasons
, data
->job_state_reasons
))
721 printf("JOB %d: %s (%s)\n", data
->job_id
, ippEnumString("job-state", (int)job_state
), job_state_reasons
);
723 data
->job_state
= job_state
;
724 strlcpy(data
->job_state_reasons
, job_state_reasons
, sizeof(data
->job_state_reasons
));
731 if (data
->job_state
< IPP_JSTATE_CANCELED
)
734 * Sleep for 5 seconds...
742 * Cleanup and return...
747 printf("FINISHED MONITORING JOB %d\n", data
->job_id
);
754 * 'run_client()' - Run a client thread.
757 static void * /* O - Thread exit code */
759 _client_data_t
*data
) /* I - Client data */
761 _cups_thread_t monitor_id
; /* Monitoring thread ID */
762 const char *name
; /* Job name */
763 char tempfile
[1024] = ""; /* Temporary file (if any) */
764 _client_data_t ldata
; /* Local client data */
765 http_t
*http
; /* Connection to printer */
766 ipp_t
*request
, /* IPP request */
767 *response
; /* IPP response */
768 ipp_attribute_t
*attr
; /* Attribute in response */
769 static const char * const pattrs
[] = /* Printer attributes we are interested in */
773 "printer-description",
774 "media-col-database",
782 * Start monitoring the printer in the background...
785 monitor_id
= _cupsThreadCreate((_cups_thread_func_t
)monitor_printer
, &ldata
);
788 * Open a connection to the printer...
791 http
= httpConnect2(data
->hostname
, data
->port
, NULL
, AF_UNSPEC
, data
->encryption
, 1, 0, NULL
);
794 * Query printer status and capabilities...
797 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
798 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, ldata
.uri
);
799 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
800 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "requested-attributes", (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
802 response
= cupsDoRequest(http
, request
, ldata
.resource
);
805 show_capabilities(response
);
808 * Now figure out what we will be printing...
814 * User specified a print file, figure out the format...
816 const char *ext
; /* Filename extension */
818 if ((ext
= strrchr(ldata
.docfile
, '.')) != NULL
)
821 * Guess the format from the extension...
824 if (!strcmp(ext
, ".jpg"))
825 ldata
.docformat
= "image/jpeg";
826 else if (!strcmp(ext
, ".pdf"))
827 ldata
.docformat
= "application/pdf";
828 else if (!strcmp(ext
, ".ps"))
829 ldata
.docformat
= "application/postscript";
830 else if (!strcmp(ext
, ".pwg"))
831 ldata
.docformat
= "image/pwg-raster";
832 else if (!strcmp(ext
, ".urf"))
833 ldata
.docformat
= "image/urf";
835 ldata
.docformat
= "application/octet-stream";
840 * Tell the printer to auto-detect...
843 ldata
.docformat
= "application/octet-stream";
849 * No file specified, make something to test with...
852 if ((ldata
.docfile
= make_raster_file(response
, ldata
.grayscale
, tempfile
, sizeof(tempfile
), &ldata
.docformat
)) == NULL
)
859 * Create a job and wait for completion...
862 request
= ippNewRequest(IPP_OP_CREATE_JOB
);
863 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, ldata
.uri
);
864 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
866 if ((name
= strrchr(ldata
.docfile
, '/')) != NULL
)
869 name
= ldata
.docfile
;
871 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
, name
);
874 show_attributes("Create-Job request", 1, request
);
876 response
= cupsDoRequest(http
, request
, ldata
.resource
);
879 show_attributes("Create-Job response", 0, response
);
881 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE
)
883 printf("Unable to create print job: %s\n", cupsLastErrorString());
885 ldata
.job_state
= IPP_JSTATE_ABORTED
;
890 if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
892 puts("No job-id returned in Create-Job request.");
894 ldata
.job_state
= IPP_JSTATE_ABORTED
;
899 ldata
.job_id
= ippGetInteger(attr
, 0);
901 printf("CREATED JOB %d, sending %s of type %s\n", ldata
.job_id
, ldata
.docfile
, ldata
.docformat
);
905 request
= ippNewRequest(IPP_OP_SEND_DOCUMENT
);
906 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, ldata
.uri
);
907 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", ldata
.job_id
);
908 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, cupsUser());
909 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, ldata
.docformat
);
910 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
913 show_attributes("Send-Document request", 1, request
);
915 response
= cupsDoFileRequest(http
, request
, ldata
.resource
, ldata
.docfile
);
918 show_attributes("Send-Document response", 0, response
);
920 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE
)
922 printf("Unable to print file: %s\n", cupsLastErrorString());
924 ldata
.job_state
= IPP_JSTATE_ABORTED
;
929 puts("WAITING FOR JOB TO COMPLETE");
931 while (ldata
.job_state
< IPP_JSTATE_CANCELED
)
935 * Cleanup after ourselves...
942 if (tempfile
[0] && !ldata
.keepfile
)
945 _cupsThreadWait(monitor_id
);
947 _cupsMutexLock(&client_mutex
);
949 _cupsMutexUnlock(&client_mutex
);
956 * 'show_attributes()' - Show attributes in a request or response.
960 show_attributes(const char *title
, /* I - Title */
961 int request
, /* I - 1 for request, 0 for response */
962 ipp_t
*ipp
) /* I - IPP request/response */
964 int minor
, major
= ippGetVersion(ipp
, &minor
);
965 /* IPP version number */
966 ipp_tag_t group
= IPP_TAG_ZERO
;
967 /* Current group tag */
968 ipp_attribute_t
*attr
; /* Current attribute */
969 const char *name
; /* Attribute name */
970 char buffer
[1024]; /* Value */
973 printf("%s:\n", title
);
974 printf(" version=%d.%d\n", major
, minor
);
975 printf(" request-id=%d\n", ippGetRequestId(ipp
));
977 printf(" status-code=%s\n", ippErrorString(ippGetStatusCode(ipp
)));
979 for (attr
= ippFirstAttribute(ipp
); attr
; attr
= ippNextAttribute(ipp
))
981 if (group
!= ippGetGroupTag(attr
))
983 group
= ippGetGroupTag(attr
);
985 printf(" %s:\n", ippTagString(group
));
988 if ((name
= ippGetName(attr
)) != NULL
)
990 ippAttributeString(attr
, buffer
, sizeof(buffer
));
991 printf(" %s(%s%s)=%s\n", name
, ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)), buffer
);
998 * 'show_capabilities()' - Show printer capabilities.
1002 show_capabilities(ipp_t
*response
) /* I - Printer attributes */
1004 int i
; /* Looping var */
1005 ipp_attribute_t
*attr
; /* Attribute */
1006 char buffer
[1024]; /* Attribute value buffer */
1007 static const char * const pattrs
[] = /* Attributes we want to show */
1011 "finishings-default",
1013 "finishings-supported",
1017 "output-bin-default",
1018 "output-bin-supported",
1019 "print-color-mode-default",
1020 "print-color-mode-supported",
1023 "document-format-default",
1024 "document-format-supported",
1025 "pwg-raster-document-resolution-supported",
1026 "pwg-raster-document-type-supported",
1031 puts("CAPABILITIES:");
1032 for (i
= 0; i
< (int)(sizeof(pattrs
) / sizeof(pattrs
[0])); i
++)
1034 if ((attr
= ippFindAttribute(response
, pattrs
[i
], IPP_TAG_ZERO
)) != NULL
)
1036 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1037 printf(" %s=%s\n", pattrs
[i
], buffer
);
1044 * 'usage()' - Show program usage...
1050 puts("Usage: ./testclient printer-uri [options]");
1052 puts(" -c num-clients Simulate multiple clients");
1053 puts(" -d document-format Generate the specified format");
1054 puts(" -f print-file Print the named file");
1055 puts(" -g Force grayscale printing");
1056 puts(" -k Keep temporary files");
1057 puts(" -v Be more verbose");