From: Michael Sweet Date: Fri, 14 Apr 2017 16:36:40 +0000 (-0400) Subject: Save work on new CUPS Programming Manual. X-Git-Tag: v2.2.4~85 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=abacc52be4910785b39b85357b66bd3686203e53;p=thirdparty%2Fcups.git Save work on new CUPS Programming Manual. --- diff --git a/.gitignore b/.gitignore index 36a09efd54..ed44c3caa6 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ berkeley/lpc berkeley/lpq berkeley/lpr berkeley/lprm +cgi-bin/makedocset cgi-bin/testcgi cgi-bin/testhi cgi-bin/testhi.index @@ -89,6 +90,7 @@ notifier/mailto notifier/rss notifier/testnotify packaging/cups.list +org.cups.docset* patches ppdc/genstrings ppdc/ppd/ diff --git a/cups/cupspm.md b/cups/cupspm.md new file mode 100644 index 0000000000..120612809e --- /dev/null +++ b/cups/cupspm.md @@ -0,0 +1,1120 @@ +--- +title: CUPS Programming Manual +author: Michael R Sweet +copyright: Copyright (c) 2007-2017 by Apple Inc. All Rights Reserved. +version: 2.2.4 +... + +# Introduction + +CUPS provides the "cups" library to talk to the different parts of CUPS and with +Internet Printing Protocol (IPP) printers. The "cups" library functions are +accessed by including the `` header. + +CUPS is based on the Internet Printing Protocol ("IPP"), which allows clients +(applications) to communicate with a server (the scheduler, printers, etc.) to +get a list of destinations, send print jobs, and so forth. You identify which +server you want to communicate with using a pointer to the opaque structure +`http_t`. The `CUPS_HTTP_DEFAULT` constant can be used when you want to talk to +the CUPS scheduler. + + +## Guidelines + +When writing software that uses the "cups" library: + +- Do not use undocumented or deprecated APIs, +- Do not rely on pre-configured printers, +- Do not assume that printers support specific features or formats, and +- Do not rely on implementation details (PPDs, etc.) + +CUPS is designed to insulate users and developers from the implementation +details of printers and file formats. The goal is to allow an application to +supply a print file in a standard format with the user intent ("print four +copies, two-sided on A4 media, and staple each copy") and have the printing +system manage the printer communication and format conversion needed. + +Similarly, printer and job management applications can use standard query +operations to obtain the status information in a common, generic form and use +standard management operations to control the state of those printers and jobs. + + +## Terms Used in This Document + +A *Destination* is a printer or print queue that accepts print jobs. A +*Print Job* is one or more documents that are processed by a destination +using options supplied when creating the job. A *Document* is a file (JPEG +image, PDF file, etc.) suitable for printing. An *Option* controls some aspect +of printing, such as the media used. *Media* is the sheets or roll that is +printed on. An *Attribute* is an option encoded for an Internet Printing +Protocol (IPP) request. + + +## Compiling Programs That Use the CUPS API + +The CUPS libraries can be used from any C, C++, or Objective C program. +The method of compiling against the libraries varies depending on the +operating system and installation of CUPS. The following sections show how +to compile a simple program (shown below) in two common environments. + +The following simple program lists the available destinations: + + #include + #include + + int print_dest(void *user_data, unsigned flags, cups_dest_t *dest) + { + if (dest->instance) + printf("%s/%s\n", dest->name, dest->instance); + else + puts(dest->name); + + return (1); + } + + int main(void) + { + cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, print_dest, NULL); + + return (0); + } + + +### Compiling with Xcode + +In Xcode, choose *New Project...* from the *File* menu (or press SHIFT+CMD+N), +then select the *Command Line Tool* under the macOS Application project type. +Click *Next* and enter a name for the project, for example "firstcups". Click +*Next* and choose a project directory. The click *Next* to create the project. + +In the project window, click on the *Build Phases* group and expand the +*Link Binary with Libraries* section. Click *+*, type "libcups" to show the +library, and then double-click on `libcups.tbd`. + +Finally, click on the `main.c` file in the sidebar and copy the example program +to the file. Build and run (CMD+R) to see the list of destinations. + + +### Compiling with GCC + +From the command-line, create a file called `sample.c` using your favorite +editor, copy the example to this file, and save. Then run the following command +to compile it with GCC and run it: + + gcc -o simple `cups-config --cflags` simple.c `cups-config --libs` + ./simple + +The `cups-config` command provides the compiler flags (`cups-config --cflags`) +and libraries (`cups-config --libs`) needed for the local system. + + +# Working with Destinations + + +## Finding Available Destinations + + +## Getting Information About a Destination + + +### Getting Supported Options and Values + +cupsCheckDestSupported and cups + +### Getting Default Options and Values + +### Getting Ready Options and Values + +### Media Options + +### Other Standard Options + +### Localizing Options and Values + +## Submitting a Print Job + +## Canceling a Print Job + + +# IPP Requests and Responses + +Why you'd want to do this, etc. + + +## Connecting to a Destination + +## Sending an IPP Request + +## Getting the IPP Response + +## Handling Authentication + +## Handling Certificate Validation + + + + + + + + + + + + + + + + + + + +

