]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - doc/help/api-filter.html
Update all references to OS X to macOS.
[thirdparty/cups.git] / doc / help / api-filter.html
index 1746ff664f79a6858b1f744450d453963c2d4d28..d159656dee0cf783fcaa8e0ca08b793bc15934aa 100644 (file)
@@ -38,7 +38,19 @@ P.example {
   font-style: italic;
   margin-left: 36pt;
 }
-  
+
+DL.man DD {
+  margin-left: 5em;
+}
+
+DL.man DT {
+  margin-left: 0;
+}
+
+PRE.man {
+  margin: 0;
+}
+
 PRE.example {
   background: #eeeeee;
   border: dotted thin #999999;
@@ -77,7 +89,7 @@ A:link:hover IMG {
 }
 
 A:link, A:visited {
-  font-weight: normal;
+  font-weight: inherit;
   text-decoration: none;
 }
 
@@ -341,11 +353,9 @@ div.contents ul.subcontents li {
 <body>
 <div class='body'>
 <!--
-  "$Id$"
-
   Filter and backend programming header for CUPS.
 
-  Copyright 2008-2011 by Apple Inc.
+  Copyright 2008-2016 by Apple Inc.
 
   These coded instructions, statements, and computer programs are the
   property of Apple Inc. and are protected by Federal copyright
@@ -361,6 +371,7 @@ div.contents ul.subcontents li {
 <tr>
        <th>Headers</th>
        <th>cups/backend.h<br>
+       cups/ppd.h<br>
        cups/sidechannel.h</th>
 </tr>
 </thead>
@@ -385,7 +396,8 @@ div.contents ul.subcontents li {
 <ul class="contents">
 <li><a href="#OVERVIEW">Overview</a><ul class="subcontents">
        <li><a href="#SECURITY">Security Considerations</a></li>
-       <li><a href="#SIGNALS">Signal Handling</a></li>
+       <li><a href="#SIGNALS">Canceled Jobs and Signal Handling</a></li>
+       <li><a href="#PERMISSIONS">File Permissions</a></li>
        <li><a href="#TEMPFILES">Temporary Files</a></li>
        <li><a href="#COPIES">Copy Generation</a></li>
        <li><a href="#EXITCODES">Exit Codes</a></li>
@@ -395,6 +407,7 @@ div.contents ul.subcontents li {
        <li><a href="#COMMUNICATING_FILTER">Communicating with Filters</a></li>
        <li><a href="#SNMP">Doing SNMP Queries with Network Printers</a></li>
 </ul></li>
+<li><a href="#SANDBOXING">Sandboxing on macOS</a></li>
 <li><a href="#FUNCTIONS">Functions</a><ul class="code">
        <li><a href="#cupsBackChannelRead" title="Read data from the backchannel.">cupsBackChannelRead</a></li>
        <li><a href="#cupsBackChannelWrite" title="Write data to the backchannel.">cupsBackChannelWrite</a></li>
@@ -425,11 +438,9 @@ div.contents ul.subcontents li {
 </ul></li>
 </ul>
 <!--
-  "$Id$"
-
   Filter and backend programming introduction for CUPS.
 
-  Copyright 2007-2011 by Apple Inc.
+  Copyright 2007-2016 by Apple Inc.
   Copyright 1997-2006 by Easy Software Products, all rights reserved.
 
   These coded instructions, statements, and computer programs are the
@@ -507,18 +518,19 @@ directory to write to.</p>
 
 <p>In addition, some operating systems provide additional security mechanisms
 that further limit file system access, even for backends running as root. On
-Mac OS X, for example, no backend may write to a user's home directory.</p>
+macOS, for example, no backend may write to a user's home directory. See the <a href="#SANDBOXING">Sandboxing on macOS</a> section for more information.</p>
 </blockquote>
 
-<h3><a name="SIGNALS">Signal Handling</a><h3>
+<h3><a name="SIGNALS">Canceled Jobs and Signal Handling</a></h3>
 
 <p>The scheduler sends <code>SIGTERM</code> when a printing job is canceled or
 held. Filters, backends, and port monitors <em>must</em> catch
 <code>SIGTERM</code> and perform any cleanup necessary to produce a valid output
 file or return the printer to a known good state. The recommended behavior is to
-end the output on the current page.</p>
+end the output on the current page, preferably on the current line or object
+being printed.</p>
 
-<p>Filters and backends may also receive <code>SIGPIPE</code> when an upstream or downstream filter/backend exits with a non-zero status. Developers should generally <code>ignore SIGPIPE</code> at the beginning of <code>main()</code> with the following function call:</p>
+<p>Filters and backends may also receive <code>SIGPIPE</code> when an upstream or downstream filter/backend exits with a non-zero status. Developers should generally ignore <code>SIGPIPE</code> at the beginning of <code>main()</code> with the following function call:</p>
 
 <pre class="example">
 #include &lt;signal.h&gt;>
@@ -578,7 +590,7 @@ when running print filters and backends:</p>
 
        <dt>APPLE_LANGUAGE</dt>
        <dd>The Apple language identifier associated with the job
-       (Mac OS X only).</dd>
+       (macOS only).</dd>
 
        <dt>CHARSET</dt>
        <dd>The job character set, typically "utf-8".</dd>
@@ -656,12 +668,13 @@ prefix strings:</p>
 
        <dt>ATTR: attribute=value [attribute=value]</dt>
        <dd>Sets the named printer or job attribute(s). Typically this is used
-       to set the <code>marker-colors</code>, <code>marker-levels</code>,
+       to set the <code>marker-colors</code>, <code>marker-high-levels</code>,
+       <code>marker-levels</code>, <code>marker-low-levels</code>,
        <code>marker-message</code>, <code>marker-names</code>,
        <code>marker-types</code>, <code>printer-alert</code>, and
        <code>printer-alert-description</code> printer attributes. Standard
        <code>marker-types</code> values are listed in <a href='#TABLE1'>Table
-       1</a>.</dd>
+       1</a>. String values need special handling - see <a href="#ATTR_STRINGS">Reporting Attribute String Values</a> below.</dd>
 
        <dt>CRIT: message</dt>
        <dd>Sets the printer-state-message attribute and adds the specified
@@ -706,23 +719,15 @@ prefix strings:</p>
        this is used to update installable options or default media settings
        based on the printer configuration.</dd>
 
-       <dt>STATE: printer-state-reason [printer-state-reason ...]</dt>
        <dt>STATE: + printer-state-reason [printer-state-reason ...]</dt>
        <dt>STATE: - printer-state-reason [printer-state-reason ...]</dt>
-       <dd>Sets, adds, or removes printer-state-reason keywords to the
-       current queue. Typically this is used to indicate persistent media,
-       ink, toner, and configuration conditions or errors on a printer.
-       <a href='#TABLE2'>Table 2</a> lists the standard state keywords -
-       use vendor-prefixed ("com.acme.foo") keywords for custom states.
-
-       <blockquote><b>Note:</b>
-
-       <p>"STATE:" messages often provide visible alerts to the user. For example, on
-       Mac OS X setting a printer-state-reason value with an "-error" or "-warning"
-       suffix will cause the printer's dock item to bounce if the corresponding reason
-       is localized with a cupsIPPReason keyword in the printer's PPD file.</p>
-
-       </blockquote></dd>
+       <dd>Sets or clears printer-state-reason keywords for the current queue.
+       Typically this is used to indicate persistent media, ink, toner, and
+       configuration conditions or errors on a printer.
+       <a href='#TABLE2'>Table 2</a> lists some of the standard "printer-state-reasons" keywords from the <a href="http://www.iana.org/assignments/ipp-registrations/ipp-registrations.xhtml#ipp-registrations-4">IANA IPP Registry</a> -
+       use vendor-prefixed ("com.example.foo") keywords for custom states. See
+       <a href="#MANAGING_STATE">Managing Printer State in a Filter</a> for more
+       information.
 
        <dt>WARNING: message</dt>
        <dd>Sets the printer-state-message attribute and adds the specified
@@ -752,11 +757,11 @@ the "DEBUG:" prefix string.</p>
        <td>Fuser unit</td>
 </tr>
 <tr>
-       <td>fuserCleaningPad</td>
+       <td>fuser-cleaning-pad</td>
        <td>Fuser cleaning pad</td>
 </tr>
 <tr>
-       <td>fuserOil</td>
+       <td>fuser-oil</td>
        <td>Fuser oil</td>
 </tr>
 <tr>
@@ -768,7 +773,7 @@ the "DEBUG:" prefix string.</p>
        <td>Photo conductor</td>
 </tr>
 <tr>
-       <td>solidWax</td>
+       <td>solid-wax</td>
        <td>Wax supply</td>
 </tr>
 <tr>
@@ -780,19 +785,19 @@ the "DEBUG:" prefix string.</p>
        <td>Toner supply</td>
 </tr>
 <tr>
-       <td>transferUnit</td>
+       <td>transfer-unit</td>
        <td>Transfer unit</td>
 </tr>
 <tr>
-       <td>wasteInk</td>
+       <td>waste-ink</td>
        <td>Waste ink tank</td>
 </tr>
 <tr>
-       <td>wasteToner</td>
+       <td>waste-toner</td>
        <td>Waste toner tank</td>
 </tr>
 <tr>
-       <td>wasteWax</td>
+       <td>waste-wax</td>
        <td>Waste wax tank</td>
 </tr>
 </tbody>
@@ -811,59 +816,279 @@ the "DEBUG:" prefix string.</p>
 <tbody>
 <tr>
        <td>connecting-to-device</td>
-       <td>Connecting to printer but not printing yet</td>
+       <td>Connecting to printer but not printing yet.</td>
 </tr>
 <tr>
        <td>cover-open</td>
-       <td>A cover is open on the printer</td>
+       <td>The printer's cover is open.</td>
 </tr>
 <tr>
        <td>input-tray-missing</td>
-       <td>An input tray is missing from the printer</td>
+       <td>The paper tray is missing.</td>
 </tr>
 <tr>
        <td>marker-supply-empty</td>
-       <td>Out of ink</td>
+       <td>The printer is out of ink.</td>
 </tr>
 <tr>
        <td>marker-supply-low</td>
-       <td>Low on ink</td>
+       <td>The printer is almost out of ink.</td>
 </tr>
 <tr>
        <td>marker-waste-almost-full</td>
-       <td>Waste tank almost full</td>
+       <td>The printer's waste bin is almost full.</td>
 </tr>
 <tr>
        <td>marker-waste-full</td>
-       <td>Waste tank full</td>
+       <td>The printer's waste bin is full.</td>
 </tr>
 <tr>
        <td>media-empty</td>
-       <td>Out of media</td>
+       <td>The paper tray (any paper tray) is empty.</td>
 </tr>
 <tr>
        <td>media-jam</td>
-       <td>Media is jammed in the printer</td>
+       <td>There is a paper jam.</td>
 </tr>
 <tr>
        <td>media-low</td>
-       <td>Low on media</td>
+       <td>The paper tray (any paper tray) is almost empty.</td>
+</tr>
+<tr>
+       <td>media-needed</td>
+       <td>The paper tray needs to be filled (for a job that is printing).</td>
 </tr>
 <tr>
        <td>paused</td>
-       <td>Stop the printer</td>
+       <td>Stop the printer.</td>
 </tr>
 <tr>
        <td>timed-out</td>
-       <td>Unable to connect to printer</td>
+       <td>Unable to connect to printer.</td>
 </tr>
 <tr>
        <td>toner-empty</td>
-       <td>Out of toner</td>
+       <td>The printer is out of toner.</td>
 </tr>
 <tr>
        <td>toner-low</td>
-       <td>Low on toner</td>
+       <td>The printer is low on toner.</td>
+</tr>
+</tbody>
+</table></div>
+
+
+<h4><a name="ATTR_STRINGS">Reporting Attribute String Values</a></h4>
+
+<p>When reporting string values using "ATTR:" messages, a filter or backend must take special care to appropriately quote those values. The scheduler uses the CUPS option parsing code for attributes, so the general syntax is:</p>
+
+<pre class="example">
+name=simple
+name=simple,simple,...
+name='complex value'
+name="complex value"
+name='"complex value"','"complex value"',...
+</pre>
+
+<p>Simple values are strings that do not contain spaces, quotes, backslashes, or the comma and can be placed verbatim in the "ATTR:" message, for example:</p>
+
+<pre class="example">
+int levels[4] = { 40, 50, 60, 70 }; /* CMYK */
+
+fputs("ATTR: marker-colors=#00FFFF,#FF00FF,#FFFF00,#000000\n", stderr);
+fputs("ATTR: marker-high-levels=100,100,100,100\n", stderr);
+fprintf(stderr, "ATTR: marker-levels=%d,%d,%d,%d\n", levels[0], levels[1],
+        levels[2], levels[3], levels[4]);
+fputs("ATTR: marker-low-levels=5,5,5,5\n", stderr);
+fputs("ATTR: marker-types=toner,toner,toner,toner\n", stderr);
+</pre>
+
+<p>Complex values that contains spaces, quotes, backslashes, or the comma must be quoted. For a single value a single set of quotes is sufficient:</p>
+
+<pre class="example">
+fputs("ATTR: marker-message='Levels shown are approximate.'\n", stderr);
+</pre>
+
+<p>When multiple values are reported, each value must be enclosed by a set of single and double quotes:</p>
+
+<pre class="example">
+fputs("ATTR: marker-names='\"Cyan Toner\"','\"Magenta Toner\"',"
+      "'\"Yellow Toner\"','\"Black Toner\"'\n", stderr);
+</pre>
+
+<p>The IPP backend includes a <var>quote_string</var> function that may be used to properly quote a complex value in an "ATTR:" message:</p>
+
+<pre class="example">
+static const char *                     /* O - Quoted string */
+quote_string(const char *s,             /* I - String */
+             char       *q,             /* I - Quoted string buffer */
+             size_t     qsize)          /* I - Size of quoted string buffer */
+{
+  char  *qptr,                          /* Pointer into string buffer */
+        *qend;                          /* End of string buffer */
+
+
+  qptr = q;
+  qend = q + qsize - 5;
+
+  if (qend &lt; q)
+  {
+    *q = '\0';
+    return (q);
+  }
+
+  *qptr++ = '\'';
+  *qptr++ = '\"';
+
+  while (*s && qptr &lt; qend)
+  {
+    if (*s == '\\' || *s == '\"' || *s == '\'')
+    {
+      if (qptr &lt; (qend - 4))
+      {
+        *qptr++ = '\\';
+        *qptr++ = '\\';
+        *qptr++ = '\\';
+      }
+      else
+        break;
+    }
+
+    *qptr++ = *s++;
+  }
+
+  *qptr++ = '\"';
+  *qptr++ = '\'';
+  *qptr   = '\0';
+
+  return (q);
+}
+</pre>
+
+
+<h4><a name="MANAGING_STATE">Managing Printer State in a Filter</a></h4>
+
+<p>Filters are responsible for managing the state keywords they set using
+"STATE:" messages. Typically you will update <em>all</em> of the keywords that
+are used by the filter at startup, for example:</p>
+
+<pre class="example">
+if (foo_condition != 0)
+  fputs("STATE: +com.example.foo\n", stderr);
+else
+  fputs("STATE: -com.example.foo\n", stderr);
+
+if (bar_condition != 0)
+  fputs("STATE: +com.example.bar\n", stderr);
+else
+  fputs("STATE: -com.example.bar\n", stderr);
+</pre>
+
+<p>Then as conditions change, your filter sends "STATE: +keyword" or "STATE:
+-keyword" messages as necessary to set or clear the corresponding keyword,
+respectively.</p>
+
+<p>State keywords are often used to notify the user of issues that span across
+jobs, for example "media-empty-warning" that indicates one or more paper trays
+are empty. These keywords should not be cleared unless the corresponding issue
+no longer exists.</p>
+
+<p>Filters should clear job-related keywords on startup and exit so that they
+do not remain set between jobs.  For example, "connecting-to-device" is a job
+sub-state and not an issue that applies when a job is not printing.</p>
+
+<blockquote><b>Note:</b>
+
+<p>"STATE:" messages often provide visible alerts to the user. For example,
+on macOS setting a printer-state-reason value with an "-error" or
+"-warning" suffix will cause the printer's dock item to bounce if the
+corresponding reason is localized with a cupsIPPReason keyword in the
+printer's PPD file.</p>
+
+<p>When providing a vendor-prefixed keyword, <em>always</em> provide the
+corresponding standard keyword (if any) to allow clients to respond to the
+condition correctly. For example, if you provide a vendor-prefixed keyword
+for a low cyan ink condition ("com.example.cyan-ink-low") you must also set the
+"marker-supply-low-warning" keyword. In such cases you should also refrain
+from localizing the vendor-prefixed keyword in the PPD file - otherwise both
+the generic and vendor-specific keyword will be shown in the user
+interface.</p>
+
+</blockquote>
+
+<h4><a name="REPORTING_SUPPLIES">Reporting Supply Levels</a></h4>
+
+<p>CUPS tracks several "marker-*" attributes for ink/toner supply level
+reporting. These attributes allow applications to display the current supply
+levels for a printer without printer-specific software. <a href="#TABLE3">Table 3</a> lists the marker attributes and what they represent.</p>
+
+<p>Filters set marker attributes by sending "ATTR:" messages to stderr. For
+example, a filter supporting an inkjet printer with black and tri-color ink
+cartridges would use the following to initialize the supply attributes:</p>
+
+<pre class="example">
+fputs("ATTR: marker-colors=#000000,#00FFFF#FF00FF#FFFF00\n", stderr);
+fputs("ATTR: marker-low-levels=5,10\n", stderr);
+fputs("ATTR: marker-names=Black,Tri-Color\n", stderr);
+fputs("ATTR: marker-types=ink,ink\n", stderr);
+</pre>
+
+<p>Then periodically the filter queries the printer for its current supply
+levels and updates them with a separate "ATTR:" message:</p>
+
+<pre class="example">
+int black_level, tri_level;
+...
+fprintf(stderr, "ATTR: marker-levels=%d,%d\n", black_level, tri_level);
+</pre>
+
+<div class='table'><table width='80%' summary='Table 3: Supply Level Attributes'>
+<caption>Table 3: <a name='TABLE3'>Supply Level Attributes</a></caption>
+<thead>
+<tr>
+       <th>Attribute</th>
+       <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+       <td>marker-colors</td>
+       <td>A list of comma-separated colors; each color is either "none" or one or
+       more hex-encoded sRGB colors of the form "#RRGGBB".</td>
+</tr>
+<tr>
+       <td>marker-high-levels</td>
+       <td>A list of comma-separated "almost full" level values from 0 to 100; a
+       value of 100 should be used for supplies that are consumed/emptied like ink
+       cartridges.</td>
+</tr>
+<tr>
+       <td>marker-levels</td>
+       <td>A list of comma-separated level values for each supply. A value of -1
+       indicates the level is unavailable, -2 indicates unknown, and -3 indicates
+       the level is unknown but has not yet reached capacity. Values from 0 to 100
+       indicate the corresponding percentage.</td>
+</tr>
+<tr>
+       <td>marker-low-levels</td>
+       <td>A list of comma-separated "almost empty" level values from 0 to 100; a
+       value of 0 should be used for supplies that are filled like waste ink
+       tanks.</td>
+</tr>
+<tr>
+       <td>marker-message</td>
+       <td>A human-readable supply status message for the user like "12 pages of
+       ink remaining."</td>
+</tr>
+<tr>
+       <td>marker-names</td>
+       <td>A list of comma-separated supply names like "Cyan Ink", "Fuser",
+       etc.</td>
+</tr>
+<tr>
+       <td>marker-types</td>
+       <td>A list of comma-separated supply types; the types are listed in
+       <a href="#TABLE1">Table 1</a>.</td>
 </tr>
 </tbody>
 </table></div>
@@ -914,12 +1139,44 @@ datalen = sizeof(data) - 1;
 status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_GET_DEVICE_ID, data, &amp;datalen, 1.0);
 
 /* Use the returned value if OK was returned and the length is non-zero */
-if (status == CUPS_SC_STATUS_OK && datalen > 0)
+if (status == CUPS_SC_STATUS_OK &amp;&amp; datalen > 0)
   data[datalen] = '\0';
 else
   data[0] = '\0';
 </pre>
 
+<h4><a name="DRAIN_OUTPUT">Forcing All Output to a Printer</a></h4>
+
+<p>The
+<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a>
+function allows you to tell the backend to send all pending data to the printer.
+This is most often needed when sending query commands to the printer. For example:</p>
+
+<pre class="example">
+#include &lt;cups/cups.h&gt;
+#include &lt;cups/sidechannel.h&gt;
+
+char data[1024];
+int datalen = sizeof(data);
+<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
+
+/* Flush pending output to stdout */
+fflush(stdout);
+
+/* Drain output to backend, waiting for up to 30 seconds */
+status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_DRAIN_OUTPUT, data, &amp;datalen, 30.0);
+
+/* Read the response if the output was sent */
+if (status == CUPS_SC_STATUS_OK)
+{
+  ssize_t bytes;
+
+  /* Wait up to 10.0 seconds for back-channel data */
+  bytes = cupsBackChannelRead(data, sizeof(data), 10.0);
+  /* do something with the data from the printer */
+}
+</pre>
+
 <h3><a name="COMMUNICATING_FILTER">Communicating with Filters</a></h3>
 
 <p>Backends communicate with filters using the reciprocal functions
@@ -1032,8 +1289,30 @@ void *my_data;
 
 <a href="#cupsSideChannelSNMPWalk">cupsSNMPSideChannelWalk</a>(".1.3.6.1.2.1.43", 5.0, my_callback, my_data);
 </pre>
+
+<h2><a name="SANDBOXING">Sandboxing on macOS</a></h2>
+
+<p>Starting with macOS 10.6, filters and backends are run inside a security "sandbox" which further limits (beyond the normal UNIX user/group permissions) what a filter or backend can do. This helps to both secure the printing system from malicious software and enforce the functional separation of components in the CUPS filter chain. What follows is a list of actions that are explicitly allowed for all filters and backends:</p>
+
+<ol>
+
+       <li>Reading of files: pursuant to normal UNIX file permissions, filters and backends can read files for the current job from the <var>/private/var/spool/cups</var> directory and other files on mounted filesystems <em>except</em> for user home directories under <var>/Users</var>.</li>
+
+       <li>Writing of files: pursuant to normal UNIX file permissions, filters and backends can read/write files to the cache directory specified by the <code>CUPS_CACHEDIR</code> environment variable, to the state directory specified by the <code>CUPS_STATEDIR</code> environment variable, to the temporary directory specified by the <code>TMPDIR</code> environment variable, and under the <var>/private/var/db</var>, <var>/private/var/folders</var>, <var>/private/var/lib</var>, <var>/private/var/mysql</var>, <var>/private/var/run</var>, <var>/private/var/spool</var> (except <var>/private/var/spool/cups</var>), <var>/Library/Application&nbsp;Support</var>, <var>/Library/Caches</var>, <var>/Library/Logs</var>, <var>/Library/Preferences</var>, <var>/Library/WebServer</var>, and <var>/Users/Shared</var> directories.</li>
+
+       <li>Execution of programs: pursuant to normal UNIX file permissions, filters and backends can execute any program not located under the <var>/Users</var> directory. Child processes inherit the sandbox and are subject to the same restrictions as the parent.</li>
+
+       <li>Bluetooth and USB: backends can access Bluetooth and USB printers through IOKit. <em>Filters cannot access Bluetooth and USB printers directly.</em></li>
+
+       <li>Network: filters and backends can access UNIX domain sockets under the <var>/private/tmp</var>, <var>/private/var/run</var>, and <var>/private/var/tmp</var> directories. Backends can also create IPv4 and IPv6 TCP (outgoing) and UDP (incoming and outgoing) socket, and bind to local source ports. <em>Filters cannot directly create IPv4 and IPv6 TCP or UDP sockets.</em></li>
+
+       <li>Notifications: filters and backends can send notifications via the Darwin <code>notify_post()</code> API.</li>
+
+</ol>
+
+<blockquote><b>Note:</b> The sandbox profile used in CUPS 2.0 still allows some actions that are not listed above - these privileges will be removed over time until the profile matches the list above.</blockquote>
 <h2 class="title"><a name="FUNCTIONS">Functions</a></h2>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.2/Mac OS X 10.5&nbsp;</span><a name="cupsBackChannelRead">cupsBackChannelRead</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.2/macOS 10.5&nbsp;</span><a name="cupsBackChannelRead">cupsBackChannelRead</a></h3>
 <p class="description">Read data from the backchannel.</p>
 <p class="code">
 ssize_t cupsBackChannelRead (<br>
@@ -1058,7 +1337,7 @@ parameter controls how many seconds to wait for the data - use 0.0 to
 return immediately if there is no data, -1.0 to wait for data indefinitely.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.2/Mac OS X 10.5&nbsp;</span><a name="cupsBackChannelWrite">cupsBackChannelWrite</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.2/macOS 10.5&nbsp;</span><a name="cupsBackChannelWrite">cupsBackChannelWrite</a></h3>
 <p class="description">Write data to the backchannel.</p>
 <p class="code">
 ssize_t cupsBackChannelWrite (<br>
@@ -1084,7 +1363,7 @@ controls how many seconds to wait for the data to be written - use
 indefinitely.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.2/Mac OS X 10.5&nbsp;</span><a name="cupsBackendDeviceURI">cupsBackendDeviceURI</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.2/macOS 10.5&nbsp;</span><a name="cupsBackendDeviceURI">cupsBackendDeviceURI</a></h3>
 <p class="description">Get the device URI for a backend.</p>
 <p class="code">
 const char *cupsBackendDeviceURI (<br>
@@ -1104,7 +1383,7 @@ variable or the device URI passed in argv[0], whichever is found
 first.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.4/Mac OS X 10.6&nbsp;</span><a name="cupsBackendReport">cupsBackendReport</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.4/macOS 10.6&nbsp;</span><a name="cupsBackendReport">cupsBackendReport</a></h3>
 <p class="description">Write a device line from a backend.</p>
 <p class="code">
 void cupsBackendReport (<br>
@@ -1136,7 +1415,7 @@ It handles quoting of special characters in the device-make-and-model,
 device-info, device-id, and device-location strings.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.3/Mac OS X 10.5&nbsp;</span><a name="cupsSideChannelDoRequest">cupsSideChannelDoRequest</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.3/macOS 10.5&nbsp;</span><a name="cupsSideChannelDoRequest">cupsSideChannelDoRequest</a></h3>
 <p class="description">Send a side-channel command to a backend and wait for a response.</p>
 <p class="code">
 <a href="#cups_sc_status_t">cups_sc_status_t</a> cupsSideChannelDoRequest (<br>
@@ -1170,7 +1449,7 @@ pointed to by the &quot;data&quot; parameter.  cupsSideChannelDoRequest() will
 update the value to contain the number of data bytes in the buffer.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.3/Mac OS X 10.5&nbsp;</span><a name="cupsSideChannelRead">cupsSideChannelRead</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.3/macOS 10.5&nbsp;</span><a name="cupsSideChannelRead">cupsSideChannelRead</a></h3>
 <p class="description">Read a side-channel message.</p>
 <p class="code">
 int cupsSideChannelRead (<br>
@@ -1206,7 +1485,7 @@ pointed to by the &quot;data&quot; parameter.  cupsSideChannelDoRequest() will
 update the value to contain the number of data bytes in the buffer.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.4/Mac OS X 10.6&nbsp;</span><a name="cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.4/macOS 10.6&nbsp;</span><a name="cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a></h3>
 <p class="description">Query a SNMP OID's value.</p>
 <p class="code">
 <a href="#cups_sc_status_t">cups_sc_status_t</a> cupsSideChannelSNMPGet (<br>
@@ -1247,7 +1526,7 @@ support SNMP queries.  <code>CUPS_SC_STATUS_NO_RESPONSE</code> is returned when
 the printer does not respond to the SNMP query.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.4/Mac OS X 10.6&nbsp;</span><a name="cupsSideChannelSNMPWalk">cupsSideChannelSNMPWalk</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.4/macOS 10.6&nbsp;</span><a name="cupsSideChannelSNMPWalk">cupsSideChannelSNMPWalk</a></h3>
 <p class="description">Query multiple SNMP OID values.</p>
 <p class="code">
 <a href="#cups_sc_status_t">cups_sc_status_t</a> cupsSideChannelSNMPWalk (<br>
@@ -1293,7 +1572,7 @@ support SNMP queries.  <code>CUPS_SC_STATUS_NO_RESPONSE</code> is returned when
 the printer does not respond to the first SNMP query.
 
 </p>
-<h3 class="function"><span class="info">&nbsp;CUPS 1.3/Mac OS X 10.5&nbsp;</span><a name="cupsSideChannelWrite">cupsSideChannelWrite</a></h3>
+<h3 class="function"><span class="info">&nbsp;CUPS 1.3/macOS 10.5&nbsp;</span><a name="cupsSideChannelWrite">cupsSideChannelWrite</a></h3>
 <p class="description">Write a side-channel message.</p>
 <p class="code">
 int cupsSideChannelWrite (<br>
@@ -1398,15 +1677,15 @@ typedef void (*cups_sc_walk_func_t)(const char *oid, const char *data, int datal
 <dd class="description">Drain all pending output</dd>
 <dt>CUPS_SC_CMD_GET_BIDI </dt>
 <dd class="description">Return bidirectional capabilities</dd>
-<dt>CUPS_SC_CMD_GET_CONNECTED <span class="info">&nbsp;CUPS 1.5/Mac OS X 10.7&nbsp;</span></dt>
+<dt>CUPS_SC_CMD_GET_CONNECTED <span class="info">&nbsp;CUPS 1.5/macOS 10.7&nbsp;</span></dt>
 <dd class="description">Return whether the backend is &quot;connected&quot; to the printer </dd>
 <dt>CUPS_SC_CMD_GET_DEVICE_ID </dt>
 <dd class="description">Return the IEEE-1284 device ID</dd>
 <dt>CUPS_SC_CMD_GET_STATE </dt>
 <dd class="description">Return the device state</dd>
-<dt>CUPS_SC_CMD_SNMP_GET <span class="info">&nbsp;CUPS 1.4/Mac OS X 10.6&nbsp;</span></dt>
+<dt>CUPS_SC_CMD_SNMP_GET <span class="info">&nbsp;CUPS 1.4/macOS 10.6&nbsp;</span></dt>
 <dd class="description">Query an SNMP OID </dd>
-<dt>CUPS_SC_CMD_SNMP_GET_NEXT <span class="info">&nbsp;CUPS 1.4/Mac OS X 10.6&nbsp;</span></dt>
+<dt>CUPS_SC_CMD_SNMP_GET_NEXT <span class="info">&nbsp;CUPS 1.4/macOS 10.6&nbsp;</span></dt>
 <dd class="description">Query the next SNMP OID </dd>
 <dt>CUPS_SC_CMD_SOFT_RESET </dt>
 <dd class="description">Do a soft reset</dd>