]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/cupspm.md
Save work on new CUPS Programming Manual.
[thirdparty/cups.git] / cups / cupspm.md
CommitLineData
abacc52b
MS
1---
2title: CUPS Programming Manual
3author: Michael R Sweet
4copyright: Copyright (c) 2007-2017 by Apple Inc. All Rights Reserved.
5version: 2.2.4
6...
7
8# Introduction
9
10CUPS provides the "cups" library to talk to the different parts of CUPS and with
11Internet Printing Protocol (IPP) printers. The "cups" library functions are
12accessed by including the `<cups/cups.h>` header.
13
14CUPS is based on the Internet Printing Protocol ("IPP"), which allows clients
15(applications) to communicate with a server (the scheduler, printers, etc.) to
16get a list of destinations, send print jobs, and so forth. You identify which
17server you want to communicate with using a pointer to the opaque structure
18`http_t`. The `CUPS_HTTP_DEFAULT` constant can be used when you want to talk to
19the CUPS scheduler.
20
21
22## Guidelines
23
24When writing software that uses the "cups" library:
25
26- Do not use undocumented or deprecated APIs,
27- Do not rely on pre-configured printers,
28- Do not assume that printers support specific features or formats, and
29- Do not rely on implementation details (PPDs, etc.)
30
31CUPS is designed to insulate users and developers from the implementation
32details of printers and file formats. The goal is to allow an application to
33supply a print file in a standard format with the user intent ("print four
34copies, two-sided on A4 media, and staple each copy") and have the printing
35system manage the printer communication and format conversion needed.
36
37Similarly, printer and job management applications can use standard query
38operations to obtain the status information in a common, generic form and use
39standard management operations to control the state of those printers and jobs.
40
41
42## Terms Used in This Document
43
44A *Destination* is a printer or print queue that accepts print jobs. A
45*Print Job* is one or more documents that are processed by a destination
46using options supplied when creating the job. A *Document* is a file (JPEG
47image, PDF file, etc.) suitable for printing. An *Option* controls some aspect
48of printing, such as the media used. *Media* is the sheets or roll that is
49printed on. An *Attribute* is an option encoded for an Internet Printing
50Protocol (IPP) request.
51
52
53## Compiling Programs That Use the CUPS API
54
55The CUPS libraries can be used from any C, C++, or Objective C program.
56The method of compiling against the libraries varies depending on the
57operating system and installation of CUPS. The following sections show how
58to compile a simple program (shown below) in two common environments.
59
60The following simple program lists the available destinations:
61
62 #include <stdio.h>
63 #include <cups/cups.h>
64
65 int print_dest(void *user_data, unsigned flags, cups_dest_t *dest)
66 {
67 if (dest->instance)
68 printf("%s/%s\n", dest->name, dest->instance);
69 else
70 puts(dest->name);
71
72 return (1);
73 }
74
75 int main(void)
76 {
77 cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, print_dest, NULL);
78
79 return (0);
80 }
81
82
83### Compiling with Xcode
84
85In Xcode, choose *New Project...* from the *File* menu (or press SHIFT+CMD+N),
86then select the *Command Line Tool* under the macOS Application project type.
87Click *Next* and enter a name for the project, for example "firstcups". Click
88*Next* and choose a project directory. The click *Next* to create the project.
89
90In the project window, click on the *Build Phases* group and expand the
91*Link Binary with Libraries* section. Click *+*, type "libcups" to show the
92library, and then double-click on `libcups.tbd`.
93
94Finally, click on the `main.c` file in the sidebar and copy the example program
95to the file. Build and run (CMD+R) to see the list of destinations.
96
97
98### Compiling with GCC
99
100From the command-line, create a file called `sample.c` using your favorite
101editor, copy the example to this file, and save. Then run the following command
102to compile it with GCC and run it:
103
104 gcc -o simple `cups-config --cflags` simple.c `cups-config --libs`
105 ./simple
106
107The `cups-config` command provides the compiler flags (`cups-config --cflags`)
108and libraries (`cups-config --libs`) needed for the local system.
109
110
111# Working with Destinations
112
113
114## Finding Available Destinations
115
116
117## Getting Information About a Destination
118
119
120### Getting Supported Options and Values
121
122cupsCheckDestSupported and cups
123
124### Getting Default Options and Values
125
126### Getting Ready Options and Values
127
128### Media Options
129
130### Other Standard Options
131
132### Localizing Options and Values
133
134## Submitting a Print Job
135
136## Canceling a Print Job
137
138
139# IPP Requests and Responses
140
141Why you'd want to do this, etc.
142
143
144## Connecting to a Destination
145
146## Sending an IPP Request
147
148## Getting the IPP Response
149
150## Handling Authentication
151
152## Handling Certificate Validation
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172<h3><a name='PRINTERS_AND_CLASSES'>Printers and Classes</a></h3>
173
174<p>Printers and classes (collections of printers) are accessed through
175the <a href="#cups_dest_t"><code>cups_dest_t</code></a> structure which
176includes the name (<code>name</code>), instance (<code>instance</code> -
177a way of selecting certain saved options/settings), and the options and
178attributes associated with that destination (<code>num_options</code> and
179<code>options</code>). Destinations are created using the
180<a href="#cupsGetDests"><code>cupsGetDests</code></a> function and freed
181using the <a href='#cupsFreeDests'><code>cupsFreeDests</code></a> function.
182The <a href='#cupsGetDest'><code>cupsGetDest</code></a> function finds a
183specific destination for printing:</p>
184
185<pre class='example'>
186#include &lt;cups/cups.h&gt;
187
188<a href='#cups_dest_t'>cups_dest_t</a> *dests;
189int num_dests = <a href='#cupsGetDests'>cupsGetDests</a>(&amp;dests);
190<a href='#cups_dest_t'>cups_dest_t</a> *dest = <a href='#cupsGetDest'>cupsGetDest</a>("name", NULL, num_dests, dests);
191
192/* do something with dest */
193
194<a href='#cupsFreeDests'>cupsFreeDests</a>(num_dests, dests);
195</pre>
196
197<p>Passing <code>NULL</code> to
198<a href='#cupsGetDest'><code>cupsGetDest</code></a> for the destination name
199will return the default destination. Similarly, passing a <code>NULL</code>
200instance will return the default instance for that destination.</p>
201
202<div class='table'><table summary='Table 1: Printer Attributes' width='80%'>
203<caption>Table 1: <a name='TABLE1'>Printer Attributes</a></caption>
204<thead>
205<tr>
206 <th>Attribute Name</th>
207 <th>Description</th>
208</tr>
209</thead>
210<tbody>
211<tr>
212 <td>"auth-info-required"</td>
213 <td>The type of authentication required for printing to this
214 destination: "none", "username,password", "domain,username,password",
215 or "negotiate" (Kerberos)</td>
216</tr>
217<tr>
218 <td>"printer-info"</td>
219 <td>The human-readable description of the destination such as "My
220 Laser Printer".</td>
221</tr>
222<tr>
223 <td>"printer-is-accepting-jobs"</td>
224 <td>"true" if the destination is accepting new jobs, "false" if
225 not.</td>
226</tr>
227<tr>
228 <td>"printer-is-shared"</td>
229 <td>"true" if the destination is being shared with other computers,
230 "false" if not.</td>
231</tr>
232<tr>
233 <td>"printer-location"</td>
234 <td>The human-readable location of the destination such as "Lab 4".</td>
235</tr>
236<tr>
237 <td>"printer-make-and-model"</td>
238 <td>The human-readable make and model of the destination such as "HP
239 LaserJet 4000 Series".</td>
240</tr>
241<tr>
242 <td>"printer-state"</td>
243 <td>"3" if the destination is idle, "4" if the destination is printing
244 a job, and "5" if the destination is stopped.</td>
245</tr>
246<tr>
247 <td>"printer-state-change-time"</td>
248 <td>The UNIX time when the destination entered the current state.</td>
249</tr>
250<tr>
251 <td>"printer-state-reasons"</td>
252 <td>Additional comma-delimited state keywords for the destination
253 such as "media-tray-empty-error" and "toner-low-warning".</td>
254</tr>
255<tr>
256 <td>"printer-type"</td>
257 <td>The <a href='#cups_printer_t'><code>cups_printer_t</code></a>
258 value associated with the destination.</td>
259</tr>
260</tbody>
261</table></div>
262
263<h3><a name='OPTIONS'>Options</a></h3>
264
265<p>Options are stored in arrays of
266<a href='#cups_option_t'><code>cups_option_t</code></a> structures. Each
267option has a name (<code>name</code>) and value (<code>value</code>)
268associated with it. The <a href='#cups_dest_t'><code>cups_dest_t</code></a>
269<code>num_options</code> and <code>options</code> members contain the
270default options for a particular destination, along with several informational
271attributes about the destination as shown in <a href='#TABLE1'>Table 1</a>.
272The <a href='#cupsGetOption'><code>cupsGetOption</code></a> function gets
273the value for the named option. For example, the following code lists the
274available destinations and their human-readable descriptions:</p>
275
276<pre class='example'>
277#include &lt;cups/cups.h&gt;
278
279<a href='#cups_dest_t'>cups_dest_t</a> *dests;
280int num_dests = <a href='#cupsGetDests'>cupsGetDests</a>(&amp;dests);
281<a href='#cups_dest_t'>cups_dest_t</a> *dest;
282int i;
283const char *value;
284
285for (i = num_dests, dest = dests; i > 0; i --, dest ++)
286 if (dest->instance == NULL)
287 {
288 value = <a href='#cupsGetOption'>cupsGetOption</a>("printer-info", dest->num_options, dest->options);
289 printf("%s (%s)\n", dest->name, value ? value : "no description");
290 }
291
292<a href='#cupsFreeDests'>cupsFreeDests</a>(num_dests, dests);
293</pre>
294
295<p>You can create your own option arrays using the
296<a href='#cupsAddOption'><code>cupsAddOption</code></a> function, which
297adds a single named option to an array:</p>
298
299<pre class='example'>
300#include &lt;cups/cups.h&gt;
301
302int num_options = 0;
303<a href='#cups_option_t'>cups_option_t</a> *options = NULL;
304
305/* The returned num_options value is updated as needed */
306num_options = <a href='#cupsAddOption'>cupsAddOption</a>("first", "value", num_options, &amp;options);
307
308/* This adds a second option value */
309num_options = <a href='#cupsAddOption'>cupsAddOption</a>("second", "value", num_options, &amp;options);
310
311/* This replaces the first option we added */
312num_options = <a href='#cupsAddOption'>cupsAddOption</a>("first", "new value", num_options, &amp;options);
313</pre>
314
315<p>Use a <code>for</code> loop to copy the options from a destination:</p>
316
317<pre class='example'>
318#include &lt;cups/cups.h&gt;
319
320int i;
321int num_options = 0;
322<a href='#cups_option_t'>cups_option_t</a> *options = NULL;
323<a href='#cups_dest_t'>cups_dest_t</a> *dest;
324
325for (i = 0; i &lt; dest->num_options; i ++)
326 num_options = <a href='#cupsAddOption'>cupsAddOption</a>(dest->options[i].name, dest->options[i].value,
327 num_options, &amp;options);
328</pre>
329
330<p>Use the <a href='#cupsFreeOptions'><code>cupsFreeOptions</code></a>
331function to free the options array when you are done using it:</p>
332
333<pre class='example'>
334<a href='#cupsFreeOptions'>cupsFreeOptions</a>(num_options, options);
335</pre>
336
337<h3><a name='PRINT_JOBS'>Print Jobs</a></h3>
338
339<p>Print jobs are identified by a locally-unique job ID number from 1 to
3402<sup>31</sup>-1 and have options and one or more files for printing to a
341single destination. The <a href='#cupsPrintFile'><code>cupsPrintFile</code></a>
342function creates a new job with one file. The following code prints the CUPS
343test page file:</p>
344
345<pre class='example'>
346#include &lt;cups/cups.h&gt;
347
348<a href='#cups_dest_t'>cups_dest_t</a> *dest;
349int num_options;
350<a href='#cups_option_t'>cups_option_t</a> *options;
351int job_id;
352
353/* Print a single file */
354job_id = <a href='#cupsPrintFile'>cupsPrintFile</a>(dest->name, "/usr/share/cups/data/testprint.ps",
355 "Test Print", num_options, options);
356</pre>
357
358<p>The <a href='#cupsPrintFiles'><code>cupsPrintFiles</code></a> function
359creates a job with multiple files. The files are provided in a
360<code>char *</code> array:</p>
361
362<pre class='example'>
363#include &lt;cups/cups.h&gt;
364
365<a href='#cups_dest_t'>cups_dest_t</a> *dest;
366int num_options;
367<a href='#cups_option_t'>cups_option_t</a> *options;
368int job_id;
369char *files[3] = { "file1.pdf", "file2.pdf", "file3.pdf" };
370
371/* Print three files */
372job_id = <a href='#cupsPrintFiles'>cupsPrintFiles</a>(dest->name, 3, files, "Test Print", num_options, options);
373</pre>
374
375<p>Finally, the <a href='#cupsCreateJob'><code>cupsCreateJob</code></a>
376function creates a new job with no files in it. Files are added using the
377<a href='#cupsStartDocument'><code>cupsStartDocument</code></a>,
378<a href='api-httpipp.html#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>,
379and <a href='#cupsFinishDocument'><code>cupsFinishDocument</code></a> functions.
380The following example creates a job with 10 text files for printing:</p>
381
382<pre class='example'>
383#include &lt;cups/cups.h&gt;
384
385<a href='#cups_dest_t'>cups_dest_t</a> *dest;
386int num_options;
387<a href='#cups_option_t'>cups_option_t</a> *options;
388int job_id;
389int i;
390char buffer[1024];
391
392/* Create the job */
393job_id = <a href='#cupsCreateJob'>cupsCreateJob</a>(CUPS_HTTP_DEFAULT, dest->name, "10 Text Files",
394 num_options, options);
395
396/* If the job is created, add 10 files */
397if (job_id > 0)
398{
399 for (i = 1; i &lt;= 10; i ++)
400 {
401 snprintf(buffer, sizeof(buffer), "file%d.txt", i);
402
403 <a href='#cupsStartDocument'>cupsStartDocument</a>(CUPS_HTTP_DEFAULT, dest->name, job_id, buffer,
404 CUPS_FORMAT_TEXT, i == 10);
405
406 snprintf(buffer, sizeof(buffer),
407 "File %d\n"
408 "\n"
409 "One fish,\n"
410 "Two fish,\n
411 "Red fish,\n
412 "Blue fish\n", i);
413
414 /* cupsWriteRequestData can be called as many times as needed */
415 <a href='#cupsWriteRequestData'>cupsWriteRequestData</a>(CUPS_HTTP_DEFAULT, buffer, strlen(buffer));
416
417 <a href='#cupsFinishDocument'>cupsFinishDocument</a>(CUPS_HTTP_DEFAULT, dest->name);
418 }
419}
420</pre>
421
422<p>Once you have created a job, you can monitor its status using the
423<a href='#cupsGetJobs'><code>cupsGetJobs</code></a> function, which returns
424an array of <a href='#cups_job_t'><code>cups_job_t</code></a> structures.
425Each contains the job ID (<code>id</code>), destination name
426(<code>dest</code>), title (<code>title</code>), and other information
427associated with the job. The job array is freed using the
428<a href='#cupsFreeJobs'><code>cupsFreeJobs</code></a> function. The following
429example monitors a specific job ID, showing the current job state once every
4305 seconds until the job is completed:</p>
431
432<pre class='example'>
433#include &lt;cups/cups.h&gt;
434
435<a href='#cups_dest_t'>cups_dest_t</a> *dest;
436int job_id;
437int num_jobs;
438<a href='#cups_job_t'>cups_job_t</a> *jobs;
439int i;
440ipp_jstate_t job_state = IPP_JOB_PENDING;
441
442while (job_state &lt; IPP_JOB_STOPPED)
443{
444 /* Get my jobs (1) with any state (-1) */
445 num_jobs = <a href='#cupsGetJobs'>cupsGetJobs</a>(&amp;jobs, dest->name, 1, -1);
446
447 /* Loop to find my job */
448 job_state = IPP_JOB_COMPLETED;
449
450 for (i = 0; i &lt; num_jobs; i ++)
451 if (jobs[i].id == job_id)
452 {
453 job_state = jobs[i].state;
454 break;
455 }
456
457 /* Free the job array */
458 <a href='#cupsFreeJobs'>cupsFreeJobs</a>(num_jobs, jobs);
459
460 /* Show the current state */
461 switch (job_state)
462 {
463 case IPP_JOB_PENDING :
464 printf("Job %d is pending.\n", job_id);
465 break;
466 case IPP_JOB_HELD :
467 printf("Job %d is held.\n", job_id);
468 break;
469 case IPP_JOB_PROCESSING :
470 printf("Job %d is processing.\n", job_id);
471 break;
472 case IPP_JOB_STOPPED :
473 printf("Job %d is stopped.\n", job_id);
474 break;
475 case IPP_JOB_CANCELED :
476 printf("Job %d is canceled.\n", job_id);
477 break;
478 case IPP_JOB_ABORTED :
479 printf("Job %d is aborted.\n", job_id);
480 break;
481 case IPP_JOB_COMPLETED :
482 printf("Job %d is completed.\n", job_id);
483 break;
484 }
485
486 /* Sleep if the job is not finished */
487 if (job_state &lt; IPP_JOB_STOPPED)
488 sleep(5);
489}
490</pre>
491
492<p>To cancel a job, use the
493<a href='#cupsCancelJob'><code>cupsCancelJob</code></a> function with the
494job ID:</p>
495
496<pre class='example'>
497#include &lt;cups/cups.h&gt;
498
499<a href='#cups_dest_t'>cups_dest_t</a> *dest;
500int job_id;
501
502<a href='#cupsCancelJob'>cupsCancelJob</a>(dest->name, job_id);
503</pre>
504
505<h3><a name='ERROR_HANDLING'>Error Handling</a></h3>
506
507<p>If any of the CUPS API printing functions returns an error, the reason for
508that error can be found by calling the
509<a href='#cupsLastError'><code>cupsLastError</code></a> and
510<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> functions.
511<a href='#cupsLastError'><code>cupsLastError</code></a> returns the last IPP
512error code
513(<a href='api-httpipp.html#ipp_status_t'><code>ipp_status_t</code></a>)
514that was encountered, while
515<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> returns
516a (localized) human-readable string that can be shown to the user. For example,
517if any of the job creation functions returns a job ID of 0, you can use
518<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> to show
519the reason why the job could not be created:</p>
520
521<pre class='example'>
522#include &lt;cups/cups.h&gt;
523
524int job_id;
525
526if (job_id == 0)
527 puts(cupsLastErrorString());
528</pre>
529
530<h3><a name='PASSWORDS_AND_AUTHENTICATION'>Passwords and Authentication</a></h3>
531
532<p>CUPS supports authentication of any request, including submission of print
533jobs. The default mechanism for getting the username and password is to use the
534login user and a password from the console.</p>
535
536<p>To support other types of applications, in particular Graphical User
537Interfaces ("GUIs"), the CUPS API provides functions to set the default
538username and to register a callback function that returns a password string.</p>
539
540<p>The <a href="#cupsSetPasswordCB"><code>cupsSetPasswordCB</code></a>
541function is used to set a password callback in your program. Only one
542function can be used at any time.</p>
543
544<p>The <a href="#cupsSetUser"><code>cupsSetUser</code></a> function sets the
545current username for authentication. This function can be called by your
546password callback function to change the current username as needed.</p>
547
548<p>The following example shows a simple password callback that gets a
549username and password from the user:</p>
550
551<pre class='example'>
552#include &lt;cups/cups.h&gt;
553
554const char *
555my_password_cb(const char *prompt)
556{
557 char user[65];
558
559
560 puts(prompt);
561
562 /* Get a username from the user */
563 printf("Username: ");
564 if (fgets(user, sizeof(user), stdin) == NULL)
565 return (NULL);
566
567 /* Strip the newline from the string and set the user */
568 user[strlen(user) - 1] = '\0';
569
570 <a href='#cupsSetUser'>cupsSetUser</a>(user);
571
572 /* Use getpass() to ask for the password... */
573 return (getpass("Password: "));
574}
575
576<a href='#cupsSetPasswordCB'>cupsSetPasswordCB</a>(my_password_cb);
577</pre>
578
579<p>Similarly, a GUI could display the prompt string in a window with input
580fields for the username and password. The username should default to the
581string returned by the <a href="#cupsUser"><code>cupsUser</code></a>
582function.</p>
583<!--
584 HTTP and IPP API introduction for CUPS.
585
586 Copyright 2007-2012 by Apple Inc.
587 Copyright 1997-2006 by Easy Software Products, all rights reserved.
588
589 These coded instructions, statements, and computer programs are the
590 property of Apple Inc. and are protected by Federal copyright
591 law. Distribution and use rights are outlined in the file "LICENSE.txt"
592 which should have been included with this file. If this file is
593 file is missing or damaged, see the license at "http://www.cups.org/".
594-->
595
596<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
597
598<p>The CUPS HTTP and IPP APIs provide low-level access to the HTTP and IPP
599protocols and CUPS scheduler. They are typically used by monitoring and
600administration programs to perform specific functions not supported by the
601high-level CUPS API functions.</p>
602
603<p>The HTTP APIs use an opaque structure called
604<a href='#http_t'><code>http_t</code></a> to manage connections to
605a particular HTTP or IPP server. The
606<a href='#httpConnectEncrypt'><code>httpConnectEncrypt</code></a> function is
607used to create an instance of this structure for a particular server.
608The constant <code>CUPS_HTTP_DEFAULT</code> can be used with all of the
609<code>cups</code> functions to refer to the default CUPS server - the functions
610create a per-thread <a href='#http_t'><code>http_t</code></a> as needed.</p>
611
612<p>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 <a href='#ipp_t'><code>ipp_t</code></a> type holds a complete request or response and is allocated using the <a href='#ippNew'><code>ippNew</code></a> or <a href='#ippNewRequest'><code>ippNewRequest</code></a> functions and freed using the <a href='#ippDelete'><code>ippDelete</code></a> function.</p>
613
614<p>The second opaque structure is called <a href='#ipp_attribute_t'><code>ipp_attribute_t</code></a> and holds a single IPP attribute which consists of a group tag (<a href='#ippGetGroupTag'><code>ippGetGroupTag</code></a>), a value type tag (<a href='#ippGetValueTag'><code>ippGetValueTag</code></a>), the attribute name (<a href='#ippGetName'><code>ippGetName</code></a>), and 1 or more values (<a href='#ippGetCount'><code>ippGetCount</code></a>, <a href='#ippGetBoolean'><code>ippGetBoolean</code></a>, <a href='#ippGetCollection'><code>ippGetCollection</code></a>, <a href='#ippGetDate'><code>ippGetDate</code></a>, <a href='#ippGetInteger'><code>ippGetInteger</code></a>, <a href='#ippGetRange'><code>ippGetRange</code></a>, <a href='#ippGetResolution'><code>ippGetResolution</code></a>, and <a href='#ippGetString'><code>ippGetString</code></a>). Attributes are added to an <a href='#ipp_t'><code>ipp_t</code></a> pointer using one of the <code>ippAdd</code> functions. For example, use <a href='#ippAddString'><code>ippAddString</code></a> to add the "printer-uri" and "requesting-user-name" string attributes to a request:</p>
615
616<pre class='example'>
617<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
618
619<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
620 NULL, "ipp://localhost/printers/");
621<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
622 NULL, cupsUser());
623</pre>
624
625<p>Once you have created an IPP request, use the <code>cups</code> functions to send the request to and read the response from the server. For example, the <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function can be used for simple query operations that do not involve files:</p>
626
627<pre class='example'>
628#include &lt;cups/cups.h&gt;
629
630
631<a href='#ipp_t'>ipp_t</a> *<a name='get_jobs'>get_jobs</a>(void)
632{
633 <a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
634
635 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
636 NULL, "ipp://localhost/printers/");
637 <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
638 NULL, cupsUser());
639
640 return (<a href='#cupsDoRequest'>cupsDoRequest</a>(CUPS_HTTP_DEFAULT, request, "/"));
641}
642</pre>
643
644<p>The <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function frees the request and returns an IPP response or <code>NULL</code> pointer if the request could not be sent to the server. Once you have a response from the server, you can either use the <a href='#ippFindAttribute'><code>ippFindAttribute</code></a> and <a href='#ippFindNextAttribute'><code>ippFindNextAttribute</code></a> functions to find specific attributes, for example:</p>
645
646<pre class='example'>
647<a href='#ipp_t'>ipp_t</a> *response;
648<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
649
650attr = <a href='#ippFindAttribute'>ippFindAttribute</a>(response, "printer-state", IPP_TAG_ENUM);
651</pre>
652
653<p>You can also walk the list of attributes with a simple <code>for</code> loop like this:</p>
654
655<pre class='example'>
656<a href='#ipp_t'>ipp_t</a> *response;
657<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
658
659for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
660 if (ippGetName(attr) == NULL)
661 puts("--SEPARATOR--");
662 else
663 puts(ippGetName(attr));
664</pre>
665
666<p>The <code>for</code> loop approach is normally used when collecting attributes for multiple objects (jobs, printers, etc.) in a response. Attributes with <code>NULL</code> names indicate a separator between the attributes of each object. For example, the following code will list the jobs returned from our previous <a href='#get_jobs'><code>get_jobs</code></a> example code:</p>
667
668<pre class='example'>
669<a href='#ipp_t'>ipp_t</a> *response = <a href='#get_jobs'>get_jobs</a>();
670
671if (response != NULL)
672{
673 <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
674 const char *attrname;
675 int job_id = 0;
676 const char *job_name = NULL;
677 const char *job_originating_user_name = NULL;
678
679 puts("Job ID Owner Title");
680 puts("------ ---------------- ---------------------------------");
681
682 for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
683 {
684 /* Attributes without names are separators between jobs */
685 attrname = ippGetName(attr);
686 if (attrname == NULL)
687 {
688 if (job_id > 0)
689 {
690 if (job_name == NULL)
691 job_name = "(withheld)";
692
693 if (job_originating_user_name == NULL)
694 job_originating_user_name = "(withheld)";
695
696 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
697 }
698
699 job_id = 0;
700 job_name = NULL;
701 job_originating_user_name = NULL;
702 continue;
703 }
704 else if (!strcmp(attrname, "job-id") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_INTEGER)
705 job_id = ippGetInteger(attr, 0);
706 else if (!strcmp(attrname, "job-name") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_NAME)
707 job_name = ippGetString(attr, 0, NULL);
708 else if (!strcmp(attrname, "job-originating-user-name") &amp;&amp;
709 ippGetValueTag(attr) == IPP_TAG_NAME)
710 job_originating_user_name = ippGetString(attr, 0, NULL);
711 }
712
713 if (job_id > 0)
714 {
715 if (job_name == NULL)
716 job_name = "(withheld)";
717
718 if (job_originating_user_name == NULL)
719 job_originating_user_name = "(withheld)";
720
721 printf("%5d %-16s %s\n", job_id, job_originating_user_name, job_name);
722 }
723}
724</pre>
725
726<h3><a name='CREATING_URI_STRINGS'>Creating URI Strings</a></h3>
727
728<p>To ensure proper encoding, the
729<a href='#httpAssembleURIf'><code>httpAssembleURIf</code></a> function must be
730used to format a "printer-uri" string for all printer-based requests:</p>
731
732<pre class='example'>
733const char *name = "Foo";
734char uri[1024];
735<a href='#ipp_t'>ipp_t</a> *request;
736
737<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
738 ippPort(), "/printers/%s", name);
739<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
740</pre>
741
742<h3><a name='SENDING_REQUESTS_WITH_FILES'>Sending Requests with Files</a></h3>
743
744<p>The <a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> and
745<a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> functions are
746used for requests involving files. The
747<a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> function
748attaches the named file to a request and is typically used when sending a print
749file or changing a printer's PPD file:</p>
750
751<pre class='example'>
752const char *filename = "/usr/share/cups/data/testprint.ps";
753const char *name = "Foo";
754char uri[1024];
755char resource[1024];
756<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_PRINT_JOB);
757<a href='#ipp_t'>ipp_t</a> *response;
758
759/* Use httpAssembleURIf for the printer-uri string */
760<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
761 ippPort(), "/printers/%s", name);
762<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
763<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
764 NULL, cupsUser());
765<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
766 NULL, "testprint.ps");
767
768/* Use snprintf for the resource path */
769snprintf(resource, sizeof(resource), "/printers/%s", name);
770
771response = <a href='#cupsDoFileRequest'>cupsDoFileRequest</a>(CUPS_HTTP_DEFAULT, request, resource, filename);
772</pre>
773
774<p>The <a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> function
775optionally attaches a file to the request and optionally saves a file in the
776response from the server. It is used when using a pipe for the request
777attachment or when using a request that returns a file, currently only
778<code>CUPS_GET_DOCUMENT</code> and <code>CUPS_GET_PPD</code>. For example,
779the following code will download the PPD file for the sample HP LaserJet
780printer driver:</p>
781
782<pre class='example'>
783char tempfile[1024];
784int tempfd;
785<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
786<a href='#ipp_t'>ipp_t</a> *response;
787
788<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
789 NULL, "laserjet.ppd");
790
791tempfd = cupsTempFd(tempfile, sizeof(tempfile));
792
793response = <a href='#cupsDoIORequest'>cupsDoIORequest</a>(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
794</pre>
795
796<p>The example passes <code>-1</code> for the input file descriptor to specify
797that no file is to be attached to the request. The PPD file attached to the
798response is written to the temporary file descriptor we created using the
799<code>cupsTempFd</code> function.</p>
800
801<h3><a name='ASYNCHRONOUS_REQUEST_PROCESSING'>Asynchronous Request Processing</a></h3>
802
803<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> and
804<a href='#cupsGetResponse'><code>cupsGetResponse</code></a> support
805asynchronous communications with the server. Unlike the other request
806functions, the IPP request is not automatically freed, so remember to
807free your request with the <a href='#ippDelete'><code>ippDelete</code></a>
808function.</p>
809
810<p>File data is attached to the request using the
811<a href='#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>
812function, while file data returned from the server is read using the
813<a href='#cupsReadResponseData'><code>cupsReadResponseData</code></a>
814function. We can rewrite the previous <code>CUPS_GET_PPD</code> example
815to use the asynchronous functions quite easily:</p>
816
817<pre class='example'>
818char tempfile[1024];
819int tempfd;
820<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
821<a href='#ipp_t'>ipp_t</a> *response;
822
823<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
824 NULL, "laserjet.ppd");
825
826tempfd = cupsTempFd(tempfile, sizeof(tempfile));
827
828if (<a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
829{
830 response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
831
832 if (response != NULL)
833 {
834 ssize_t bytes;
835 char buffer[8192];
836
837 while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
838 write(tempfd, buffer, bytes);
839 }
840}
841
842/* Free the request! */
843<a href='#ippDelete'>ippDelete</a>(request);
844</pre>
845
846<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> function
847returns the initial HTTP request status, typically either
848<code>HTTP_CONTINUE</code> or <code>HTTP_UNAUTHORIZED</code>. The latter status
849is returned when the request requires authentication of some sort. The
850<a href='#cupsDoAuthentication'><code>cupsDoAuthentication</code></a> function
851must be called when your see <code>HTTP_UNAUTHORIZED</code> and the request
852re-sent. We can add authentication support to our example code by using a
853<code>do ... while</code> loop:</p>
854
855<pre class='example'>
856char tempfile[1024];
857int tempfd;
858<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
859<a href='#ipp_t'>ipp_t</a> *response;
860http_status_t status;
861
862<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
863 NULL, "laserjet.ppd");
864
865tempfd = cupsTempFd(tempfile, sizeof(tempfile));
866
867/* Loop for authentication */
868do
869{
870 status = <a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/");
871
872 if (status == HTTP_UNAUTHORIZED)
873 {
874 /* Try to authenticate, break out of the loop if that fails */
875 if (<a href='#cupsDoAuthentication'>cupsDoAuthentication</a>(CUPS_HTTP_DEFAULT, "POST", "/"))
876 break;
877 }
878}
879while (status != HTTP_CONTINUE &amp;&amp; status != HTTP_UNAUTHORIZED);
880
881if (status == HTTP_CONTINUE)
882{
883 response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
884
885 if (response != NULL)
886 {
887 ssize_t bytes;
888 char buffer[8192];
889
890 while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
891 write(tempfd, buffer, bytes);
892 }
893}
894
895/* Free the request! */
896<a href='#ippDelete'>ippDelete</a>(request);
897</pre>
898<!--
899 File and directory API introduction for CUPS.
900
901 Copyright 2007-2011 by Apple Inc.
902 Copyright 1997-2005 by Easy Software Products, all rights reserved.
903
904 These coded instructions, statements, and computer programs are the
905 property of Apple Inc. and are protected by Federal copyright
906 law. Distribution and use rights are outlined in the file "LICENSE.txt"
907 which should have been included with this file. If this file is
908 file is missing or damaged, see the license at "http://www.cups.org/".
909-->
910
911<h2 class='title'><a name="OVERVIEW">Overview</a></h2>
912
913<p>The CUPS file and directory APIs provide portable interfaces
914for manipulating files and listing files and directories. Unlike
915stdio <code>FILE</code> streams, the <code>cupsFile</code> functions
916allow you to open more than 256 files at any given time. They
917also manage the platform-specific details of locking, large file
918support, line endings (CR, LF, or CR LF), and reading and writing
919files using Flate ("gzip") compression. Finally, you can also
920connect, read from, and write to network connections using the
921<code>cupsFile</code> functions.</p>
922
923<p>The <code>cupsDir</code> functions manage the platform-specific
924details of directory access/listing and provide a convenient way
925to get both a list of files and the information (permissions,
926size, timestamp, etc.) for each of those files.</p>
927<!--
928 Array API introduction for CUPS.
929
930 Copyright 2007-2011 by Apple Inc.
931 Copyright 1997-2006 by Easy Software Products, all rights reserved.
932
933 These coded instructions, statements, and computer programs are the
934 property of Apple Inc. and are protected by Federal copyright
935 law. Distribution and use rights are outlined in the file "LICENSE.txt"
936 which should have been included with this file. If this file is
937 file is missing or damaged, see the license at "http://www.cups.org/".
938-->
939
940<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
941
942<p>The CUPS array API provides a high-performance generic array container.
943The contents of the array container can be sorted and the container itself is
944designed for optimal speed and memory usage under a wide variety of conditions.
945Sorted arrays use a binary search algorithm from the last found or inserted
946element to quickly find matching elements in the array. Arrays created with the
947optional hash function can often find elements with a single lookup. The
948<a href='#cups_array_t'><code>cups_array_t</code></a> type is used when
949referring to a CUPS array.</p>
950
951<p>The CUPS scheduler (<tt>cupsd</tt>) and many of the CUPS API
952functions use the array API to efficiently manage large lists of
953data.</p>
954
955<h3><a name='MANAGING_ARRAYS'>Managing Arrays</a></h3>
956
957<p>Arrays are created using either the
958<a href='#cupsArrayNew'><code>cupsArrayNew</code></a>,
959<a href='#cupsArrayNew2'><code>cupsArrayNew2</code></a>, or
960<a href='#cupsArrayNew2'><code>cupsArrayNew3</code></a> functions. The
961first function creates a new array with the specified callback function
962and user data pointer:</p>
963
964<pre class='example'>
965#include &lt;cups/array.h&gt;
966
967static int compare_func(void *first, void *second, void *user_data);
968
969void *user_data;
970<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>(compare_func, user_data);
971</pre>
972
973<p>The comparison function (type
974<a href="#cups_arrayfunc_t"><code>cups_arrayfunc_t</code></a>) is called
975whenever an element is added to the array and can be <code>NULL</code> to
976create an unsorted array. The function returns -1 if the first element should
977come before the second, 0 if the first and second elements should have the same
978ordering, and 1 if the first element should come after the second.</p>
979
980<p>The "user_data" pointer is passed to your comparison function. Pass
981<code>NULL</code> if you do not need to associate the elements in your array
982with additional information.</p>
983
984<p>The <a href='#cupsArrayNew2'><code>cupsArrayNew2</code></a> function adds
985two more arguments to support hashed lookups, which can potentially provide
986instantaneous ("O(1)") lookups in your array:</p>
987
988<pre class='example'>
989#include &lt;cups/array.h&gt;
990
991#define HASH_SIZE 512 /* Size of hash table */
992
993static int compare_func(void *first, void *second, void *user_data);
994static int hash_func(void *element, void *user_data);
995
996void *user_data;
997<a href='#cups_array_t'>cups_array_t</a> *hash_array = <a href='#cupsArrayNew2'>cupsArrayNew2</a>(compare_func, user_data, hash_func, HASH_SIZE);
998</pre>
999
1000<p>The hash function (type
1001<a href="#cups_ahash_func_t"><code>cups_ahash_func_t</code></a>) should return a
1002number from 0 to (hash_size-1) that (hopefully) uniquely identifies the
1003element and is called whenever you look up an element in the array with
1004<a href='#cupsArrayFind'><code>cupsArrayFind</code></a>. The hash size is
1005only limited by available memory, but generally should not be larger than
100616384 to realize any performance improvement.</p>
1007
1008<p>The <a href='#cupsArrayNew3'><code>cupsArrayNew3</code></a> function adds
1009copy and free callbacks to support basic memory management of elements:</p>
1010
1011<pre class='example'>
1012#include &lt;cups/array.h&gt;
1013
1014#define HASH_SIZE 512 /* Size of hash table */
1015
1016static int compare_func(void *first, void *second, void *user_data);
1017static void *copy_func(void *element, void *user_data);
1018static void free_func(void *element, void *user_data);
1019static int hash_func(void *element, void *user_data);
1020
1021void *user_data;
1022<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew3'>cupsArrayNew3</a>(compare_func, user_data, NULL, 0, copy_func, free_func);
1023
1024<a href='#cups_array_t'>cups_array_t</a> *hash_array = <a href='#cupsArrayNew3'>cupsArrayNew3</a>(compare_func, user_data, hash_func, HASH_SIZE, copy_func, free_func);
1025</pre>
1026
1027<p>Once you have created the array, you add elements using the
1028<a href='#cupsArrayAdd'><code>cupsArrayAdd</code></a>
1029<a href='#cupsArrayInsert'><code>cupsArrayInsert</code></a> functions.
1030The first function adds an element to the array, adding the new element
1031after any elements that have the same order, while the second inserts the
1032element before others with the same order. For unsorted arrays,
1033<a href='#cupsArrayAdd'><code>cupsArrayAdd</code></a> appends the element to
1034the end of the array while
1035<a href='#cupsArrayInsert'><code>cupsArrayInsert</code></a> inserts the
1036element at the beginning of the array. For example, the following code
1037creates a sorted array of character strings:</p>
1038
1039<pre class='example'>
1040#include &lt;cups/array.h&gt;
1041
1042/* Use strcmp() to compare strings - it will ignore the user_data pointer */
1043<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
1044
1045/* Add four strings to the array */
1046<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
1047<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
1048<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
1049<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
1050</pre>
1051
1052<p>Elements are removed using the
1053<a href='#cupsArrayRemove'><code>cupsArrayRemove</code></a> function, for
1054example:</p>
1055
1056<pre class='example'>
1057#include &lt;cups/array.h&gt;
1058
1059/* Use strcmp() to compare strings - it will ignore the user_data pointer */
1060<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
1061
1062/* Add four strings to the array */
1063<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
1064<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
1065<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
1066<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
1067
1068/* Remove "Red Fish" */
1069<a href='#cupsArrayRemove'>cupsArrayRemove</a>(array, "Red Fish");
1070</pre>
1071
1072<p>Finally, you free the memory used by the array using the
1073<a href='#cupsArrayDelete'><code>cupsArrayDelete</code></a> function. All
1074of the memory for the array and hash table (if any) is freed, however <em>CUPS
1075does not free the elements unless you provide copy and free functions</em>.</p>
1076
1077<h3><a name='FINDING_AND_ENUMERATING'>Finding and Enumerating Elements</a></h3>
1078
1079<p>CUPS provides several functions to find and enumerate elements in an
1080array. Each one sets or updates a "current index" into the array, such that
1081future lookups will start where the last one left off:</p>
1082
1083<dl>
1084 <dt><a href='#cupsArrayFind'><code>cupsArrayFind</code></a></dt>
1085 <dd>Returns the first matching element.</dd>
1086 <dt><a href='#cupsArrayFirst'><code>cupsArrayFirst</code></a></dt>
1087 <dd>Returns the first element in the array.</dd>
1088 <dt><a href='#cupsArrayIndex'><code>cupsArrayIndex</code></a></dt>
1089 <dd>Returns the Nth element in the array, starting at 0.</dd>
1090 <dt><a href='#cupsArrayLast'><code>cupsArrayLast</code></a></dt>
1091 <dd>Returns the last element in the array.</dd>
1092 <dt><a href='#cupsArrayNext'><code>cupsArrayNext</code></a></dt>
1093 <dd>Returns the next element in the array.</dd>
1094 <dt><a href='#cupsArrayPrev'><code>cupsArrayPrev</code></a></dt>
1095 <dd>Returns the previous element in the array.</dd>
1096</dl>
1097
1098<p>Each of these functions returns <code>NULL</code> when there is no
1099corresponding element. For example, a simple <code>for</code> loop using the
1100<a href='#cupsArrayFirst'><code>cupsArrayFirst</code></a> and
1101<a href='#cupsArrayNext'><code>cupsArrayNext</code></a> functions will
1102enumerate all of the strings in our previous example:</p>
1103
1104<pre class='example'>
1105#include &lt;cups/array.h&gt;
1106
1107/* Use strcmp() to compare strings - it will ignore the user_data pointer */
1108<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
1109
1110/* Add four strings to the array */
1111<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
1112<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
1113<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
1114<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
1115
1116/* Show all of the strings in the array */
1117char *s;
1118for (s = (char *)<a href='#cupsArrayFirst'>cupsArrayFirst</a>(array); s != NULL; s = (char *)<a href='#cupsArrayNext'>cupsArrayNext</a>(array))
1119 puts(s);
1120</pre>