Printers and Classes

+ +

Printers and classes (collections of printers) are accessed through +the cups_dest_t structure which +includes the name (name), instance (instance - +a way of selecting certain saved options/settings), and the options and +attributes associated with that destination (num_options and +options). Destinations are created using the +cupsGetDests function and freed +using the cupsFreeDests function. +The cupsGetDest function finds a +specific destination for printing:

+ +
+#include <cups/cups.h>
+
+cups_dest_t *dests;
+int num_dests = cupsGetDests(&dests);
+cups_dest_t *dest = cupsGetDest("name", NULL, num_dests, dests);
+
+/* do something with dest */
+
+cupsFreeDests(num_dests, dests);
+
+ +

Passing NULL to +cupsGetDest for the destination name +will return the default destination. Similarly, passing a NULL +instance will return the default instance for that destination.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1: Printer Attributes
Attribute NameDescription
"auth-info-required"The type of authentication required for printing to this + destination: "none", "username,password", "domain,username,password", + or "negotiate" (Kerberos)
"printer-info"The human-readable description of the destination such as "My + Laser Printer".
"printer-is-accepting-jobs""true" if the destination is accepting new jobs, "false" if + not.
"printer-is-shared""true" if the destination is being shared with other computers, + "false" if not.
"printer-location"The human-readable location of the destination such as "Lab 4".
"printer-make-and-model"The human-readable make and model of the destination such as "HP + LaserJet 4000 Series".
"printer-state""3" if the destination is idle, "4" if the destination is printing + a job, and "5" if the destination is stopped.
"printer-state-change-time"The UNIX time when the destination entered the current state.
"printer-state-reasons"Additional comma-delimited state keywords for the destination + such as "media-tray-empty-error" and "toner-low-warning".
"printer-type"The cups_printer_t + value associated with the destination.
+ +

Options

+ +

Options are stored in arrays of +cups_option_t structures. Each +option has a name (name) and value (value) +associated with it. The cups_dest_t +num_options and options members contain the +default options for a particular destination, along with several informational +attributes about the destination as shown in Table 1. +The cupsGetOption function gets +the value for the named option. For example, the following code lists the +available destinations and their human-readable descriptions:

+ +
+#include <cups/cups.h>
+
+cups_dest_t *dests;
+int num_dests = cupsGetDests(&dests);
+cups_dest_t *dest;
+int i;
+const char *value;
+
+for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+  if (dest->instance == NULL)
+  {
+    value = cupsGetOption("printer-info", dest->num_options, dest->options);
+    printf("%s (%s)\n", dest->name, value ? value : "no description");
+  }
+
+cupsFreeDests(num_dests, dests);
+
+ +

You can create your own option arrays using the +cupsAddOption function, which +adds a single named option to an array:

+ +
+#include <cups/cups.h>
+
+int num_options = 0;
+cups_option_t *options = NULL;
+
+/* The returned num_options value is updated as needed */
+num_options = cupsAddOption("first", "value", num_options, &options);
+
+/* This adds a second option value */
+num_options = cupsAddOption("second", "value", num_options, &options);
+
+/* This replaces the first option we added */
+num_options = cupsAddOption("first", "new value", num_options, &options);
+
+ +

Use a for loop to copy the options from a destination:

+ +
+#include <cups/cups.h>
+
+int i;
+int num_options = 0;
+cups_option_t *options = NULL;
+cups_dest_t *dest;
+
+for (i = 0; i < dest->num_options; i ++)
+  num_options = cupsAddOption(dest->options[i].name, dest->options[i].value,
+                              num_options, &options);
+
+ +

Use the cupsFreeOptions +function to free the options array when you are done using it:

+ +
+cupsFreeOptions(num_options, options);
+
+ +

Print Jobs

+ +

Print jobs are identified by a locally-unique job ID number from 1 to +231-1 and have options and one or more files for printing to a +single destination. The cupsPrintFile +function creates a new job with one file. The following code prints the CUPS +test page file:

+ +
+#include <cups/cups.h>
+
+cups_dest_t *dest;
+int num_options;
+cups_option_t *options;
+int job_id;
+
+/* Print a single file */
+job_id = cupsPrintFile(dest->name, "/usr/share/cups/data/testprint.ps",
+                        "Test Print", num_options, options);
+
+ +

The cupsPrintFiles function +creates a job with multiple files. The files are provided in a +char * array:

+ +
+#include <cups/cups.h>
+
+cups_dest_t *dest;
+int num_options;
+cups_option_t *options;
+int job_id;
+char *files[3] = { "file1.pdf", "file2.pdf", "file3.pdf" };
+
+/* Print three files */
+job_id = cupsPrintFiles(dest->name, 3, files, "Test Print", num_options, options);
+
+ +

Finally, the cupsCreateJob +function creates a new job with no files in it. Files are added using the +cupsStartDocument, +cupsWriteRequestData, +and cupsFinishDocument functions. +The following example creates a job with 10 text files for printing:

+ +
+#include <cups/cups.h>
+
+cups_dest_t *dest;
+int num_options;
+cups_option_t *options;
+int job_id;
+int i;
+char buffer[1024];
+
+/* Create the job */
+job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, dest->name, "10 Text Files",
+                       num_options, options);
+
+/* If the job is created, add 10 files */
+if (job_id > 0)
+{
+  for (i = 1; i <= 10; i ++)
+  {
+    snprintf(buffer, sizeof(buffer), "file%d.txt", i);
+
+    cupsStartDocument(CUPS_HTTP_DEFAULT, dest->name, job_id, buffer,
+                      CUPS_FORMAT_TEXT, i == 10);
+
+    snprintf(buffer, sizeof(buffer),
+             "File %d\n"
+             "\n"
+             "One fish,\n"
+             "Two fish,\n
+             "Red fish,\n
+             "Blue fish\n", i);
+
+    /* cupsWriteRequestData can be called as many times as needed */
+    cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, strlen(buffer));
+
+    cupsFinishDocument(CUPS_HTTP_DEFAULT, dest->name);
+  }
+}
+
+ +

Once you have created a job, you can monitor its status using the +cupsGetJobs function, which returns +an array of cups_job_t structures. +Each contains the job ID (id), destination name +(dest), title (title), and other information +associated with the job. The job array is freed using the +cupsFreeJobs function. The following +example monitors a specific job ID, showing the current job state once every +5 seconds until the job is completed:

+ +
+#include <cups/cups.h>
+
+cups_dest_t *dest;
+int job_id;
+int num_jobs;
+cups_job_t *jobs;
+int i;
+ipp_jstate_t job_state = IPP_JOB_PENDING;
+
+while (job_state < IPP_JOB_STOPPED)
+{
+  /* Get my jobs (1) with any state (-1) */
+  num_jobs = cupsGetJobs(&jobs, dest->name, 1, -1);
+
+  /* Loop to find my job */
+  job_state = IPP_JOB_COMPLETED;
+
+  for (i = 0; i < num_jobs; i ++)
+    if (jobs[i].id == job_id)
+    {
+      job_state = jobs[i].state;
+      break;
+    }
+
+  /* Free the job array */
+  cupsFreeJobs(num_jobs, jobs);
+
+  /* Show the current state */
+  switch (job_state)
+  {
+    case IPP_JOB_PENDING :
+        printf("Job %d is pending.\n", job_id);
+        break;
+    case IPP_JOB_HELD :
+        printf("Job %d is held.\n", job_id);
+        break;
+    case IPP_JOB_PROCESSING :
+        printf("Job %d is processing.\n", job_id);
+        break;
+    case IPP_JOB_STOPPED :
+        printf("Job %d is stopped.\n", job_id);
+        break;
+    case IPP_JOB_CANCELED :
+        printf("Job %d is canceled.\n", job_id);
+        break;
+    case IPP_JOB_ABORTED :
+        printf("Job %d is aborted.\n", job_id);
+        break;
+    case IPP_JOB_COMPLETED :
+        printf("Job %d is completed.\n", job_id);
+        break;
+  }
+
+  /* Sleep if the job is not finished */
+  if (job_state < IPP_JOB_STOPPED)
+    sleep(5);
+}
+
+ +

To cancel a job, use the +cupsCancelJob function with the +job ID:

+ +
+#include <cups/cups.h>
+
+cups_dest_t *dest;
+int job_id;
+
+cupsCancelJob(dest->name, job_id);
+
+ +

Error Handling

+ +

If any of the CUPS API printing functions returns an error, the reason for +that error can be found by calling the +cupsLastError and +cupsLastErrorString functions. +cupsLastError returns the last IPP +error code +(ipp_status_t) +that was encountered, while +cupsLastErrorString returns +a (localized) human-readable string that can be shown to the user. For example, +if any of the job creation functions returns a job ID of 0, you can use +cupsLastErrorString to show +the reason why the job could not be created:

+ +
+#include <cups/cups.h>
+
+int job_id;
+
+if (job_id == 0)
+  puts(cupsLastErrorString());
+
+ +

Passwords and Authentication

+ +

CUPS supports authentication of any request, including submission of print +jobs. The default mechanism for getting the username and password is to use the +login user and a password from the console.

+ +

To support other types of applications, in particular Graphical User +Interfaces ("GUIs"), the CUPS API provides functions to set the default +username and to register a callback function that returns a password string.

+ +

The cupsSetPasswordCB +function is used to set a password callback in your program. Only one +function can be used at any time.

+ +

The cupsSetUser function sets the +current username for authentication. This function can be called by your +password callback function to change the current username as needed.

+ +

The following example shows a simple password callback that gets a +username and password from the user:

+ +
+#include <cups/cups.h>
+
+const char *
+my_password_cb(const char *prompt)
+{
+  char	user[65];
+
+
+  puts(prompt);
+
+  /* Get a username from the user */
+  printf("Username: ");
+  if (fgets(user, sizeof(user), stdin) == NULL)
+    return (NULL);
+
+  /* Strip the newline from the string and set the user */
+  user[strlen(user) - 1] = '\0';
+
+  cupsSetUser(user);
+
+  /* Use getpass() to ask for the password... */
+  return (getpass("Password: "));
+}
+
+cupsSetPasswordCB(my_password_cb);
+
+ +

Similarly, a GUI could display the prompt string in a window with input +fields for the username and password. The username should default to the +string returned by the cupsUser +function.

+ + +

Overview

+ +

The CUPS HTTP and IPP APIs provide low-level access to the HTTP and IPP +protocols and CUPS scheduler. They are typically used by monitoring and +administration programs to perform specific functions not supported by the +high-level CUPS API functions.

+ +

The HTTP APIs use an opaque structure called +http_t to manage connections to +a particular HTTP or IPP server. The +httpConnectEncrypt function is +used to create an instance of this structure for a particular server. +The constant CUPS_HTTP_DEFAULT can be used with all of the +cups functions to refer to the default CUPS server - the functions +create a per-thread http_t as needed.

+ +

The IPP APIs use two opaque structures for requests (messages sent to the CUPS scheduler) and responses (messages sent back to your application from the scheduler). The ipp_t type holds a complete request or response and is allocated using the ippNew or ippNewRequest functions and freed using the ippDelete function.

+ +

The second opaque structure is called ipp_attribute_t and holds a single IPP attribute which consists of a group tag (ippGetGroupTag), a value type tag (ippGetValueTag), the attribute name (ippGetName), and 1 or more values (ippGetCount, ippGetBoolean, ippGetCollection, ippGetDate, ippGetInteger, ippGetRange, ippGetResolution, and ippGetString). Attributes are added to an ipp_t pointer using one of the ippAdd functions. For example, use ippAddString to add the "printer-uri" and "requesting-user-name" string attributes to a request:

+ +
+ipp_t *request = ippNewRequest(IPP_GET_JOBS);
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+             NULL, "ipp://localhost/printers/");
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+             NULL, cupsUser());
+
+ +

Once you have created an IPP request, use the cups functions to send the request to and read the response from the server. For example, the cupsDoRequest function can be used for simple query operations that do not involve files:

+ +
+#include <cups/cups.h>
+
+
+ipp_t *get_jobs(void)
+{
+  ipp_t *request = ippNewRequest(IPP_GET_JOBS);
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, "ipp://localhost/printers/");
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+               NULL, cupsUser());
+
+  return (cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/"));
+}
+
+ +

The cupsDoRequest function frees the request and returns an IPP response or NULL pointer if the request could not be sent to the server. Once you have a response from the server, you can either use the ippFindAttribute and ippFindNextAttribute functions to find specific attributes, for example:

+ +
+ipp_t *response;
+ipp_attribute_t *attr;
+
+attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM);
+
+ +

You can also walk the list of attributes with a simple for loop like this:

+ +
+ipp_t *response;
+ipp_attribute_t *attr;
+
+for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response))
+  if (ippGetName(attr) == NULL)
+    puts("--SEPARATOR--");
+  else
+    puts(ippGetName(attr));
+
+ +

The for loop approach is normally used when collecting attributes for multiple objects (jobs, printers, etc.) in a response. Attributes with NULL names indicate a separator between the attributes of each object. For example, the following code will list the jobs returned from our previous get_jobs example code:

+ +
+ipp_t *response = get_jobs();
+
+if (response != NULL)
+{
+  ipp_attribute_t *attr;
+  const char *attrname;
+  int job_id = 0;
+  const char *job_name = NULL;
+  const char *job_originating_user_name = NULL;
+
+  puts("Job ID  Owner             Title");
+  puts("------  ----------------  ---------------------------------");
+
+  for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response))
+  {
+   /* Attributes without names are separators between jobs */
+    attrname = ippGetName(attr);
+    if (attrname == NULL)
+    {
+      if (job_id > 0)
+      {
+        if (job_name == NULL)
+          job_name = "(withheld)";
+
+        if (job_originating_user_name == NULL)
+          job_originating_user_name = "(withheld)";
+
+        printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
+      }
+
+      job_id = 0;
+      job_name = NULL;
+      job_originating_user_name = NULL;
+      continue;
+    }
+    else if (!strcmp(attrname, "job-id") && ippGetValueTag(attr) == IPP_TAG_INTEGER)
+      job_id = ippGetInteger(attr, 0);
+    else if (!strcmp(attrname, "job-name") && ippGetValueTag(attr) == IPP_TAG_NAME)
+      job_name = ippGetString(attr, 0, NULL);
+    else if (!strcmp(attrname, "job-originating-user-name") &&
+             ippGetValueTag(attr) == IPP_TAG_NAME)
+      job_originating_user_name = ippGetString(attr, 0, NULL);
+  }
+
+  if (job_id > 0)
+  {
+    if (job_name == NULL)
+      job_name = "(withheld)";
+
+    if (job_originating_user_name == NULL)
+      job_originating_user_name = "(withheld)";
+
+    printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
+  }
+}
+
+ +

Creating URI Strings

+ +

To ensure proper encoding, the +httpAssembleURIf function must be +used to format a "printer-uri" string for all printer-based requests:

+ +
+const char *name = "Foo";
+char uri[1024];
+ipp_t *request;
+
+httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
+                 ippPort(), "/printers/%s", name);
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ +

Sending Requests with Files

+ +

The cupsDoFileRequest and +cupsDoIORequest functions are +used for requests involving files. The +cupsDoFileRequest function +attaches the named file to a request and is typically used when sending a print +file or changing a printer's PPD file:

+ +
+const char *filename = "/usr/share/cups/data/testprint.ps";
+const char *name = "Foo";
+char uri[1024];
+char resource[1024];
+ipp_t *request = ippNewRequest(IPP_PRINT_JOB);
+ipp_t *response;
+
+/* Use httpAssembleURIf for the printer-uri string */
+httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
+                 ippPort(), "/printers/%s", name);
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+             NULL, cupsUser());
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
+             NULL, "testprint.ps");
+
+/* Use snprintf for the resource path */
+snprintf(resource, sizeof(resource), "/printers/%s", name);
+
+response = cupsDoFileRequest(CUPS_HTTP_DEFAULT, request, resource, filename);
+
+ +

The cupsDoIORequest function +optionally attaches a file to the request and optionally saves a file in the +response from the server. It is used when using a pipe for the request +attachment or when using a request that returns a file, currently only +CUPS_GET_DOCUMENT and CUPS_GET_PPD. For example, +the following code will download the PPD file for the sample HP LaserJet +printer driver:

+ +
+char tempfile[1024];
+int tempfd;
+ipp_t *request = ippNewRequest(CUPS_GET_PPD);
+ipp_t *response;
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
+             NULL, "laserjet.ppd");
+
+tempfd = cupsTempFd(tempfile, sizeof(tempfile));
+
+response = cupsDoIORequest(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
+
+ +

The example passes -1 for the input file descriptor to specify +that no file is to be attached to the request. The PPD file attached to the +response is written to the temporary file descriptor we created using the +cupsTempFd function.

+ +

Asynchronous Request Processing

+ +

The cupsSendRequest and +cupsGetResponse support +asynchronous communications with the server. Unlike the other request +functions, the IPP request is not automatically freed, so remember to +free your request with the ippDelete +function.

+ +

File data is attached to the request using the +cupsWriteRequestData +function, while file data returned from the server is read using the +cupsReadResponseData +function. We can rewrite the previous CUPS_GET_PPD example +to use the asynchronous functions quite easily:

+ +
+char tempfile[1024];
+int tempfd;
+ipp_t *request = ippNewRequest(CUPS_GET_PPD);
+ipp_t *response;
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
+             NULL, "laserjet.ppd");
+
+tempfd = cupsTempFd(tempfile, sizeof(tempfile));
+
+if (cupsSendRequest(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
+{
+  response = cupsGetResponse(CUPS_HTTP_DEFAULT, "/");
+
+  if (response != NULL)
+  {
+    ssize_t bytes;
+    char buffer[8192];
+
+    while ((bytes = cupsReadResponseData(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
+      write(tempfd, buffer, bytes);
+  }
+}
+
+/* Free the request! */
+ippDelete(request);
+
+ +

The cupsSendRequest function +returns the initial HTTP request status, typically either +HTTP_CONTINUE or HTTP_UNAUTHORIZED. The latter status +is returned when the request requires authentication of some sort. The +cupsDoAuthentication function +must be called when your see HTTP_UNAUTHORIZED and the request +re-sent. We can add authentication support to our example code by using a +do ... while loop:

+ +
+char tempfile[1024];
+int tempfd;
+ipp_t *request = ippNewRequest(CUPS_GET_PPD);
+ipp_t *response;
+http_status_t status;
+
+ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
+             NULL, "laserjet.ppd");
+
+tempfd = cupsTempFd(tempfile, sizeof(tempfile));
+
+/* Loop for authentication */
+do
+{
+  status = cupsSendRequest(CUPS_HTTP_DEFAULT, request, "/");
+
+  if (status == HTTP_UNAUTHORIZED)
+  {
+    /* Try to authenticate, break out of the loop if that fails */
+    if (cupsDoAuthentication(CUPS_HTTP_DEFAULT, "POST", "/"))
+      break;
+  }
+}
+while (status != HTTP_CONTINUE && status != HTTP_UNAUTHORIZED);
+
+if (status == HTTP_CONTINUE)
+{
+  response = cupsGetResponse(CUPS_HTTP_DEFAULT, "/");
+
+  if (response != NULL)
+  {
+    ssize_t bytes;
+    char buffer[8192];
+
+    while ((bytes = cupsReadResponseData(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
+      write(tempfd, buffer, bytes);
+  }
+}
+
+/* Free the request! */
+ippDelete(request);
+
+ + +

Overview

+ +

The CUPS file and directory APIs provide portable interfaces +for manipulating files and listing files and directories. Unlike +stdio FILE streams, the cupsFile functions +allow you to open more than 256 files at any given time. They +also manage the platform-specific details of locking, large file +support, line endings (CR, LF, or CR LF), and reading and writing +files using Flate ("gzip") compression. Finally, you can also +connect, read from, and write to network connections using the +cupsFile functions.

+ +

The cupsDir functions manage the platform-specific +details of directory access/listing and provide a convenient way +to get both a list of files and the information (permissions, +size, timestamp, etc.) for each of those files.

+ + +

Overview

+ +

The CUPS array API provides a high-performance generic array container. +The contents of the array container can be sorted and the container itself is +designed for optimal speed and memory usage under a wide variety of conditions. +Sorted arrays use a binary search algorithm from the last found or inserted +element to quickly find matching elements in the array. Arrays created with the +optional hash function can often find elements with a single lookup. The +cups_array_t type is used when +referring to a CUPS array.

+ +

The CUPS scheduler (cupsd) and many of the CUPS API +functions use the array API to efficiently manage large lists of +data.

+ +

Managing Arrays

+ +

Arrays are created using either the +cupsArrayNew, +cupsArrayNew2, or +cupsArrayNew3 functions. The +first function creates a new array with the specified callback function +and user data pointer:

+ +
+#include <cups/array.h>
+
+static int compare_func(void *first, void *second, void *user_data);
+
+void *user_data;
+cups_array_t *array = cupsArrayNew(compare_func, user_data);
+
+ +

The comparison function (type +cups_arrayfunc_t) is called +whenever an element is added to the array and can be NULL to +create an unsorted array. The function returns -1 if the first element should +come before the second, 0 if the first and second elements should have the same +ordering, and 1 if the first element should come after the second.

+ +

The "user_data" pointer is passed to your comparison function. Pass +NULL if you do not need to associate the elements in your array +with additional information.

+ +

The cupsArrayNew2 function adds +two more arguments to support hashed lookups, which can potentially provide +instantaneous ("O(1)") lookups in your array:

+ +
+#include <cups/array.h>
+
+#define HASH_SIZE 512 /* Size of hash table */
+
+static int compare_func(void *first, void *second, void *user_data);
+static int hash_func(void *element, void *user_data);
+
+void *user_data;
+cups_array_t *hash_array = cupsArrayNew2(compare_func, user_data, hash_func, HASH_SIZE);
+
+ +

The hash function (type +cups_ahash_func_t) should return a +number from 0 to (hash_size-1) that (hopefully) uniquely identifies the +element and is called whenever you look up an element in the array with +cupsArrayFind. The hash size is +only limited by available memory, but generally should not be larger than +16384 to realize any performance improvement.

+ +

The cupsArrayNew3 function adds +copy and free callbacks to support basic memory management of elements:

+ +
+#include <cups/array.h>
+
+#define HASH_SIZE 512 /* Size of hash table */
+
+static int compare_func(void *first, void *second, void *user_data);
+static void *copy_func(void *element, void *user_data);
+static void free_func(void *element, void *user_data);
+static int hash_func(void *element, void *user_data);
+
+void *user_data;
+cups_array_t *array = cupsArrayNew3(compare_func, user_data, NULL, 0, copy_func, free_func);
+
+cups_array_t *hash_array = cupsArrayNew3(compare_func, user_data, hash_func, HASH_SIZE, copy_func, free_func);
+
+ +

Once you have created the array, you add elements using the +cupsArrayAdd +cupsArrayInsert functions. +The first function adds an element to the array, adding the new element +after any elements that have the same order, while the second inserts the +element before others with the same order. For unsorted arrays, +cupsArrayAdd appends the element to +the end of the array while +cupsArrayInsert inserts the +element at the beginning of the array. For example, the following code +creates a sorted array of character strings:

+ +
+#include <cups/array.h>
+
+/* Use strcmp() to compare strings - it will ignore the user_data pointer */
+cups_array_t *array = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+/* Add four strings to the array */
+cupsArrayAdd(array, "One Fish");
+cupsArrayAdd(array, "Two Fish");
+cupsArrayAdd(array, "Red Fish");
+cupsArrayAdd(array, "Blue Fish");
+
+ +

Elements are removed using the +cupsArrayRemove function, for +example:

+ +
+#include <cups/array.h>
+
+/* Use strcmp() to compare strings - it will ignore the user_data pointer */
+cups_array_t *array = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+/* Add four strings to the array */
+cupsArrayAdd(array, "One Fish");
+cupsArrayAdd(array, "Two Fish");
+cupsArrayAdd(array, "Red Fish");
+cupsArrayAdd(array, "Blue Fish");
+
+/* Remove "Red Fish" */
+cupsArrayRemove(array, "Red Fish");
+
+ +

Finally, you free the memory used by the array using the +cupsArrayDelete function. All +of the memory for the array and hash table (if any) is freed, however CUPS +does not free the elements unless you provide copy and free functions.

+ +

Finding and Enumerating Elements

+ +

CUPS provides several functions to find and enumerate elements in an +array. Each one sets or updates a "current index" into the array, such that +future lookups will start where the last one left off:

+ +
+
cupsArrayFind
+
Returns the first matching element.
+
cupsArrayFirst
+
Returns the first element in the array.
+
cupsArrayIndex
+
Returns the Nth element in the array, starting at 0.
+
cupsArrayLast
+
Returns the last element in the array.
+
cupsArrayNext
+
Returns the next element in the array.
+
cupsArrayPrev
+
Returns the previous element in the array.
+
+ +

Each of these functions returns NULL when there is no +corresponding element. For example, a simple for loop using the +cupsArrayFirst and +cupsArrayNext functions will +enumerate all of the strings in our previous example:

+ +
+#include <cups/array.h>
+
+/* Use strcmp() to compare strings - it will ignore the user_data pointer */
+cups_array_t *array = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+/* Add four strings to the array */
+cupsArrayAdd(array, "One Fish");
+cupsArrayAdd(array, "Two Fish");
+cupsArrayAdd(array, "Red Fish");
+cupsArrayAdd(array, "Blue Fish");
+
+/* Show all of the strings in the array */
+char *s;
+for (s = (char *)cupsArrayFirst(array); s != NULL; s = (char *)cupsArrayNext(array))
+  puts(s);
+
diff --git a/doc/help/api-admin.html b/doc/help/api-admin.html index 5ca4096147..2af3c86b0c 100644 --- a/doc/help/api-admin.html +++ b/doc/help/api-admin.html @@ -1,12 +1,14 @@ - + - - Administration APIs - - - - - - -
+ + - - Array API - - - - - - -
+ + - - CUPS API - - - - - - -
+ + - - File and Directory APIs - - - - - - -
+ + - - Filter and Backend Programming - - - - - - -
+ + - - HTTP and IPP APIs - - - - - - -
+ + - - Introduction to CUPS Programming - - - - - - -
+ + - - PPD API (DEPRECATED) - - - - - - -
+ + - - Raster API - - - - - - -
+ + - - Developing PostScript Printer Drivers - - - - - - -
+ + - - Introduction to the PPD Compiler - - - - - - -
+ + - - Developing Raster Printer Drivers - - - - - - -
+ + - - CUPS PPD Extensions - - - - - - -
+ +