target is one of
+the following:
+
+ epm - Builds a portable shell script and tar file based
+ distribution. This format will also backup your
+ existing printing system if you decide to remove
+ CUPS at some future time.
+ aix - Builds an AIX binary distribution.
+ bsd - Builds a *BSD binary distribution.
+ deb - Builds a Debian binary distribution.
+ depot - Builds a HP-UX binary distribution.
+ pkg - Builds a Solaris binary distribution.
+ rpm - Builds a RPM binary distribution.
+ tardist - Builds an IRIX binary distribution.
+
+
+REPORTING PROBLEMS
+
+If you have problems, READ THE DOCUMENTATION FIRST! If the
+documentation does not solve your problems please send an email
+to "cups-support@cups.org". Include your operating system and
+version, compiler and version, and any errors or problems you've
+run into. The "/var/log/cups/error_log" file should also be sent,
+as it often helps to determine the cause of your problem.
+
+If you are running a version of Linux, be sure to provide the
+Linux distribution you have, too.
+
+Please note that the "cups-support@cups.org" email address goes
+to the CUPS developers; they are busy people, so your email may
+go unanswered for days or weeks. In general, only general build
+or distribution problems will actually get answered - for
+end-user support see the "README.txt" for a summary of the
+resources available.
diff --git a/LICENSE.html b/LICENSE.html
new file mode 100644
index 000000000..2008a6312
--- /dev/null
+++ b/LICENSE.html
@@ -0,0 +1,1068 @@
+
+
+ Software License Agreement - Common UNIX Printing System
+
+
+
+
+Common UNIX Printing System License Agreement
+
+Copyright 1997-2006 by Easy Software Products
+44141 AIRPORT VIEW DR STE 204
+HOLLYWOOD, MARYLAND 20636 USA
+
+Voice: +1.301.373.9600
+Email: cups-info@cups.org
+WWW: http://www.cups.org
+
+
Introduction
+
+The Common UNIX Printing SystemTM,
+("CUPSTM"), is provided under the GNU General Public
+License ("GPL") and GNU Library General Public License ("LGPL"),
+Version 2, with exceptions for Apple operating systems and the
+OpenSSL toolkit. A copy of the exceptions and licenses follow
+this introduction.
+
+
The GNU LGPL applies to the CUPS API library, located in the
+"cups" subdirectory of the CUPS source distribution and in the
+"cups" include directory and library files in the binary
+distributions. The GNU GPL applies to the remainder of the CUPS
+distribution, including the "pdftops" filter which is based upon
+Xpdf and the CUPS imaging library.
+
+
For those not familiar with the GNU GPL, the license basically
+allows you to:
+
+
+
+ - Use the CUPS software at no charge.
+
+ - Distribute verbatim copies of the software in source
+ or binary form.
+
+ - Sell verbatim copies of the software for a media
+ fee, or sell support for the software.
+
+ - Distribute or sell printer drivers and filters that
+ use CUPS so long as source code is made available under
+ the GPL.
+
+
+
+What this license does not allow you to do is make
+changes or add features to CUPS and then sell a binary
+distribution without source code. You must provide source for
+any new drivers, changes, or additions to the software, and all
+code must be provided under the GPL or LGPL as appropriate. The
+only exceptions to this are the portions of the CUPS software
+covered by the Apple operating system license exceptions
+outlined later in this license agreement.
+
+
The GNU LGPL relaxes the "link-to" restriction, allowing you
+to develop applications that use the CUPS API library under
+other licenses and/or conditions as appropriate for your
+application.
+
+
License Exceptions
+
+In addition, as the copyright holder of CUPS, Easy Software
+Products grants the following special exceptions:
+
+
+
+ - Apple Operating System Development License
+ Exception;
+
+
+
+ - Software that is developed by any person or
+ entity for an Apple Operating System ("Apple
+ OS-Developed Software"), including but not
+ limited to Apple and third party printer
+ drivers, filters, and backends for an Apple
+ Operating System, that is linked to the CUPS
+ imaging library or based on any sample filters
+ or backends provided with CUPS shall not be
+ considered to be a derivative work or collective
+ work based on the CUPS program and is exempt
+ from the mandatory source code release clauses
+ of the GNU GPL. You may therefore distribute
+ linked combinations of the CUPS imaging library
+ with Apple OS-Developed Software without
+ releasing the source code of the Apple
+ OS-Developed Software. You may also use sample
+ filters and backends provided with CUPS to
+ develop Apple OS-Developed Software without
+ releasing the source code of the Apple
+ OS-Developed Software.
+
+ - An Apple Operating System means any
+ operating system software developed and/or
+ marketed by Apple Computer, Inc., including but
+ not limited to all existing releases and
+ versions of Apple's Darwin, Mac OS X, and Mac OS
+ X Server products and all follow-on releases and
+ future versions thereof.
+
+ - This exception is only available for Apple
+ OS-Developed Software and does not apply to
+ software that is distributed for use on other
+ operating systems.
+
+ - All CUPS software that falls under this
+ license exception have the following text at the
+ top of each source file:
+
+
This file is subject to the Apple
+ OS-Developed Software
+ exception.
+
+
+
+ - OpenSSL Toolkit License Exception;
+
+
+
+ - Easy Software Products explicitly allows the
+ compilation and distribution of the CUPS
+ software with the OpenSSL Toolkit.
+
+
+
+
+
+No developer is required to provide these exceptions in a
+derived work.
+
+
Trademarks
+
+Easy Software Products has trademarked the Common UNIX
+Printing System, CUPS, and CUPS logo. You may use these names
+and logos in any direct port or binary distribution of CUPS.
+Please contact Easy Software Products for written permission to
+use them in derivative products. Our intention is to protect the
+value of these trademarks and ensure that any derivative product
+meets the same high-quality standards as the original.
+
+
Binary Distribution Rights
+
+Easy Software Products also sells rights to the CUPS source
+code under a binary distribution license for vendors that are
+unable to release source code for their drivers, additions, and
+modifications to CUPS under the GNU GPL and LGPL. For
+information please contact us at the address shown above.
+
+
The Common UNIX Printing System provides a "pdftops" filter
+that is based on the Xpdf software. For binary distribution
+licensing of this software, please contact:
+
+
+Derek B. Noonburg
+Email: derekn@foolabs.com
+WWW: http://www.foolabs.com/xpdf/
+
+
+Support
+
+Easy Software Products sells software support for CUPS as
+well as a commercial printing product based on CUPS called ESP
+Print Pro. You can find out more at our web site:
+
+
+http://www.easysw.com/
+
+
+
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+
+Copyright 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim
+copies of this license document, but changing it is not allowed.
+
+
+Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+
When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+
To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+
For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+
We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+
Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+
Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+
The precise terms and conditions for copying, distribution and
+modification follow.
+
+
GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+
+
+- This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+
Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+
- You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+
You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+
- You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+
+
+- You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+
- You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+
- if the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+
Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+
In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+
- You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+
+
+- Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+
- Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+
- Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+
If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
- You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
- You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+
- Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+
- If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+
If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+
It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+
This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
- If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+
- The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+
Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+
- If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+
+
+NO WARRANTY
+
+
+
+- BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+
To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+one line to give the program's name and an idea of what it does.
+Copyright (C) yyyy name of author
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+
If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
+type `show w'. This is free software, and you are welcome
+to redistribute it under certain conditions; type `show c'
+for details.
+
+
+The hypothetical commands `show w' and `show c' should show
+the appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and
+`show c'; they could even be mouse-clicks or menu items--whatever
+suits your program.
+
+
You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+
+Yoyodyne, Inc., hereby disclaims all copyright
+interest in the program `Gnomovision'
+(which makes passes at compilers) written
+by James Hacker.
+
+signature of Ty Coon, 1 April 1989
+Ty Coon, President of Vice
+
+
+
+GNU LIBRARY GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+
+Copyright (C) 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+
+Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+
This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+
When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+
To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+
For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+
Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+
Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+
Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+
Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+
The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+
Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+
However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+
The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+
Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0.
+This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+
A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+
The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+
"Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+
Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+
1.
+You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+
You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
2.
+You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+
+
+ - The modified work must itself be a software library.
+
+
+
- You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+
+
- You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+
+
- If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+
(For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+
Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+
In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+
3.
+You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+
Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+
This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+
4.
+You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+
If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
5.
+A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+
However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+
When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+
If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+
Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
6.
+As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+
You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+
+
+ - Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+
+
- Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+
+
- If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+
+
- Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+
+
+For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+
It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
7.
+You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+
+
+ - Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+
+
- Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+
+
+8.
+You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+
9.
+You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+
10.
+Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+
11.
+If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+
If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+
It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+
This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
12.
+If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+
13.
+The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+
Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
14.
+If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+
NO WARRANTY
+
+
15.
+BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+
16.
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+
END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+
To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+one line to give the library's name and an idea of what it does.
+Copyright (C) year name of author
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+
You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+
+
+That's all there is to it!
+
+
+
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 000000000..12e4af6bc
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,964 @@
+ Common UNIX Printing System License Agreement
+
+ Copyright 1997-2006 by Easy Software Products
+ 44141 AIRPORT VIEW DR STE 204
+ HOLLYWOOD, MARYLAND 20636 USA
+
+ Voice: +1.301.373.9600
+ Email: cups-info@cups.org
+ WWW: http://www.cups.org
+
+
+INTRODUCTION
+
+The Common UNIX Printing System(tm), ("CUPS(tm)"), is provided
+under the GNU General Public License ("GPL") and GNU Library
+General Public License ("LGPL"), Version 2, with exceptions for
+Apple operating systems and the OpenSSL toolkit. A copy of the
+exceptions and licenses follow this introduction.
+
+The GNU LGPL applies to the CUPS API library, located in the
+"cups" subdirectory of the CUPS source distribution and in the
+"cups" include directory and library files in the binary
+distributions. The GNU GPL applies to the remainder of the CUPS
+distribution, including the "pdftops" filter which is based upon
+Xpdf and the CUPS imaging library.
+
+For those not familiar with the GNU GPL, the license basically
+allows you to:
+
+ - Use the CUPS software at no charge.
+ - Distribute verbatim copies of the software in source or
+ binary form.
+ - Sell verbatim copies of the software for a media fee, or
+ sell support for the software.
+ - Distribute or sell printer drivers and filters that use
+ CUPS so long as source code is made available under the
+ GPL.
+
+What this license *does not* allow you to do is make changes or
+add features to CUPS and then sell a binary distribution without
+source code. You must provide source for any new drivers,
+changes, or additions to the software, and all code must be
+provided under the GPL or LGPL as appropriate. The only
+exceptions to this are the portions of the CUPS software covered
+by the Apple operating system license exceptions outlined later
+in this license agreement.
+
+The GNU LGPL relaxes the "link-to" restriction, allowing you to
+develop applications that use the CUPS API library under other
+licenses and/or conditions as appropriate for your application.
+
+
+LICENSE EXCEPTIONS
+
+In addition, as the copyright holder of CUPS, Easy Software
+Products grants the following special exceptions:
+
+ 1. Apple Operating System Development License Exception;
+
+ a. Software that is developed by any person or entity
+ for an Apple Operating System ("Apple OS-Developed
+ Software"), including but not limited to Apple and
+ third party printer drivers, filters, and backends
+ for an Apple Operating System, that is linked to the
+ CUPS imaging library or based on any sample filters
+ or backends provided with CUPS shall not be
+ considered to be a derivative work or collective work
+ based on the CUPS program and is exempt from the
+ mandatory source code release clauses of the GNU GPL.
+ You may therefore distribute linked combinations of
+ the CUPS imaging library with Apple OS-Developed
+ Software without releasing the source code of the
+ Apple OS-Developed Software. You may also use sample
+ filters and backends provided with CUPS to develop
+ Apple OS-Developed Software without releasing the
+ source code of the Apple OS-Developed Software.
+
+ b. An Apple Operating System means any operating system
+ software developed and/or marketed by Apple Computer,
+ Inc., including but not limited to all existing
+ releases and versions of Apple's Darwin, Mac OS X,
+ and Mac OS X Server products and all follow-on
+ releases and future versions thereof.
+
+ c. This exception is only available for Apple
+ OS-Developed Software and does not apply to software
+ that is distributed for use on other operating
+ systems.
+
+ d. All CUPS software that falls under this license
+ exception have the following text at the top of each
+ source file:
+
+ This file is subject to the Apple OS-Developed
+ Software exception.
+
+ 2. OpenSSL Toolkit License Exception;
+
+ a. Easy Software Products explicitly allows the
+ compilation and distribution of the CUPS software
+ with the OpenSSL Toolkit.
+
+No developer is required to provide these exceptions in a
+derived work.
+
+
+TRADEMARKS
+
+Easy Software Products has trademarked the Common UNIX Printing
+System, CUPS, and CUPS logo. You may use these names and logos
+in any direct port or binary distribution of CUPS. Please
+contact Easy Software Products for written permission to use
+them in derivative products. Our intention is to protect the
+value of these trademarks and ensure that any derivative product
+meets the same high-quality standards as the original.
+
+
+BINARY DISTRIBUTION RIGHTS
+
+Easy Software Products also sells rights to the CUPS source code
+under a binary distribution license for vendors that are unable
+to release source code for their drivers, additions, and
+modifications to CUPS under the GNU GPL and LGPL. For
+information please contact us at the address shown above.
+
+The Common UNIX Printing System provides a "pdftops" filter that
+is based on the Xpdf software. For binary distribution licensing
+of this software, please contact:
+
+ Derek B. Noonburg
+ Email: derekn@foolabs.com
+ WWW: http://www.foolabs.com/xpdf/
+
+
+SUPPORT
+
+Easy Software Products sells software support for CUPS as well
+as a commercial printing product based on CUPS called ESP Print
+Pro. You can find out more at our web site:
+
+ http://www.easysw.com/
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C) 19yy
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ [This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makedefs.in b/Makedefs.in
new file mode 100644
index 000000000..7829d96b4
--- /dev/null
+++ b/Makedefs.in
@@ -0,0 +1,193 @@
+#
+# "$Id: Makedefs.in 4836 2005-11-13 06:14:46Z mike $"
+#
+# Common makefile definitions for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2005 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636 USA
+#
+# Voice: (301) 373-9600
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+#
+# Programs...
+#
+
+AR = @AR@
+AWK = @AWK@
+CC = @LIBTOOL@ @CC@
+CXX = @LIBTOOL@ @CXX@
+DSO = @DSO@
+HTMLDOC = @HTMLDOC@
+INSTALL = @INSTALL@
+LIBTOOL = @LIBTOOL@
+LN = @LN@ -sf
+MV = @MV@
+RANLIB = @RANLIB@
+RM = @RM@ -f
+SED = @SED@
+SHELL = /bin/sh
+STRIP = @STRIP@
+
+#
+# Installation programs...
+#
+
+INSTALL_BIN = $(LIBTOOL) $(INSTALL) -m 755 -s
+INSTALL_DATA = $(INSTALL) -m 644
+INSTALL_DIR = $(INSTALL) -d
+INSTALL_LIB = $(LIBTOOL) $(INSTALL) -m 755
+INSTALL_MAN = $(INSTALL) -m 644
+INSTALL_SCRIPT = $(INSTALL) -m 755
+
+#
+# Default user and group for the scheduler...
+#
+
+CUPS_USER = @CUPS_USER@
+CUPS_GROUP = @CUPS_GROUP@
+
+#
+# Libraries...
+#
+
+LIBCUPS = @LIBCUPS@
+LIBCUPSIMAGE = @LIBCUPSIMAGE@
+LIBJPEG = @LIBJPEG@
+LIBMALLOC = @LIBMALLOC@
+LIBPAPER = @LIBPAPER@
+LIBPNG = @LIBPNG@
+LIBSLP = @LIBSLP@
+LIBTIFF = @LIBTIFF@
+LIBZ = @LIBZ@
+
+#
+# Program options...
+#
+# OPTIM defines the common compiler optimization/debugging options.
+# OPTIONS defines other compile-time options (currently only -dDEBUG for
+# extra debug info)
+#
+
+ARFLAGS = @ARFLAGS@
+BACKLIBS = @BACKLIBS@
+CFLAGS = -I.. $(RC_CFLAGS) $(SSLFLAGS) @CPPFLAGS@ @CFLAGS@ \
+ @LARGEFILE@ $(OPTIONS)
+COMMONLIBS = @LIBS@
+CXXFLAGS = -I.. $(RC_CFLAGS) $(SSLFLAGS) @CPPFLAGS@ @CXXFLAGS@ \
+ @LARGEFILE@ $(OPTIONS)
+CXXLIBS = @CXXLIBS@
+DSOFLAGS = @DSOFLAGS@
+DSOLIBS = @DSOLIBS@ $(COMMONLIBS)
+IMGLIBS = @IMGLIBS@ -lm
+LDFLAGS = -L../cups -L../filter $(RC_CFLAGS) @LDFLAGS@ $(OPTIM)
+LINKCUPS = @LINKCUPS@ $(SSLLIBS)
+LINKCUPSIMAGE = @LINKCUPSIMAGE@
+LIBS = $(LINKCUPS) $(COMMONLIBS)
+OPTIM = @OPTIM@
+OPTIONS =
+PAMLIBS = @PAMLIBS@
+SSLFLAGS = @SSLFLAGS@
+SSLLIBS = @SSLLIBS@
+
+#
+# Directories...
+#
+# The first section uses the GNU names (which are *extremely*
+# difficult to find in a makefile because they are lowercase...)
+# We have to define these first because autoconf uses ${prefix}
+# and ${exec_prefix} for most of the other directories...
+#
+# This is immediately followed by definition in ALL CAPS for the
+# needed directories...
+#
+
+bindir = @bindir@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+includedir = @includedir@
+infodir = @infodir@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+top_srcdir = @top_srcdir@
+
+BUILDROOT = $(DSTROOT)
+
+AMANDIR = $(BUILDROOT)@AMANDIR@
+BINDIR = $(BUILDROOT)@bindir@
+CACHEDIR = $(BUILDROOT)@CUPS_CACHEDIR@
+DATADIR = $(BUILDROOT)@CUPS_DATADIR@
+DOCDIR = $(BUILDROOT)@CUPS_DOCROOT@
+INCLUDEDIR = $(BUILDROOT)$(includedir)
+INITDIR = @INITDIR@
+INITDDIR = @INITDDIR@
+LIBDIR = $(BUILDROOT)$(libdir)
+LOCALEDIR = $(BUILDROOT)@CUPS_LOCALEDIR@
+LOGDIR = $(BUILDROOT)@CUPS_LOGDIR@
+MANDIR = $(BUILDROOT)@mandir@
+PMANDIR = $(BUILDROOT)@PMANDIR@
+REQUESTS = $(BUILDROOT)@CUPS_REQUESTS@
+SBINDIR = $(BUILDROOT)@sbindir@
+SERVERBIN = $(BUILDROOT)@CUPS_SERVERBIN@
+SERVERROOT = $(BUILDROOT)@CUPS_SERVERROOT@
+STATEDIR = $(BUILDROOT)@CUPS_STATEDIR@
+
+MAN1EXT = @MAN1EXT@
+MAN5EXT = @MAN5EXT@
+MAN8EXT = @MAN8EXT@
+MAN8DIR = @MAN8DIR@
+
+PAMDIR = $(BUILDROOT)@PAMDIR@
+PAMFILE = @PAMFILE@
+
+
+#
+# Rules...
+#
+
+.SILENT:
+.SUFFIXES: .1 .1.gz .1m .1m.gz .5 .5.gz .8 .8.gz .a .c .cxx .h .man .o .gz
+
+.c.o:
+ echo Compiling $<...
+ $(CC) $(OPTIM) $(CFLAGS) -c $<
+
+.cxx.o:
+ echo Compiling $<...
+ $(CXX) $(OPTIM) $(CXXFLAGS) -c $<
+
+.man.1 .man.1m .man.5 .man.8:
+ echo Linking $<...
+ $(RM) $@
+ $(LN) $< $@
+
+.man.1.gz .man.1m.gz .man.5.gz .man.8.gz .man.gz:
+ echo -n Compressing $<...
+ $(RM) $@
+ gzip -v9 <$< >$@
+
+
+#
+# End of "$Id: Makedefs.in 4836 2005-11-13 06:14:46Z mike $"
+#
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..5d28e1bfa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,171 @@
+#
+# "$Id: Makefile 4835 2005-11-12 22:00:16Z mike $"
+#
+# Top-level Makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2004 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3142 USA
+#
+# Voice: (301) 373-9600
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include Makedefs
+
+#
+# Directories to make...
+#
+
+DIRS = cups backend berkeley cgi-bin filter man pdftops \
+ notifier scheduler systemv
+
+#
+# Make all targets...
+#
+
+all:
+ chmod +x cups-config
+ for dir in $(DIRS); do\
+ echo Making all in $$dir... ;\
+ (cd $$dir ; $(MAKE) $(MFLAGS)) || exit 1;\
+ done
+
+#
+# Remove object and target files...
+#
+
+clean:
+ for dir in $(DIRS); do\
+ echo Cleaning in $$dir... ;\
+ (cd $$dir; $(MAKE) $(MFLAGS) clean) || exit 1;\
+ done
+
+#
+# Make dependencies
+#
+
+depend:
+ for dir in $(DIRS); do\
+ echo Making dependencies in $$dir... ;\
+ (cd $$dir; $(MAKE) $(MFLAGS) depend) || exit 1;\
+ done
+
+
+#
+# Install object and target files...
+#
+
+install: installhdrs
+ for dir in $(DIRS); do\
+ echo Installing in $$dir... ;\
+ (cd $$dir; $(MAKE) $(MFLAGS) install) || exit 1;\
+ done
+ echo Installing in conf...
+ (cd conf; $(MAKE) $(MFLAGS) install)
+ echo Installing in data...
+ (cd data; $(MAKE) $(MFLAGS) install)
+ echo Installing in doc...
+ (cd doc; $(MAKE) $(MFLAGS) install)
+ echo Installing in fonts...
+ (cd fonts; $(MAKE) $(MFLAGS) install)
+ echo Installing in locale...
+ (cd locale; $(MAKE) $(MFLAGS) install)
+ echo Installing in ppd...
+ (cd ppd; $(MAKE) $(MFLAGS) install)
+ echo Installing in templates...
+ (cd templates; $(MAKE) $(MFLAGS) install)
+ echo Installing cups-config script...
+ $(INSTALL_DIR) $(BINDIR)
+ $(INSTALL_SCRIPT) cups-config $(BINDIR)/cups-config
+ echo Installing startup script...
+ if test "x$(INITDIR)" != "x"; then \
+ $(INSTALL_DIR) $(BUILDROOT)$(INITDIR)/init.d; \
+ $(INSTALL_SCRIPT) cups.sh $(BUILDROOT)$(INITDIR)/init.d/cups; \
+ $(INSTALL_DIR) $(BUILDROOT)$(INITDIR)/rc0.d; \
+ $(INSTALL_SCRIPT) cups.sh $(BUILDROOT)$(INITDIR)/rc0.d/K00cups; \
+ $(INSTALL_DIR) $(BUILDROOT)$(INITDIR)/rc2.d; \
+ $(INSTALL_SCRIPT) cups.sh $(BUILDROOT)$(INITDIR)/rc2.d/S99cups; \
+ $(INSTALL_DIR) $(BUILDROOT)$(INITDIR)/rc3.d; \
+ $(INSTALL_SCRIPT) cups.sh $(BUILDROOT)$(INITDIR)/rc3.d/S99cups; \
+ $(INSTALL_DIR) $(BUILDROOT)$(INITDIR)/rc5.d; \
+ $(INSTALL_SCRIPT) cups.sh $(BUILDROOT)$(INITDIR)/rc5.d/S99cups; \
+ fi
+ if test "x$(INITDIR)" = "x" -a "x$(INITDDIR)" != "x"; then \
+ $(INSTALL_DIR) $(BUILDROOT)$(INITDDIR); \
+ if test "$(INITDDIR)" = "/System/Library/StartupItems/PrintingServices"; then \
+ $(INSTALL_SCRIPT) cups.osx $(BUILDROOT)$(INITDDIR)/PrintingServices; \
+ $(INSTALL_DATA) cups.plist $(BUILDROOT)$(INITDDIR)/StartupParameters.plist; \
+ $(INSTALL_DIR) $(BUILDROOT)$(INITDDIR)/Resources/English.lproj; \
+ $(INSTALL_DATA) cups.strings $(BUILDROOT)$(INITDDIR)/Resources/English.lproj/Localizable.strings; \
+ else \
+ $(INSTALL_SCRIPT) cups.sh $(BUILDROOT)$(INITDDIR)/cups; \
+ fi \
+ fi
+
+
+#
+# Install source and header files...
+#
+
+installsrc:
+ gnutar --dereference --exclude=CVS -cf - . | gnutar -C $(SRCROOT) -xf -
+
+installhdrs:
+ (cd cups ; $(MAKE) $(MFLAGS) installhdrs) || exit 1;\
+ (cd filter ; $(MAKE) $(MFLAGS) installhdrs) || exit 1;
+
+
+#
+# Run the test suite...
+#
+
+check test: all
+ echo Running CUPS test suite...
+ cd test; ./run-stp-tests.sh
+
+
+#
+# Make software distributions using EPM (http://www.easysw.com/epm)...
+#
+
+EPMFLAGS = -v
+
+aix:
+ epm $(EPMFLAGS) -f aix cups packaging/cups.list
+
+bsd:
+ epm $(EPMFLAGS) -f bsd cups packaging/cups.list
+
+epm:
+ epm $(EPMFLAGS) cups packaging/cups.list
+
+rpm:
+ epm $(EPMFLAGS) -f rpm cups packaging/cups.list
+
+deb:
+ epm $(EPMFLAGS) -f deb cups packaging/cups.list
+
+depot:
+ epm $(EPMFLAGS) -f depot cups packaging/cups.list
+
+pkg:
+ epm $(EPMFLAGS) -f pkg cups packaging/cups.list
+
+tardist:
+ epm $(EPMFLAGS) -f tardist cups packaging/cups.list
+
+#
+# End of "$Id: Makefile 4835 2005-11-12 22:00:16Z mike $".
+#
diff --git a/README.txt b/README.txt
new file mode 100644
index 000000000..c2f964832
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,290 @@
+README - CUPS v1.2.0b1 - 01/10/2006
+-----------------------------------
+
+*****************************************************************
+*****************************************************************
+**** ****
+**** WARNING: THIS IS BETA RELEASE SOFTWARE AND MAY BE ****
+**** TOTALLY UNSTABLE. DO NOT USE IN ENVIRONMENTS ****
+**** WHERE RELIABLE SOFTWARE IS REQUIRED! ****
+**** ****
+*****************************************************************
+*****************************************************************
+
+Looking for compile instructions? Read the file "INSTALL.txt"
+instead...
+
+*****************************************************************
+*****************************************************************
+**** ****
+**** IF YOU HAVE A NON-POSTSCRIPT PRINTER, YOU WILL ALSO ****
+**** NEED TO INSTALL ESP GHOSTSCRIPT OR A PATCHED VERSION ****
+**** OF A STANDARD GHOSTSCRIPT RELEASE. ****
+**** ****
+*****************************************************************
+*****************************************************************
+
+
+INTRODUCTION
+
+CUPS provides a portable printing layer for UNIX(r)-based
+operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX
+vendors and users. CUPS provides the System V and Berkeley
+command-line interfaces.
+
+CUPS uses the Internet Printing Protocol ("IPP") as the basis
+for managing print jobs and queues. The Line Printer Daemon
+("LPD") Server Message Block ("SMB"), and AppSocket (a.k.a.
+JetDirect) protocols are also supported with reduced
+functionality. CUPS adds network printer browsing and
+PostScript Printer Description ("PPD") based printing options to
+support real-world printing under UNIX.
+
+CUPS includes an image file RIP that supports printing of image
+files to non-PostScript printers. A customized version of GNU
+Ghostscript for CUPS called ESP Ghostscript is available
+separately to support printing of PostScript files within the
+CUPS driver framework. Sample drivers for Dymo, EPSON, HP, and
+OKIDATA printers are included that use these filters.
+
+Drivers for thousands of printers are provided with our ESP
+Print Pro software, available at:
+
+ http://www.easysw.com/printpro/
+
+CUPS is licensed under the GNU General Public License and GNU
+Library General Public License. Please contact Easy Software
+Products for commercial support and "binary distribution"
+rights.
+
+
+SYSTEM REQUIREMENTS
+
+Binary distributions require a minimum of 10MB of free disk
+space. We do not recommend using CUPS on a workstation with less
+than 32MB of RAM or a PC with less than 16MB of RAM.
+
+If you are installing from source you'll need ANSI-compliant C
+and C++ compilers and optionally one or more image file support
+libraries. Complete source installation instructions can be
+found in the file "INSTALL.txt".
+
+
+SOFTWARE REQUIREMENTS
+
+The following operating system software is required to install
+one of the binary distributions from Easy Software Products:
+
+ - AIX 4.3 or higher
+ - HP-UX 11.00 or higher
+ - IRIX 6.5 or higher
+ - Linux 2.4 with glibc 2.2 or higher
+ - Solaris 7 or higher (SPARC or Intel)
+
+
+INSTALLING "PORTABLE" CUPS DISTRIBUTIONS
+
+We are currently distributing "portable" CUPS binary
+distributions in TAR format with installation and removal
+scripts generated by our ESP Package Manager (EPM) software,
+which is available from:
+
+ http://www.easysw.com/epm
+
+WARNING: Installing CUPS will overwrite your existing printing
+system. Backup files are made by the installation script and
+restored by the removal script, so if you experience problems
+you should be able to remove the CUPS software to restore your
+previous configuration. However, Easy Software Products makes
+no warranty for this and will not be liable for any lost
+revenues, etc.
+
+To install the CUPS software you will need to be logged in as
+root (doing an "su" is good enough). Once you are the root
+user, run the installation script with:
+
+ ./cups.install ENTER
+
+After asking you a few yes/no questions the CUPS software will
+be installed and the scheduler will be started automatically.
+
+
+INSTALLING HOST-SPECIFIC (RPM, DEBIAN, ETC.) DISTRIBUTIONS
+
+The host-specific distributions use the operating system
+software installation tools. To install a host-specific
+distribution please consult the CUPS Software Administrators
+Manual or your operating system documentation.
+
+
+READING THE DOCUMENTATION
+
+Once you have installed the software you can access the
+documentation (and a bunch of other stuff) on-line at:
+
+ http://localhost:631
+
+If you're having trouble getting that far, the documentation is
+located in the "/usr/share/doc/cups" directory in the binary
+distributions, and under the "doc" directory in the source
+archives.
+
+Please read the documentation before asking questions.
+
+
+GETTING SUPPORT AND OTHER RESOURCES
+
+If you have problems, READ THE DOCUMENTATION FIRST! We also
+provide many discussion forums which are available at:
+
+ http://www.cups.org/newsgroups.php
+
+Commercial support (with a guaranteed response time) is available
+from Easy Software Products. For more information see:
+
+ http://www.easysw.com/cups/
+
+See the CUPS web site at "http://www.cups.org/" for other site
+links.
+
+
+SETTING UP PRINTER QUEUES USING YOUR WEB BROWSER
+
+CUPS 1.2 includes a web-based administration tool that allows you
+to manage printers, classes, and jobs on your server. To access
+the printer administration tools open the following URL in your
+browser:
+
+ http://localhost:631/admin
+
+You will be asked for the administration password (root or any
+other user in the sys/system/root group on your system) and then
+shown a menu of available functions.
+
+DO NOT use the hostname for your machine - it will not work with
+the default CUPS configuration. To enable administration access
+on other addresses, consult the CUPS Software Administrators
+Manual.
+
+
+SETTING UP PRINTER QUEUES FROM THE COMMAND-LINE
+
+CUPS works best with PPD (PostScript Printer Description)
+files. In a pinch you can also use System V style printer
+interface scripts.
+
+Six sample PPD files are provided with this distribution that
+utilize the PostScript and image file RIPs and the sample EPSON
+and HP printer drivers. To add the sample DeskJet driver to the
+system for a printer connected to the parallel port, use one of
+the following commands:
+
+ HP-UX:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/c2t0d0_lp -E
+
+ IRIX:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/plp -E
+
+ Linux:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/lp0 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/lp1 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/lp2 -E
+
+ Solaris:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/bpp0 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/ecpp0 -E
+
+Similarly, for the other sample drivers you can use:
+
+ Driver PPD File
+ ----------------------------- ------------
+ Dymo Label Printers dymo.ppd
+ EPSON Stylus Color Series stcolor.ppd
+ EPSON Stylus Photo Series stphoto.ppd
+ EPSON Stylus New Color Series stcolor2.ppd
+ EPSON Stylus New Photo Series stphoto2.ppd
+ EPSON 9-pin Series epson9.ppd
+ EPSON 24-pin Series epson24.ppd
+ HP DeskJet Series deskjet.ppd
+ HP New DeskJet Series deskjet2.ppd
+ HP LaserJet Series laserjet.ppd
+ OKIDATA 9-Pin Series okidata9.ppd
+ OKIDATA 24-Pin Series okidat24.ppd
+
+These sample drivers provide basic printing capabilities, but
+generally do not exercise the full potential of the printers or
+CUPS. For commercial printer drivers check out our ESP Print
+Pro software at:
+
+ http://www.easysw.com/printpro/
+
+
+PRINTING FILES
+
+CUPS provides both the System V "lp" and Berkeley "lpr" commands
+for printing:
+
+ lp filename
+ lpr filename
+
+Both the "lp" and "lpr" commands support printing options for
+the driver:
+
+ lp -omedia=A4 -oresolution=600dpi filename
+ lpr -omedia=A4 -oresolution=600dpi filename
+
+CUPS recognizes many types of images files as well as PDF,
+PostScript, HP-GL/2, and text files, so you can print those
+files directly rather than through an application.
+
+If you have an application that generates output specifically
+for your printer then you need to use the "-oraw" or "-l"
+options:
+
+ lp -oraw filename
+ lpr -l filename
+
+This will prevent the filters from misinterpreting your print
+file.
+
+
+LEGAL STUFF
+
+CUPS is Copyright 1993-2006 by Easy Software Products. CUPS,
+the CUPS logo, and the Common UNIX Printing System are the
+trademark property of Easy Software Products.
+
+The MD5 Digest code is Copyright 1999 Aladdin Enterprises.
+
+The PDF filter (pdftops) is based on the Xpdf software,
+Copyright 1996-2005 by Derek B. Noonburg.
+
+This software is based in part on the work of the Independent
+JPEG Group.
+
+CUPS is provided under the terms of the GNU General Public
+License and GNU Library General Public License. This program is
+distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the "LICENSE.html",
+"LICENSE.txt", or "cups.license" files for more information.
+
+For commercial licensing information, please contact:
+
+ Attn: CUPS Licensing Information
+ Easy Software Products
+ 44141 Airport View Drive, Suite 204
+ Hollywood, Maryland 20636 USA
+
+ Voice: +1.301.373.9600
+ Email: cups-info@cups.org
+ WWW: http://www.cups.org
+
+Note that commercial licensors may also require a license from
+Derek B. Noonburg who developed the Xpdf software used to print
+PDF files.
diff --git a/backend/Dependencies b/backend/Dependencies
new file mode 100644
index 000000000..70897ad97
--- /dev/null
+++ b/backend/Dependencies
@@ -0,0 +1,25 @@
+# DO NOT DELETE
+
+betest.o: ../cups/string.h ../config.h
+ipp.o: ../cups/http-private.h ../config.h ../cups/http.h ../cups/md5.h
+ipp.o: ../cups/backend.h ../cups/cups.h ../cups/ipp.h ../cups/ppd.h
+ipp.o: ../cups/file.h ../cups/language.h ../cups/array.h ../cups/string.h
+lpd.o: ../cups/backend.h ../cups/http-private.h ../config.h ../cups/http.h
+lpd.o: ../cups/md5.h ../cups/cups.h ../cups/ipp.h ../cups/ppd.h
+lpd.o: ../cups/file.h ../cups/string.h
+parallel.o: ../cups/backend.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+parallel.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h ../cups/string.h
+parallel.o: ../config.h ieee1284.c ../cups/debug.h
+scsi.o: ../cups/backend.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+scsi.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h ../cups/string.h
+scsi.o: ../config.h
+serial.o: ../cups/backend.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+serial.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h ../cups/string.h
+serial.o: ../config.h
+socket.o: ../cups/backend.h ../cups/http-private.h ../config.h ../cups/http.h
+socket.o: ../cups/md5.h ../cups/cups.h ../cups/ipp.h ../cups/ppd.h
+socket.o: ../cups/file.h ../cups/string.h
+test1284.o: ../cups/string.h ../config.h ieee1284.c ../cups/debug.h
+usb.o: ../cups/backend.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+usb.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h ../cups/string.h
+usb.o: ../config.h
diff --git a/backend/Makefile b/backend/Makefile
new file mode 100644
index 000000000..26ce19b4f
--- /dev/null
+++ b/backend/Makefile
@@ -0,0 +1,178 @@
+#
+# "$Id: Makefile 4903 2006-01-10 20:02:46Z mike $"
+#
+# Backend makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2006 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636 USA
+#
+# Voice: (301) 373-9600
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+# This file is subject to the Apple OS-Developed Software exception.
+#
+
+include ../Makedefs
+
+BACKENDS = ipp lpd parallel scsi serial socket usb
+TARGETS = betest test1284 $(BACKENDS)
+OBJS = betest.o ipp.o lpd.o parallel.o scsi.o \
+ serial.o socket.o test1284.o usb.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS) http
+
+
+#
+# Update dependencies (without system header dependencies...)
+#
+
+depend:
+ makedepend -Y -I.. -fDependencies $(OBJS:.o=.c) >/dev/null 2>&1
+
+
+#
+# Install all targets...
+#
+
+install: all
+ $(INSTALL_DIR) $(SERVERBIN)/backend
+ for file in $(BACKENDS); do \
+ $(INSTALL_BIN) $$file $(SERVERBIN)/backend; \
+ done
+ $(RM) $(SERVERBIN)/backend/http
+ $(LN) ipp $(SERVERBIN)/backend/http
+
+
+#
+# betest
+#
+
+betest: betest.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o betest betest.o $(LIBS)
+
+
+#
+# test1284
+#
+
+test1284: test1284.o ../cups/libcups.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o test1284 test1284.o ../cups/libcups.a
+
+
+#
+# ieee1394
+#
+
+ieee1394: ieee1394.o ieee1394-linux.o
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o ieee1394 ieee1394.o ieee1394-linux.o -lraw1394 $(LIBS)
+
+ieee1394.o: ieee1394.h
+ieee1394-linux.o: ieee1394.h
+
+
+#
+# ipp
+#
+
+ipp: ipp.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o ipp ipp.o $(LIBS)
+ $(RM) http
+ $(LN) ipp http
+
+
+#
+# lpd
+#
+
+lpd: lpd.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpd lpd.o $(LIBS)
+
+
+#
+# parallel
+#
+
+parallel: parallel.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o parallel parallel.o $(LIBS)
+
+
+#
+# scsi
+#
+
+scsi: scsi.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o scsi scsi.o $(LIBS)
+
+scsi.o: scsi.c scsi-irix.c scsi-linux.c
+
+
+#
+# serial
+#
+
+serial: serial.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o serial serial.o $(BACKLIBS) $(LIBS)
+
+
+#
+# socket
+#
+
+socket: socket.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o socket socket.o $(LIBS)
+
+
+#
+# usb
+#
+
+usb: usb.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o usb usb.o $(BACKLIBS) $(LIBS)
+usb.o: usb.c usb-darwin.c usb-unix.c ieee1284.c
+
+
+#
+# Dependencies...
+#
+
+include Dependencies
+
+
+#
+# End of "$Id: Makefile 4903 2006-01-10 20:02:46Z mike $".
+#
diff --git a/backend/betest.c b/backend/betest.c
new file mode 100644
index 000000000..f0ea6cdb2
--- /dev/null
+++ b/backend/betest.c
@@ -0,0 +1,87 @@
+/*
+ * "$Id: betest.c 4494 2005-02-18 02:18:11Z mike $"
+ *
+ * Backend test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Run the named backend.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+
+
+/*
+ * 'main()' - Run the named backend.
+ *
+ * Usage:
+ *
+ * betest device-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (7 or 8) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char backend[255]; /* Method in URI */
+
+
+ if (argc < 7 || argc > 8)
+ {
+ fputs("Usage: betest device-uri job-id user title copies options [file]\n",
+ stderr);
+ return (1);
+ }
+
+ /*
+ * Extract the method from the device-uri - that's the program we want to
+ * execute.
+ */
+
+ if (sscanf(argv[1], "%254[^:]", backend) != 1)
+ {
+ fputs("betest: Bad device-uri - no colon!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Execute and return
+ */
+
+ execl(backend, argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7],
+ NULL);
+
+ return (1);
+}
+
+
+/*
+ * End of "$Id: betest.c 4494 2005-02-18 02:18:11Z mike $".
+ */
diff --git a/backend/easysw-firewire-design.txt b/backend/easysw-firewire-design.txt
new file mode 100644
index 000000000..194c487ed
--- /dev/null
+++ b/backend/easysw-firewire-design.txt
@@ -0,0 +1,71 @@
+Preliminary Design for CUPS Firewire Printer Backend - 03/19/2002
+-----------------------------------------------------------------
+
+OVERVIEW
+
+ Easy Software Products will develop an IEEE-1394, a.k.a.
+ Firewire, printing interface for its Common UNIX Printing
+ System ("CUPS") for initial use under the Linux operating
+ system. A follow-on implementation for MacOS X is
+ anticipated as well.
+
+ The operating system interfaces for IEEE-1394 ports vary
+ widely; the CUPS printing interface will abstract the OS
+ layer to a simpler interface geared towards discovering,
+ opening, reading from, writing to, and closing IEEE-1394
+ printers.
+
+ The initial development of the CUPS backend will be targeted
+ at the EPSON Stylus Pro 10000 large format printer, which
+ requires the bandwidth provided by Firewire in order to
+ print at full speed. This printer supports printing via
+ Serial Bus Protocol 2 (SBP-2) using the SCSI and PWG command
+ sets. The CUPS backend will implement the PWG command set on
+ LUN 0 only.
+
+
+OS ABSTRACTION LAYER
+
+ The OS abstraction layer will be a thin client library that
+ implements the following functions:
+
+ ieee1394_list
+ ieee1394_open
+ ieee1394_close
+ ieee1394_read
+ ieee1394_write
+ ieee1394_error
+
+ The "ieee1394_list" function will list all of the available
+ printer devices on the bus. The device information will
+ consist of the device URI (ieee1394:/something) used to
+ access the device and the make and model information, if
+ available, for the device ("EPSON Stylus Printer").
+
+ The "ieee1394_open" and "ieee1394_close" functions will open
+ and close a connection to the printer, respectively.
+
+ The "ieee1394_read" and "ieee1394_write" functions will read
+ and write data to and from the printer, respectively. The
+ read function will be non-blocking, returning data only if
+ there is data coming back from the printer.
+
+ The "ieee1394_error" function will return a string
+ describing the last error or NULL if no error occurred.
+
+ The library will be responsible for creating any background
+ threads that are needed to monitor the connection to the
+ printer.
+
+
+CUPS BACKEND
+
+ The CUPS backend will use the OS abstraction layer to list
+ and access the Firewire printers. The "main" function will
+ read and write printer data, while the "list_devices"
+ function will be called as necessary to identify the
+ available devices.
+
+ The CUPS 1.1 backend will record any status information in
+ the error log file, while the 1.2 backend will supply it to
+ the printer driver process.
diff --git a/backend/easysw-firewire-linux.txt b/backend/easysw-firewire-linux.txt
new file mode 100644
index 000000000..a8e461189
--- /dev/null
+++ b/backend/easysw-firewire-linux.txt
@@ -0,0 +1,35 @@
+Easy Software Products
+44141 Airport View Drive
+Suite 204
+Hollywood, Maryland 20636
++1.301.373.9600
+March 8, 2002
+
+
+Subject: EPSON Firewire Printer Driver for Linux
+
+Currently, no Firewire printer support exists for Linux. Since
+the latest EPSON printer products depend on the Firewire
+interface to print at full speed, a solution is needed to
+support customers using Linux as their server platform.
+
+The Linux Firewire subsystem provides a user-mode driver
+interface that allows driver programs to access Firewire
+devices. Easy Software Products will utilize this interface to
+develop a "backend" program for the Common UNIX Printing System
+that will allow users to print to EPSON printers using the
+Firewire interface.
+
+After examining the Linux interface, we estimate that it will
+require approximately 30 hours of development time to write,
+test, and document the Firewire backend, for a total cost of
+$3,000. The new backend will become a standard part of the CUPS
+software distribution and will be included with at least the
+following Linux distributions:
+
+ - Caldera Linux
+ - Mandrake Linux
+ - Red Hat Linux
+ - SuSE Linux
+
+ESP will provide EPSON with binaries for Red Hat Linux 7.2.
diff --git a/backend/ieee1284.c b/backend/ieee1284.c
new file mode 100644
index 000000000..6152c4c7a
--- /dev/null
+++ b/backend/ieee1284.c
@@ -0,0 +1,373 @@
+/*
+ * "$Id: ieee1284.c 4903 2006-01-10 20:02:46Z mike $"
+ *
+ * IEEE-1284 support functions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * get_device_id() - Get the IEEE-1284 device ID string and corresponding
+ * URI.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#ifdef __linux
+# include
+# include
+# define IOCNR_GET_DEVICE_ID 1
+# define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)
+#endif /* __linux */
+
+#ifdef __sun
+# ifdef __sparc
+# include
+# else
+# include
+# include
+# endif /* __sparc */
+#endif /* __sun */
+
+
+/*
+ * 'get_device_id()' - Get the IEEE-1284 device ID string and
+ * corresponding URI.
+ */
+
+int /* O - 0 on success, -1 on failure */
+get_device_id(
+ int fd, /* I - File descriptor */
+ char *device_id, /* O - 1284 device ID */
+ int device_id_size, /* I - Size of buffer */
+ char *make_model, /* O - Make/model */
+ int make_model_size, /* I - Size of buffer */
+ const char *scheme, /* I - URI scheme */
+ char *uri, /* O - Device URI */
+ int uri_size) /* I - Size of buffer */
+{
+ char *attr, /* 1284 attribute */
+ *delim, /* 1284 delimiter */
+ *uriptr, /* Pointer into URI */
+ *mfg, /* Manufacturer string */
+ *mdl, /* Model string */
+ serial_number[1024]; /* Serial number string */
+#ifdef __linux
+ int length; /* Length of device ID info */
+#endif /* __linux */
+#ifdef __sun
+ struct ecpp_device_id did; /* Device ID buffer */
+#endif /* __sun */
+
+ DEBUG_printf(("get_device_id(fd=%d, device_id=%p, device_id_size=%d, "
+ "make_model=%p, make_model_size=%d, scheme=\"%s\", "
+ "uri=%p, uri_size=%d)\n", fd, device_id, device_id_size,
+ make_model, make_model_size, scheme ? scheme : "(null)",
+ uri, uri_size));
+
+ /*
+ * Range check input...
+ */
+
+ if (fd < 0 ||
+ !device_id || device_id_size < 32 ||
+ !make_model || make_model_size < 32)
+ {
+ DEBUG_puts("get_device_id: Bad args!");
+ return (-1);
+ }
+
+ *device_id = '\0';
+ *make_model = '\0';
+
+ if (uri)
+ *uri = '\0';
+
+ /*
+ * Get the device ID string...
+ */
+
+#ifdef __linux
+ if (!ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id))
+ {
+ /*
+ * Extract the length of the device ID string from the first two
+ * bytes. The 1284 spec says the length is stored MSB first...
+ */
+
+ length = (((unsigned)device_id[0] & 255) << 8) +
+ ((unsigned)device_id[1] & 255);
+
+ /*
+ * Check to see if the length is larger than our buffer; first
+ * assume that the vendor incorrectly implemented the 1284 spec,
+ * and then limit the length to the size of our buffer...
+ */
+
+ if (length > (device_id_size - 2))
+ length = (((unsigned)device_id[1] & 255) << 8) +
+ ((unsigned)device_id[0] & 255);
+
+ if (length > (device_id_size - 2))
+ length = device_id_size - 2;
+
+ /*
+ * Copy the device ID text to the beginning of the buffer and
+ * nul-terminate.
+ */
+
+ memmove(device_id, device_id + 2, length);
+ device_id[length] = '\0';
+ }
+# ifdef DEBUG
+ else
+ printf("get_device_id: ioctl failed - %s\n", strerror(errno));
+# endif /* DEBUG */
+#endif /* __linux */
+
+#if defined(__sun) && defined(ECPPIOC_GETDEVID)
+ did.mode = ECPP_CENTRONICS;
+ did.len = device_id_size - 1;
+ did.rlen = 0;
+ did.addr = device_id;
+
+ if (!ioctl(fd, ECPPIOC_GETDEVID, &did))
+ {
+ /*
+ * Nul-terminate the device ID text.
+ */
+
+ if (did.rlen < (device_id_size - 1))
+ device_id[did.rlen] = '\0';
+ else
+ device_id[device_id_size - 1] = '\0';
+ }
+# ifdef DEBUG
+ else
+ printf("get_device_id: ioctl failed - %s\n", strerror(errno));
+# endif /* DEBUG */
+#endif /* __sun && ECPPIOC_GETDEVID */
+
+ DEBUG_printf(("get_device_id: device_id=\"%s\"\n", device_id));
+
+ if (!*device_id)
+ return (-1);
+
+ /*
+ * Look for the description field...
+ */
+
+ if ((attr = strstr(device_id, "DES:")) != NULL)
+ attr += 4;
+ else if ((attr = strstr(device_id, "DESCRIPTION:")) != NULL)
+ attr += 12;
+
+ if (attr)
+ {
+ /*
+ * Make sure the description contains something useful, since some
+ * printer manufacturers (HP) apparently don't follow the standards
+ * they helped to define...
+ *
+ * Here we require the description to be 8 or more characters in length,
+ * containing at least one space and one letter.
+ */
+
+ if ((delim = strchr(attr, ';')) == NULL)
+ delim = attr + strlen(attr);
+
+ if ((delim - attr) < 8)
+ attr = NULL;
+ else
+ {
+ char *ptr; /* Pointer into description */
+ int letters, /* Number of letters seen */
+ spaces; /* Number of spaces seen */
+
+
+ for (ptr = attr, letters = 0, spaces = 0; ptr < delim; ptr ++)
+ {
+ if (isspace(*ptr & 255))
+ spaces ++;
+ else if (isalpha(*ptr & 255))
+ letters ++;
+
+ if (spaces && letters)
+ break;
+ }
+
+ if (!spaces || !letters)
+ attr = NULL;
+ }
+ }
+
+ if ((mfg = strstr(device_id, "MANUFACTURER:")) != NULL)
+ mfg += 13;
+ else if ((mfg = strstr(device_id, "MFG:")) != NULL)
+ mfg += 4;
+
+ if ((mdl = strstr(device_id, "MODEL:")) != NULL)
+ mdl += 6;
+ else if ((mdl = strstr(device_id, "MDL:")) != NULL)
+ mdl += 4;
+
+ if (attr)
+ {
+ /*
+ * Use description...
+ */
+
+ if (!strncasecmp(attr, "Hewlett-Packard hp ", 19))
+ {
+ /*
+ * Check for a common HP bug...
+ */
+
+ strlcpy(make_model, "HP ", make_model_size);
+ strlcpy(make_model + 3, attr + 19, make_model_size - 3);
+ }
+ else if (!strncasecmp(attr, "Hewlett-Packard ", 16))
+ {
+ strlcpy(make_model, "HP ", make_model_size);
+ strlcpy(make_model + 3, attr + 16, make_model_size - 3);
+ }
+ else
+ {
+ strlcpy(make_model, attr, make_model_size);
+ }
+ }
+ else if (mfg && mdl)
+ {
+ /*
+ * Build a make-model string from the manufacturer and model attributes...
+ */
+
+ if (!strncasecmp(mfg, "Hewlett-Packard", 15))
+ strlcpy(make_model, "HP", make_model_size);
+ else
+ strlcpy(make_model, mfg, make_model_size);
+
+ if ((delim = strchr(make_model, ';')) != NULL)
+ *delim = '\0';
+
+ if (!strncasecmp(make_model, mdl, strlen(make_model)))
+ {
+ /*
+ * Just copy model string, since it has the manufacturer...
+ */
+
+ strlcpy(make_model, mdl, make_model_size);
+ }
+ else
+ {
+ /*
+ * Concatenate the make and model...
+ */
+
+ strlcat(make_model, " ", make_model_size);
+ strlcat(make_model, mdl, make_model_size);
+ }
+ }
+ else
+ {
+ /*
+ * Use "Unknown" as the printer make and model...
+ */
+
+ strlcpy(make_model, "Unknown", make_model_size);
+ }
+
+ if ((delim = strchr(make_model, ';')) != NULL)
+ *delim = '\0';
+
+ if (scheme && uri && uri_size > 32)
+ {
+ /*
+ * Look for the serial number field...
+ */
+
+ if ((attr = strstr(device_id, "SERN:")) != NULL)
+ attr += 5;
+ else if ((attr = strstr(device_id, "SERIALNUMBER:")) != NULL)
+ attr += 13;
+ else if ((attr = strstr(device_id, ";SN:")) != NULL)
+ attr += 4;
+
+ if (attr)
+ {
+ strlcpy(serial_number, attr, sizeof(serial_number));
+
+ if ((delim = strchr(serial_number, ';')) != NULL)
+ *delim = '\0';
+ }
+ else
+ serial_number[0] = '\0';
+
+ /*
+ * Generate the device URI from the make_model and serial number strings.
+ */
+
+ snprintf(uri, uri_size, "%s://", scheme);
+ for (uriptr = uri + strlen(uri), delim = make_model;
+ *delim && uriptr < (uri + uri_size - 1);
+ delim ++)
+ if (*delim == ' ')
+ {
+ delim ++;
+ *uriptr++ = '/';
+ break;
+ }
+ else
+ *uriptr++ = *delim;
+
+ for (; *delim && uriptr < (uri + uri_size - 3); delim ++)
+ if (*delim == ' ')
+ {
+ *uriptr++ = '%';
+ *uriptr++ = '2';
+ *uriptr++ = '0';
+ }
+ else
+ *uriptr++ = *delim;
+
+ *uriptr = '\0';
+
+ if (serial_number[0])
+ {
+ /*
+ * Add the serial number to the URI...
+ */
+
+ strlcat(uri, "?serial=", uri_size);
+ strlcat(uri, serial_number, uri_size);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: ieee1284.c 4903 2006-01-10 20:02:46Z mike $".
+ */
diff --git a/backend/ieee1394-linux.c b/backend/ieee1394-linux.c
new file mode 100644
index 000000000..b95e8204e
--- /dev/null
+++ b/backend/ieee1394-linux.c
@@ -0,0 +1,877 @@
+/*
+ * "$Id: ieee1394-linux.c 4703 2005-09-26 19:33:58Z mike $"
+ *
+ * Linux IEEE-1394 glue for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2002 by Easy Software Products, all rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use
+ * of this software must display the following
+ * acknowledgement:
+ *
+ * This product includes software developed by Easy
+ * Software Products.
+ *
+ * 4. The name of Easy Software Products may not be used to
+ * endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Contents:
+ *
+ * get_device_id() - Get the IEEE-1284 device ID for a node...
+ * get_unit_type() - Get the unit type for a node...
+ * show_data() - Show a data node...
+ * show_dir() - Show a directory list...
+ * ieee1394_list() - List the available printer devices.
+ * ieee1394_open() - Open a printer device.
+ * ieee1394_close() - Close a printer device.
+ * ieee1394_read() - Read from a printer device.
+ * ieee1394_write() - Write data to a printer device.
+ * ieee1394_error() - Return the last error.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "ieee1394.h"
+#include
+#include
+#include
+
+
+/*
+ * Limits...
+ */
+
+#define MAX_NODES 100
+
+
+/*
+ * Structures...
+ */
+
+typedef struct
+{
+ char uri[HTTP_MAX_URI],/* URI for this node... */
+ description[128],/* Description of port */
+ make_model[128];/* Make and model */
+ int port, /* Port where this node is found */
+ node; /* Node number */
+ unsigned long long addr; /* Management address */
+} linux1394_node_t;
+
+typedef struct
+{
+ raw1394handle_t handle; /* Handle for printer device */
+ int node; /* Node number for printer device */
+ unsigned long long addr; /* Management address */
+} linux1394_dev_t;
+
+
+/*
+ * ORB messages for communication with the device...
+ */
+
+typedef struct /**** Login ORB Message */
+{
+ unsigned char passwd_addr[8]; /* Password address */
+ unsigned char resp_addr[8]; /* Login response address */
+ unsigned char notify_excl; /* Notify and exclusive bits */
+ unsigned char recon_func; /* Reconnect time and function */
+ unsigned char lun[2]; /* Logical unit number */
+ unsigned char passwd_len[2]; /* Length of password */
+ unsigned char resp_len[2]; /* Length of login response */
+ unsigned char fifo_addr[8]; /* Local status FIFO address */
+} login_orb_t;
+
+typedef struct /**** Login Response Message ****/
+{
+ unsigned char length[2]; /* Length of response */
+ unsigned char login_id[2]; /* Login ID */
+ unsigned char cmd_addr[8]; /* Command block agent address */
+ unsigned char reserved[2]; /* Reserved (0) */
+ unsigned char recon_hold[2]; /* Number of seconds to hold login */
+} login_resp_t;
+
+
+/*
+ * Local globals...
+ */
+
+static char error_string[1024] = "";
+static int num_nodes;
+static linux1394_node_t nodes[MAX_NODES];
+
+
+/*
+ * 'get_device_id()' - Get the IEEE-1284 device ID for a node...
+ */
+
+static char * /* O - Device ID */
+get_device_id(raw1394handle_t handle,/* I - Handle for device */
+ int node, /* I - Node number */
+ unsigned long long offset,/* I - Offset to directory */
+ char *id, /* O - ID string */
+ int idlen) /* I - Size of ID string */
+{
+ unsigned char data[1024], /* Data from ROM */
+ *dataptr; /* Pointer into data */
+ int length; /* Length of directory */
+ int datalen; /* Length of data */
+ unsigned long long dataoff; /* Offset of data */
+
+
+ DEBUG_printf(("get_device_id(handle = %p, node = %d, offset = %llx, id = %p, idlen = %d)\n",
+ handle, node, offset, id, idlen));
+
+ *id = '\0';
+
+ /*
+ * Read the directory length from the first quadlet...
+ */
+
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return (NULL);
+
+ offset += 4;
+
+ /*
+ * The length is in the upper 16 bits...
+ */
+
+ length = (data[0] << 8) | data[1];
+
+ DEBUG_printf((" length = %d\n", length));
+
+ /*
+ * Then read the directory, looking for unit directory or device tags...
+ */
+
+ while (length > 0)
+ {
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return (NULL);
+
+ DEBUG_printf((" data = %02X %02X %02X %02X\n", data[0], data[1],
+ data[2], data[3]));
+
+ if (data[0] == 0xd1)
+ {
+ /*
+ * Found the unit directory...
+ */
+
+ offset += ((((data[1] << 8) | data[2]) << 8) | data[3]) << 2;
+
+ return (get_device_id(handle, node, offset, id, idlen));
+ }
+ else if (data[0] == 0x81)
+ {
+ /*
+ * Found potential IEEE-1284 device ID...
+ */
+
+ dataoff = offset + (((((data[1] << 8) | data[2]) << 8) | data[3]) << 2);
+
+ if (raw1394_read(handle, 0xffc0 | node, dataoff, 4, (quadlet_t *)data) < 0)
+ return (NULL);
+
+ dataoff += 4;
+
+ /*
+ * Read the leaf value...
+ */
+
+ datalen = (data[0] << 8) | data[1];
+
+ if (datalen > (sizeof(data) / 4))
+ datalen = sizeof(data) / 4;
+
+ for (dataptr = data; datalen > 0; datalen --, dataptr += 4, dataoff += 4)
+ if (raw1394_read(handle, 0xffc0 | node, dataoff, 4,
+ (quadlet_t *)dataptr) < 0)
+ return (NULL);
+
+ if (data[0] == 0 && memcmp(data + 8, "MFG:", 4) == 0)
+ {
+ /*
+ * Found the device ID...
+ */
+
+ datalen = dataptr - data - 8;
+ if (datalen >= idlen)
+ datalen --;
+
+ memcpy(id, data + 8, datalen);
+ id[datalen] = '\0';
+
+ return (id);
+ }
+ }
+
+ offset += 4;
+ length --;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'get_man_addr()' - Get the management address for a node...
+ */
+
+static int /* O - Unit type */
+get_man_addr(raw1394handle_t handle, /* I - Handle for device */
+ int node, /* I - Node number */
+ unsigned long long offset) /* I - Offset to directory */
+{
+ unsigned char data[4]; /* Data from ROM */
+ int length; /* Length of directory */
+
+
+ DEBUG_printf(("get_man_addr(handle = %p, node = %d, offset = %llx)\n",
+ handle, node, offset));
+
+ /*
+ * Read the directory length from the first quadlet...
+ */
+
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return (-1);
+
+ offset += 4;
+
+ /*
+ * The length is in the upper 16 bits...
+ */
+
+ length = (data[0] << 8) | data[1];
+
+ DEBUG_printf((" length = %d\n", length));
+
+ /*
+ * Then read the directory, looking for unit directory or type tags...
+ */
+
+ while (length > 0)
+ {
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return (-1);
+
+ DEBUG_printf((" data = %02X %02X %02X %02X\n", data[0], data[1],
+ data[2], data[3]));
+
+ if (data[0] == 0xd1)
+ {
+ /*
+ * Found the unit directory...
+ */
+
+ offset += ((((data[1] << 8) | data[2]) << 8) | data[3]) << 2;
+
+ return (get_man_addr(handle, node, offset));
+ }
+ else if (data[0] == 0x54)
+ {
+ /*
+ * Found the management address...
+ */
+
+ return (((((data[1] << 8) | data[2]) << 8) | data[3]) << 2);
+ }
+
+ offset += 4;
+ length --;
+ }
+
+ return (-1);
+}
+
+
+/*
+ * 'get_unit_type()' - Get the unit type for a node...
+ */
+
+static int /* O - Unit type */
+get_unit_type(raw1394handle_t handle,/* I - Handle for device */
+ int node, /* I - Node number */
+ unsigned long long offset)/* I - Offset to directory */
+{
+ unsigned char data[4]; /* Data from ROM */
+ int length; /* Length of directory */
+
+
+ DEBUG_printf(("get_unit_type(handle = %p, node = %d, offset = %llx)\n",
+ handle, node, offset));
+
+ /*
+ * Read the directory length from the first quadlet...
+ */
+
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return (-1);
+
+ offset += 4;
+
+ /*
+ * The length is in the upper 16 bits...
+ */
+
+ length = (data[0] << 8) | data[1];
+
+ DEBUG_printf((" length = %d\n", length));
+
+ /*
+ * Then read the directory, looking for unit directory or type tags...
+ */
+
+ while (length > 0)
+ {
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return (-1);
+
+ DEBUG_printf((" data = %02X %02X %02X %02X\n", data[0], data[1],
+ data[2], data[3]));
+
+ if (data[0] == 0xd1)
+ {
+ /*
+ * Found the unit directory...
+ */
+
+ offset += ((((data[1] << 8) | data[2]) << 8) | data[3]) << 2;
+
+ return (get_unit_type(handle, node, offset));
+ }
+ else if (data[0] == 0x14)
+ {
+ /*
+ * Found the unit type...
+ */
+
+ return (data[1] & 0x1f);
+ }
+
+ offset += 4;
+ length --;
+ }
+
+ return (-1);
+}
+
+
+#ifdef DEBUG
+/*
+ * 'show_data()' - Show a data node...
+ */
+
+static void
+show_data(raw1394handle_t handle, /* I - Handle for device */
+ int node, /* I - Node number */
+ unsigned long long offset, /* I - Offset to directory */
+ int indent) /* Amount to indent */
+{
+ int i; /* Looping var */
+ unsigned char data[4]; /* Data from ROM */
+ int length; /* Length of data */
+
+
+ /*
+ * Read the data length from the first quadlet...
+ */
+
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return;
+
+ offset += 4;
+
+ /*
+ * The length is in the upper 16 bits...
+ */
+
+ length = (data[0] << 8) | data[1];
+
+ /*
+ * Then read the data...
+ */
+
+ for (i = 0; i < indent; i ++)
+ putchar(' ');
+
+ printf("LEAF (%d quadlets)\n", length);
+
+ while (length > 0)
+ {
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return;
+
+ for (i = 0; i < indent; i ++)
+ putchar(' ');
+
+ printf("%02X %02X %02X %02X '%c%c%c%c'\n",
+ data[0], data[1], data[2], data[3],
+ (data[0] < ' ' || data[0] >= 0x7f) ? '.' : data[0],
+ (data[1] < ' ' || data[1] >= 0x7f) ? '.' : data[1],
+ (data[2] < ' ' || data[2] >= 0x7f) ? '.' : data[2],
+ (data[3] < ' ' || data[3] >= 0x7f) ? '.' : data[3]);
+
+ offset += 4;
+ length --;
+ }
+}
+
+
+/*
+ * 'show_dir()' - Show a directory list...
+ */
+
+static void
+show_dir(raw1394handle_t handle, /* I - Handle for device */
+ int node, /* I - Node number */
+ unsigned long long offset, /* I - Offset to directory */
+ int indent) /* Amount to indent */
+{
+ int i; /* Looping var */
+ unsigned char data[4]; /* Data from ROM */
+ int length; /* Length of directory */
+ int value; /* Value in directory */
+
+
+ /*
+ * Read the directory length from the first quadlet...
+ */
+
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return;
+
+ offset += 4;
+
+ /*
+ * The length is in the upper 16 bits...
+ */
+
+ length = (data[0] << 8) | data[1];
+
+ /*
+ * Then read the directory...
+ */
+
+ while (length > 0)
+ {
+ if (raw1394_read(handle, 0xffc0 | node, offset, 4, (quadlet_t *)data) < 0)
+ return;
+
+ for (i = 0; i < indent; i ++)
+ putchar(' ');
+
+ printf("%02X %02X %02X %02X\n", data[0], data[1], data[2], data[3]);
+
+ value = (((data[1] << 8) | data[2]) << 8) | data[3];
+
+ switch (data[0] & 0xc0)
+ {
+ case 0x00 :
+ for (i = -4; i < indent; i ++)
+ putchar(' ');
+
+ printf("IMMEDIATE %d\n", value);
+ break;
+
+ case 0x40 :
+ for (i = -4; i < indent; i ++)
+ putchar(' ');
+
+ printf("CSR OFFSET +%06X\n", value);
+ break;
+
+ case 0x80 :
+ show_data(handle, node, offset + value * 4, indent + 4);
+ break;
+
+ case 0xc0 :
+ show_dir(handle, node, offset + value * 4, indent + 4);
+ break;
+ }
+
+ offset += 4;
+ length --;
+ }
+}
+#endif /* DEBUG */
+
+
+/*
+ * 'ieee1394_list()' - List the available printer devices.
+ */
+
+ieee1394_info_t * /* O - Printer information */
+ieee1394_list(int *num_devices) /* O - Number of printers */
+{
+ int i, j; /* Looping vars */
+ raw1394handle_t handle; /* 1394 handle */
+ int num_ports; /* Number of ports */
+ struct raw1394_portinfo ports[100]; /* Port data... */
+ unsigned char guid[8]; /* Global unique ID */
+ int vendor; /* Vendor portion of GUID */
+ int unit_type; /* Unit type */
+ int addr; /* Management address offset */
+ char id[1024], /* Device ID string */
+ *idptr, /* Pointer into ID string */
+ *idsep; /* Pointer to separator */
+ ieee1394_info_t *devices; /* Device list */
+
+
+ /*
+ * Connect to the user-mode driver interface...
+ */
+
+ handle = raw1394_new_handle();
+ num_ports = raw1394_get_port_info(handle, ports,
+ sizeof(ports) / sizeof(ports[0]));
+
+ DEBUG_printf(("num_ports = %d\n", num_ports));
+
+ /*
+ * Loop through the ports to discover what nodes are available.
+ */
+
+ num_nodes = 0;
+
+ for (i = 0; i < num_ports; i ++)
+ {
+ DEBUG_printf(("ports[%d] = { nodes = %d, name = \"%s\" }\n", i,
+ ports[i].nodes, ports[i].name));
+
+ raw1394_set_port(handle, i);
+
+ for (j = 0; j < ports[i].nodes; j ++)
+ {
+ if (raw1394_read(handle, 0xffc0 | j,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM + 12, 4,
+ (quadlet_t *)guid) < 0)
+ {
+ DEBUG_printf((" Node #%d: Unable to contact (%s)!\n", j,
+ strerror(errno)));
+ continue;
+ }
+ else
+ {
+ raw1394_read(handle, 0xffc0 | j,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM + 16, 4,
+ (quadlet_t *)(guid + 4));
+
+ DEBUG_printf((" Node #%d: GUID = %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ j, guid[0], guid[1], guid[2], guid[3], guid[4],
+ guid[5], guid[6], guid[7]));
+
+ vendor = (((guid[0] << 8) | guid[1]) << 8) | guid[2];
+ unit_type = get_unit_type(handle, j,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM + 20);
+
+ DEBUG_printf(("vendor = %x, unit_type = %d\n", vendor, unit_type));
+
+ if (unit_type == 2 && num_nodes < MAX_NODES)
+ {
+ /*
+ * Found a printer device; add it to the nodes list...
+ */
+
+#ifdef DEBUG
+ show_dir(handle, j, CSR_REGISTER_BASE + CSR_CONFIG_ROM + 20, 0);
+#endif /* DEBUG */
+
+ memset(nodes + num_nodes, 0, sizeof(linux1394_node_t));
+
+ sprintf(nodes[num_nodes].uri, "ieee1394://%02X%02X%02X%02X%02X%02X%02X%02X",
+ guid[0], guid[1], guid[2], guid[3], guid[4],
+ guid[5], guid[6], guid[7]);
+
+ nodes[num_nodes].port = i;
+ nodes[num_nodes].node = j;
+
+ addr = get_man_addr(handle, j, CSR_REGISTER_BASE + CSR_CONFIG_ROM + 20);
+
+ if (addr < 0)
+ continue;
+
+ nodes[num_nodes].addr = CSR_REGISTER_BASE + addr;
+
+ DEBUG_printf(("Node address = %llx\n", nodes[num_nodes].addr));
+
+ get_device_id(handle, j, CSR_REGISTER_BASE + CSR_CONFIG_ROM + 20,
+ id, sizeof(id));
+
+ if (id[0])
+ {
+ /*
+ * Grab the manufacturer and model name from the device ID
+ * string...
+ */
+
+ idptr = id + 4;
+ idsep = strchr(id, ';');
+ if (idsep)
+ *idsep++ = '\0';
+ else
+ idsep = idptr;
+
+ snprintf(nodes[num_nodes].description,
+ sizeof(nodes[num_nodes].description),
+ "%s Firewire Printer", idptr);
+
+ if ((idptr = strstr(idsep, "DES:")) == NULL)
+ idptr = strstr(idsep, "MDL:");
+
+ if (idptr == NULL)
+ strcpy(nodes[num_nodes].make_model, "Unknown");
+ else
+ {
+ /*
+ * Grab the DES or MDL code...
+ */
+
+ idptr += 4;
+ idsep = strchr(idptr, ';');
+ if (idsep)
+ *idsep = '\0';
+
+ if (strncmp(id + 4, idptr, strlen(id + 4)) == 0)
+ {
+ /*
+ * Use the description directly...
+ */
+
+ strlcpy(nodes[num_nodes].make_model, idptr,
+ sizeof(nodes[num_nodes].make_model));
+ }
+ else
+ {
+ /*
+ * Add the manufacturer to the front of the name...
+ */
+
+ snprintf(nodes[num_nodes].make_model,
+ sizeof(nodes[num_nodes].make_model),
+ "%s %s", id + 4, idptr);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Flag it as an unknown printer...
+ */
+
+ sprintf(nodes[num_nodes].description,
+ "Unknown%06X Firewire Printer", vendor);
+ strcpy(nodes[num_nodes].make_model, "Unknown");
+ }
+
+ num_nodes ++;
+ }
+ }
+ }
+ }
+
+ /*
+ * Done querying the Firewire bus...
+ */
+
+ raw1394_destroy_handle(handle);
+
+ /*
+ * Build an array of device info structures as needed...
+ */
+
+ if (num_devices == NULL)
+ return (NULL);
+
+ *num_devices = num_nodes;
+
+ if (num_nodes)
+ {
+ if ((devices = calloc(sizeof(ieee1394_info_t), num_nodes)) != NULL)
+ {
+ for (i = 0; i < num_nodes; i ++)
+ {
+ strcpy(devices[i].uri, nodes[i].uri);
+ strcpy(devices[i].description, nodes[i].description);
+ strcpy(devices[i].make_model, nodes[i].make_model);
+ }
+ }
+
+ return (devices);
+ }
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'ieee1394_open()' - Open a printer device.
+ */
+
+ieee1394_dev_t /* O - Printer device or NULL */
+ieee1394_open(const char *uri) /* I - Device URI */
+{
+ int i; /* Looping var */
+ linux1394_dev_t *ldev; /* Linux device */
+
+
+ /*
+ * Return early if we can't see any printers...
+ */
+
+ if (num_nodes == 0)
+ ieee1394_list(NULL);
+
+ if (num_nodes == 0)
+ {
+ strcpy(error_string, "No IEEE-1394 printers found!");
+ return (NULL);
+ }
+
+ /*
+ * Look for the URI...
+ */
+
+ for (i = 0; i < num_nodes; i ++)
+ if (strcmp(nodes[i].uri, uri) == 0)
+ break;
+
+ if (i >= num_nodes)
+ {
+ snprintf(error_string, sizeof(error_string), "Device %s not found!", uri);
+ return (NULL);
+ }
+
+ /*
+ * Now create a new device structure...
+ */
+
+ if ((ldev = calloc(sizeof(linux1394_dev_t), 1)) == NULL)
+ {
+ strcpy(error_string, "Out of memory!");
+ return (NULL);
+ }
+
+ ldev->handle = raw1394_new_handle();
+ ldev->node = nodes[i].node;
+ ldev->addr = nodes[i].addr;
+
+ raw1394_set_port(ldev->handle, nodes[i].port);
+
+ error_string[0] = '\0';
+
+ return ((ieee1394_dev_t)ldev);
+}
+
+
+/*
+ * 'ieee1394_close()' - Close a printer device.
+ */
+
+int /* O - 0 on success, -1 on failure */
+ieee1394_close(ieee1394_dev_t dev) /* I - Printer device */
+{
+ linux1394_dev_t *ldev; /* Linux device */
+
+
+ ldev = (linux1394_dev_t *)dev;
+
+ raw1394_destroy_handle(ldev->handle);
+
+ free(ldev);
+
+ return (0);
+}
+
+
+/*
+ * 'ieee1394_read()' - Read from a printer device.
+ */
+
+int /* O - Number of bytes read or -1 */
+ieee1394_read(ieee1394_dev_t dev, /* I - Printer device */
+ char *buffer, /* I - Read buffer */
+ int len) /* I - Max bytes to read */
+{
+ linux1394_dev_t *ldev; /* Linux device */
+
+
+ ldev = (linux1394_dev_t *)dev;
+
+
+ return (0);
+}
+
+
+/*
+ * 'ieee1394_write()' - Write data to a printer device.
+ */
+
+int /* O - Number of bytes written or -1 */
+ieee1394_write(ieee1394_dev_t dev, /* I - Printer device */
+ char *buffer, /* I - Buffer to write */
+ int len) /* I - Number of bytes to write */
+{
+ linux1394_dev_t *ldev; /* Linux device */
+
+
+ ldev = (linux1394_dev_t *)dev;
+
+
+/* if (raw1394_write(handle, 0xffc0 | j, 0, ,
+ (quadlet_t *)guid) < 0)*/
+
+ return (len);
+}
+
+
+/*
+ * 'ieee1394_error()' - Return the last error.
+ */
+
+const char * /* O - Error string or NULL */
+ieee1394_error(void)
+{
+ if (error_string[0])
+ return (error_string);
+ else
+ return (NULL);
+}
+
+
+/*
+ * End of "$Id: ieee1394-linux.c 4703 2005-09-26 19:33:58Z mike $".
+ */
diff --git a/backend/ieee1394.c b/backend/ieee1394.c
new file mode 100644
index 000000000..0be210581
--- /dev/null
+++ b/backend/ieee1394.c
@@ -0,0 +1,263 @@
+/*
+ * "$Id: ieee1394.c 4494 2005-02-18 02:18:11Z mike $"
+ *
+ * IEEE-1394 backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2002 by Easy Software Products, all rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use
+ * of this software must display the following
+ * acknowledgement:
+ *
+ * This product includes software developed by Easy
+ * Software Products.
+ *
+ * 4. The name of Easy Software Products may not be used to
+ * endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer.
+ * list_devices() - List all known printer devices...
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "ieee1394.h"
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+
+
+/*
+ * 'main()' - Send a file to the printer.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ ieee1394_dev_t dev; /* Printer device */
+ int fp; /* Print file */
+ int copies; /* Number of copies to print */
+ int rbytes; /* Number of bytes read from device */
+ size_t nbytes, /* Number of bytes read from file */
+ tbytes; /* Total number of bytes written */
+ char buffer[8192]; /* Input/output buffer */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ list_devices();
+
+ return (0);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
+ argv[0]);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ {
+ fp = 0;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = open(argv[6], O_RDONLY)) < 0)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * Try to open the printer device...
+ */
+
+ do
+ {
+ if ((dev = ieee1394_open(argv[0])) == NULL)
+ {
+ fputs("INFO: Firewire printer busy; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ }
+ while (dev == NULL);
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (argc < 7)
+ {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ while (copies > 0)
+ {
+ copies --;
+
+ if (fp != 0)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ lseek(fp, 0, SEEK_SET);
+ }
+
+ tbytes = 0;
+ while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ tbytes += nbytes;
+
+ if (ieee1394_write(dev, buffer, nbytes) < 0)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+
+ if ((rbytes = ieee1394_read(dev, buffer, sizeof(buffer))) > 0)
+ fprintf(stderr, "INFO: Read %d bytes from printer...\n", rbytes);
+
+ if (argc > 6)
+ fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
+ (unsigned long)tbytes);
+ }
+ }
+
+ /*
+ * Close the printer device and input file and return...
+ */
+
+ ieee1394_close(dev);
+
+ if (fp != 0)
+ close(fp);
+
+ fputs("INFO: Ready to print.\n", stderr);
+
+ return (0);
+}
+
+
+/*
+ * 'list_devices()' - List all known devices...
+ */
+
+void
+list_devices(void)
+{
+ int i, /* Looping var */
+ num_info; /* Number of devices */
+ ieee1394_info_t *info; /* Devices... */
+
+
+ /*
+ * Get the available devices...
+ */
+
+ info = ieee1394_list(&num_info);
+
+ /*
+ * List them as needed...
+ */
+
+ if (num_info > 0)
+ {
+ for (i = 0; i < num_info; i ++)
+ printf("direct %s \"%s\" \"%s\"\n", info[i].uri,
+ info[i].make_model, info[i].description);
+
+ free(info);
+ }
+}
+
+
+/*
+ * End of "$Id: ieee1394.c 4494 2005-02-18 02:18:11Z mike $".
+ */
diff --git a/backend/ieee1394.h b/backend/ieee1394.h
new file mode 100644
index 000000000..19181c5c0
--- /dev/null
+++ b/backend/ieee1394.h
@@ -0,0 +1,103 @@
+/*
+ * "$Id: ieee1394.h 4494 2005-02-18 02:18:11Z mike $"
+ *
+ * IEEE-1394 header for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2002 by Easy Software Products, all rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use
+ * of this software must display the following
+ * acknowledgement:
+ *
+ * This product includes software developed by Easy
+ * Software Products.
+ *
+ * 4. The name of Easy Software Products may not be used to
+ * endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef WIN32
+# include
+#else
+# include
+# include
+#endif /* WIN32 */
+
+
+/*
+ * Device information structure...
+ */
+
+typedef struct
+{
+ char uri[HTTP_MAX_URI], /* Device URI */
+ description[128], /* Description of port */
+ make_model[128]; /* Make and model */
+} ieee1394_info_t;
+
+
+/*
+ * Private device connection information...
+ */
+
+typedef void *ieee1394_dev_t;
+
+
+/*
+ * Prototypes for standard IEEE-1394 interface...
+ */
+
+extern ieee1394_info_t *ieee1394_list(int *num_devices);
+extern ieee1394_dev_t ieee1394_open(const char *uri);
+extern int ieee1394_close(ieee1394_dev_t dev);
+extern int ieee1394_read(ieee1394_dev_t dev, char *buffer, int len);
+extern int ieee1394_write(ieee1394_dev_t dev, char *buffer, int len);
+extern const char *ieee1394_error(void);
+
+
+/*
+ * End of "$Id: ieee1394.h 4494 2005-02-18 02:18:11Z mike $".
+ */
diff --git a/backend/ipp.c b/backend/ipp.c
new file mode 100644
index 000000000..47cee2b91
--- /dev/null
+++ b/backend/ipp.c
@@ -0,0 +1,1468 @@
+/*
+ * "$Id: ipp.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * IPP backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ * check_printer_state() - Check the printer state...
+ * password_cb() - Disable the password prompt for
+ * cupsDoFileRequest().
+ * report_printer_state() - Report the printer state.
+ * run_pictwps_filter() - Convert PICT files to PostScript when printing
+ * remotely.
+ * sigterm_handler() - Handle 'terminate' signals that stop the backend.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/*
+ * Globals...
+ */
+
+static char *password = NULL; /* Password for device URI */
+#ifdef __APPLE__
+static char pstmpname[1024] = ""; /* Temporary PostScript file name */
+#endif /* __APPLE__ */
+static char tmpfilename[1024] = ""; /* Temporary spool file name */
+
+
+/*
+ * Local functions...
+ */
+
+void check_printer_state(http_t *http, cups_lang_t *language,
+ const char *charset, const char *uri, /* I - Printer URI */
+ const char *resource, const char *user,
+ int version);
+const char *password_cb(const char *);
+int report_printer_state(ipp_t *ipp);
+
+#ifdef __APPLE__
+int run_pictwps_filter(char **argv, const char *filename);
+#endif /* __APPLE__ */
+static void sigterm_handler(int sig);
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int num_options; /* Number of printer options */
+ cups_option_t *options; /* Printer options */
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info */
+ resource[1024], /* Resource info (printer name) */
+ *optptr, /* Pointer to URI options */
+ name[255], /* Name of option */
+ value[255], /* Value of option */
+ *ptr; /* Pointer into name or value */
+ char *filename; /* File to print */
+ int port; /* Port number (not used) */
+ char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
+ ipp_status_t ipp_status; /* Status of IPP request */
+ http_t *http; /* HTTP connection */
+ ipp_t *request, /* IPP request */
+ *response, /* IPP response */
+ *supported; /* get-printer-attributes response */
+ int waitjob, /* Wait for job complete? */
+ waitprinter; /* Wait for printer ready? */
+ ipp_attribute_t *job_id_attr; /* job-id attribute */
+ int job_id; /* job-id value */
+ ipp_attribute_t *job_sheets; /* job-media-sheets-completed attribute */
+ ipp_attribute_t *job_state; /* job-state attribute */
+ ipp_attribute_t *copies_sup; /* copies-supported attribute */
+ ipp_attribute_t *charset_sup; /* charset-supported attribute */
+ ipp_attribute_t *format_sup; /* document-format-supported attribute */
+ ipp_attribute_t *printer_state; /* printer-state attribute */
+ ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs attribute */
+ const char *charset; /* Character set to use */
+ cups_lang_t *language; /* Default language */
+ int copies; /* Number of copies remaining */
+ const char *content_type; /* CONTENT_TYPE environment variable */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+ int version; /* IPP version */
+ int reasons; /* Number of printer-state-reasons shown */
+ static const char * const pattrs[] =
+ { /* Printer attributes we want */
+ "copies-supported",
+ "charset-supported",
+ "document-format-supported",
+ "printer-is-accepting-jobs",
+ "printer-state",
+ "printer-state-reasons",
+ };
+ static const char * const jattrs[] =
+ { /* Job attributes we want */
+ "job-media-sheets-completed",
+ "job-state"
+ };
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore SIGPIPE and catch SIGTERM signals...
+ */
+
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+ sigset(SIGTERM, sigterm_handler);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGTERM);
+ action.sa_handler = sigterm_handler;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGTERM, sigterm_handler);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ char *s;
+
+ if ((s = strrchr(argv[0], '/')) != NULL)
+ s ++;
+ else
+ s = argv[0];
+
+ printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n", s, s);
+ return (CUPS_BACKEND_OK);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
+ argv[0]);
+ return (CUPS_BACKEND_STOP);
+ }
+
+ /*
+ * Get the content type...
+ */
+
+ if (argc > 6)
+ content_type = getenv("CONTENT_TYPE");
+ else
+ content_type = "application/vnd.cups-raw";
+
+ if (content_type == NULL)
+ content_type = "application/octet-stream";
+
+ /*
+ * Extract the hostname and printer name from the URI...
+ */
+
+ if (getenv("DEVICE_URI") != NULL)
+ /* authentication information is only available in the env var */
+ httpSeparateURI(getenv("DEVICE_URI"), method, sizeof(method),
+ username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+ else if (strchr(argv[0], ':') != NULL)
+ httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+ else
+ {
+ fputs("ERROR: Missing device URI on command-line and no DEVICE_URI environment variable!\n",
+ stderr);
+ return (CUPS_BACKEND_STOP);
+ }
+
+ if (!strcmp(method, "https"))
+ cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, copy stdin to a temporary file and print the temporary
+ * file.
+ */
+
+ if (argc == 6)
+ {
+ /*
+ * Copy stdin to a temporary file...
+ */
+
+ int fd; /* Temporary file */
+ char buffer[8192]; /* Buffer for copying */
+ int bytes; /* Number of bytes read */
+
+
+ if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
+ {
+ perror("ERROR: unable to create temporary file");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
+ if (write(fd, buffer, bytes) < bytes)
+ {
+ perror("ERROR: unable to write to temporary file");
+ close(fd);
+ unlink(tmpfilename);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ close(fd);
+ filename = tmpfilename;
+ }
+ else
+ filename = argv[6];
+
+ /*
+ * See if there are any options...
+ */
+
+ version = 1;
+ waitjob = 1;
+ waitprinter = 1;
+
+ if ((optptr = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the optptr...
+ */
+
+ *optptr++ = '\0';
+
+ /*
+ * Then parse the optptr...
+ */
+
+ while (*optptr)
+ {
+ /*
+ * Get the name...
+ */
+
+ for (ptr = name; *optptr && *optptr != '=';)
+ if (ptr < (name + sizeof(name) - 1))
+ *ptr++ = *optptr++;
+ *ptr = '\0';
+
+ if (*optptr == '=')
+ {
+ /*
+ * Get the value...
+ */
+
+ optptr ++;
+
+ for (ptr = value; *optptr && *optptr != '+' && *optptr != '&';)
+ if (ptr < (value + sizeof(value) - 1))
+ *ptr++ = *optptr++;
+ *ptr = '\0';
+
+ if (*optptr == '+')
+ optptr ++;
+ }
+ else
+ value[0] = '\0';
+
+ /*
+ * Process the option...
+ */
+
+ if (!strcasecmp(name, "waitjob"))
+ {
+ /*
+ * Wait for job completion?
+ */
+
+ waitjob = !strcasecmp(value, "on") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "true");
+ }
+ else if (!strcasecmp(name, "waitprinter"))
+ {
+ /*
+ * Wait for printer idle?
+ */
+
+ waitprinter = !strcasecmp(value, "on") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "true");
+ }
+ else if (!strcasecmp(name, "encryption"))
+ {
+ /*
+ * Enable/disable encryption?
+ */
+
+ if (!strcasecmp(value, "always"))
+ cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
+ else if (!strcasecmp(value, "required"))
+ cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
+ else if (!strcasecmp(value, "never"))
+ cupsSetEncryption(HTTP_ENCRYPT_NEVER);
+ else if (!strcasecmp(value, "ifrequested"))
+ cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
+ else
+ {
+ fprintf(stderr, "ERROR: Unknown encryption option value \"%s\"!\n",
+ value);
+ }
+ }
+ else if (!strcasecmp(name, "version"))
+ {
+ if (!strcmp(value, "1.0"))
+ version = 0;
+ else if (!strcmp(value, "1.1"))
+ version = 1;
+ else
+ {
+ fprintf(stderr, "ERROR: Unknown version option value \"%s\"!\n",
+ value);
+ }
+ }
+ else
+ {
+ /*
+ * Unknown option...
+ */
+
+ fprintf(stderr, "ERROR: Unknown option \"%s\" with value \"%s\"!\n",
+ name, value);
+ }
+ }
+ }
+
+ /*
+ * Set the authentication info, if any...
+ */
+
+ cupsSetPasswordCB(password_cb);
+
+ if (username[0])
+ {
+ /*
+ * Use authenticaion information in the device URI...
+ */
+
+ if ((password = strchr(username, ':')) != NULL)
+ *password++ = '\0';
+
+ cupsSetUser(username);
+ }
+ else if (!getuid())
+ {
+ /*
+ * Try loading authentication information from the a##### file.
+ */
+
+ const char *request_root; /* CUPS_REQUESTROOT env var */
+ char afilename[1024], /* a##### filename */
+ aline[1024]; /* Line from file */
+ FILE *fp; /* File pointer */
+
+
+ if ((request_root = getenv("CUPS_REQUESTROOT")) != NULL)
+ {
+ /*
+ * Try opening authentication cache file...
+ */
+
+ snprintf(afilename, sizeof(afilename), "%s/a%05d", request_root,
+ atoi(argv[1]));
+ if ((fp = fopen(afilename, "r")) != NULL)
+ {
+ /*
+ * Read username...
+ */
+
+ if (fgets(aline, sizeof(aline), fp))
+ {
+ /*
+ * Decode username...
+ */
+
+ i = sizeof(username);
+ httpDecode64_2(username, &i, aline);
+
+ /*
+ * Read password...
+ */
+
+ if (fgets(aline, sizeof(aline), fp))
+ {
+ /*
+ * Decode password...
+ */
+
+ i = sizeof(password);
+ httpDecode64_2(password, &i, aline);
+ }
+ }
+
+ /*
+ * Close the file...
+ */
+
+ fclose(fp);
+ }
+ }
+ }
+
+ /*
+ * Try connecting to the remote server...
+ */
+
+ do
+ {
+ fprintf(stderr, "INFO: Connecting to %s on port %d...\n", hostname, port);
+
+ if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
+ {
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fprintf(stderr, "INFO: Unable to connect to %s, queuing on next printer in class...\n",
+ hostname);
+
+ if (argc == 6 || strcmp(filename, argv[6]))
+ unlink(filename);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
+ errno == EHOSTUNREACH)
+ {
+ fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...\n",
+ hostname);
+ sleep(30);
+ }
+ else if (h_errno)
+ {
+ fprintf(stderr, "INFO: Unable to lookup host \'%s\' - %s\n",
+ hostname, hstrerror(h_errno));
+ sleep(30);
+ }
+ else
+ {
+ perror("ERROR: Unable to connect to IPP host");
+ sleep(30);
+ }
+ }
+ }
+ while (http == NULL);
+
+ fprintf(stderr, "INFO: Connected to %s...\n", hostname);
+
+ /*
+ * Build a URI for the printer and fill the standard IPP attributes for
+ * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
+ * might contain username:password information...
+ */
+
+ snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
+
+ /*
+ * First validate the destination and see if the device supports multiple
+ * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
+ * don't support the copies attribute...
+ */
+
+ language = cupsLangDefault();
+ charset_sup = NULL;
+ copies_sup = NULL;
+ format_sup = NULL;
+ supported = NULL;
+
+ do
+ {
+ /*
+ * Build the IPP request...
+ */
+
+ request = ippNew();
+ request->request.op.version[1] = version;
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, "utf-8");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language->language : "en");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
+ NULL, pattrs);
+
+ /*
+ * Do the request...
+ */
+
+ fputs("DEBUG: Getting supported attributes...\n", stderr);
+
+ if ((supported = cupsDoRequest(http, request, resource)) == NULL)
+ ipp_status = cupsLastError();
+ else
+ ipp_status = supported->request.status.status_code;
+
+ if (ipp_status > IPP_OK_CONFLICT)
+ {
+ if (ipp_status == IPP_PRINTER_BUSY ||
+ ipp_status == IPP_SERVICE_UNAVAILABLE)
+ {
+ fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
+ report_printer_state(supported);
+ sleep(10);
+ }
+ else if ((ipp_status == IPP_BAD_REQUEST ||
+ ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
+ {
+ /*
+ * Switch to IPP/1.0...
+ */
+
+ fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr);
+ version = 0;
+ httpReconnect(http);
+ }
+ else if (ipp_status == IPP_NOT_FOUND)
+ {
+ fputs("ERROR: Destination printer does not exist!\n", stderr);
+
+ if (supported)
+ ippDelete(supported);
+
+ return (CUPS_BACKEND_STOP);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to get printer status (%s)!\n",
+ ippErrorString(ipp_status));
+ sleep(10);
+ }
+
+ if (supported)
+ ippDelete(supported);
+
+ continue;
+ }
+ else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
+ IPP_TAG_RANGE)) != NULL)
+ {
+ /*
+ * Has the "copies-supported" attribute - does it have an upper
+ * bound > 1?
+ */
+
+ if (copies_sup->values[0].range.upper <= 1)
+ copies_sup = NULL; /* No */
+ }
+
+ charset_sup = ippFindAttribute(supported, "charset-supported",
+ IPP_TAG_CHARSET);
+ format_sup = ippFindAttribute(supported, "document-format-supported",
+ IPP_TAG_MIMETYPE);
+
+ if (format_sup)
+ {
+ fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
+ format_sup->num_values);
+ for (i = 0; i < format_sup->num_values; i ++)
+ fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
+ format_sup->values[i].string.text);
+ }
+
+ report_printer_state(supported);
+ }
+ while (ipp_status > IPP_OK_CONFLICT);
+
+ /*
+ * See if the printer is accepting jobs and is not stopped; if either
+ * condition is true and we are printing to a class, requeue the job...
+ */
+
+ if (getenv("CLASS") != NULL)
+ {
+ printer_state = ippFindAttribute(supported, "printer-state",
+ IPP_TAG_ENUM);
+ printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
+ IPP_TAG_BOOLEAN);
+
+ if (printer_state == NULL ||
+ (printer_state->values[0].integer > IPP_PRINTER_PROCESSING && waitprinter) ||
+ printer_accepting == NULL ||
+ !printer_accepting->values[0].boolean)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fprintf(stderr, "INFO: Unable to queue job on %s, queuing on next printer in class...\n",
+ hostname);
+
+ ippDelete(supported);
+ httpClose(http);
+
+ if (argc == 6 || strcmp(filename, argv[6]))
+ unlink(filename);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+ }
+
+ /*
+ * See if the printer supports multiple copies...
+ */
+
+ if (copies_sup || argc < 7)
+ copies = 1;
+ else
+ copies = atoi(argv[4]);
+
+ /*
+ * Figure out the character set to use...
+ */
+
+ charset = language ? cupsLangEncoding(language) : "us-ascii";
+
+ if (charset_sup)
+ {
+ /*
+ * See if IPP server supports the requested character set...
+ */
+
+ for (i = 0; i < charset_sup->num_values; i ++)
+ if (strcasecmp(charset, charset_sup->values[i].string.text) == 0)
+ break;
+
+ /*
+ * If not, choose us-ascii or utf-8...
+ */
+
+ if (i >= charset_sup->num_values)
+ {
+ /*
+ * See if us-ascii is supported...
+ */
+
+ for (i = 0; i < charset_sup->num_values; i ++)
+ if (strcasecmp("us-ascii", charset_sup->values[i].string.text) == 0)
+ break;
+
+ if (i < charset_sup->num_values)
+ charset = "us-ascii";
+ else
+ charset = "utf-8";
+ }
+ }
+
+ /*
+ * Then issue the print-job request...
+ */
+
+ reasons = 0;
+
+ while (copies > 0)
+ {
+ /*
+ * Build the IPP request...
+ */
+
+ request = ippNew();
+ request->request.op.version[1] = version;
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, charset);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language->language : "en");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
+
+ if (argv[2][0])
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, argv[2]);
+
+ fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
+
+ if (argv[3][0])
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+ argv[3]);
+
+ fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
+
+ /*
+ * Handle options on the command-line...
+ */
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+#ifdef __APPLE__
+ if (content_type != NULL && strcasecmp(content_type, "application/pictwps") == 0)
+ {
+ if (format_sup != NULL)
+ {
+ for (i = 0; i < format_sup->num_values; i ++)
+ if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
+ break;
+ }
+
+ if (format_sup == NULL || i >= format_sup->num_values)
+ {
+ /*
+ * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
+ * so convert the document to PostScript...
+ */
+
+ if (run_pictwps_filter(argv, filename))
+ return (CUPS_BACKEND_FAILED);
+
+ filename = pstmpname;
+
+ /*
+ * Change the MIME type to application/postscript...
+ */
+
+ content_type = "application/postscript";
+ }
+ }
+#endif /* __APPLE__ */
+
+ if (content_type != NULL && format_sup != NULL)
+ {
+ for (i = 0; i < format_sup->num_values; i ++)
+ if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
+ break;
+
+ if (i < format_sup->num_values)
+ num_options = cupsAddOption("document-format", content_type,
+ num_options, &options);
+ }
+
+ if (copies_sup)
+ {
+ /*
+ * Only send options if the destination printer supports the copies
+ * attribute. This is a hack for the HP JetDirect implementation of
+ * IPP, which does not accept extension attributes and incorrectly
+ * reports a client-error-bad-request error instead of the
+ * successful-ok-unsupported-attributes status. In short, at least
+ * some HP implementations of IPP are non-compliant.
+ */
+
+ cupsEncodeOptions(request, num_options, options);
+ ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
+ atoi(argv[4]));
+ }
+
+ cupsFreeOptions(num_options, options);
+
+ /*
+ * If copies aren't supported, then we are likely dealing with an HP
+ * JetDirect. The HP IPP implementation seems to close the connection
+ * after every request (that is, it does *not* implement HTTP Keep-
+ * Alive, which is REQUIRED by HTTP/1.1...
+ */
+
+ if (!copies_sup)
+ httpReconnect(http);
+
+ /*
+ * Do the request...
+ */
+
+ if ((response = cupsDoFileRequest(http, request, resource, filename)) == NULL)
+ ipp_status = cupsLastError();
+ else
+ ipp_status = response->request.status.status_code;
+
+ if (ipp_status > IPP_OK_CONFLICT)
+ {
+ job_id = 0;
+
+ if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
+ ipp_status == IPP_PRINTER_BUSY)
+ {
+ fputs("INFO: Printer is busy; retrying print job...\n", stderr);
+ sleep(10);
+ }
+ else
+ fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
+ ippErrorString(ipp_status));
+ }
+ else if ((job_id_attr = ippFindAttribute(response, "job-id",
+ IPP_TAG_INTEGER)) == NULL)
+ {
+ fputs("NOTICE: Print file accepted - job ID unknown.\n", stderr);
+ job_id = 0;
+ }
+ else
+ {
+ job_id = job_id_attr->values[0].integer;
+ fprintf(stderr, "NOTICE: Print file accepted - job ID %d.\n", job_id);
+ }
+
+ if (response)
+ ippDelete(response);
+
+ if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
+ {
+ fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
+ copies --;
+ }
+ else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
+ ipp_status == IPP_PRINTER_BUSY)
+ break;
+ else
+ copies --;
+
+ /*
+ * Wait for the job to complete...
+ */
+
+ if (!job_id || !waitjob)
+ continue;
+
+ fputs("INFO: Waiting for job to complete...\n", stderr);
+
+ for (;;)
+ {
+ /*
+ * Build an IPP_GET_JOB_ATTRIBUTES request...
+ */
+
+ request = ippNew();
+ request->request.op.version[1] = version;
+ request->request.op.operation_id = IPP_GET_JOB_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, charset);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language->language : "en");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
+ job_id);
+
+ if (argv[2][0])
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, argv[2]);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
+ NULL, jattrs);
+
+ /*
+ * Do the request...
+ */
+
+ if (!copies_sup)
+ httpReconnect(http);
+
+ if ((response = cupsDoRequest(http, request, resource)) == NULL)
+ ipp_status = cupsLastError();
+ else
+ ipp_status = response->request.status.status_code;
+
+ if (ipp_status == IPP_NOT_FOUND)
+ {
+ /*
+ * Job has gone away and/or the server has no job history...
+ */
+
+ ippDelete(response);
+
+ ipp_status = IPP_OK;
+ break;
+ }
+
+ if (ipp_status > IPP_OK_CONFLICT)
+ {
+ if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
+ ipp_status != IPP_PRINTER_BUSY)
+ {
+ if (response)
+ ippDelete(response);
+
+ fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
+ job_id, ippErrorString(ipp_status));
+ break;
+ }
+ }
+
+ if (response != NULL)
+ {
+ if ((job_state = ippFindAttribute(response, "job-state",
+ IPP_TAG_ENUM)) != NULL)
+ {
+ /*
+ * Stop polling if the job is finished or pending-held...
+ */
+
+ if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
+ job_state->values[0].integer == IPP_JOB_HELD)
+ {
+ if ((job_sheets = ippFindAttribute(response, "job-media-sheets-completed",
+ IPP_TAG_INTEGER)) != NULL)
+ fprintf(stderr, "PAGE: total %d\n", job_sheets->values[0].integer);
+
+ ippDelete(response);
+ break;
+ }
+ }
+ }
+
+ if (response)
+ ippDelete(response);
+
+ /*
+ * Check the printer state and report it if necessary...
+ */
+
+/* if (!copies_sup)
+ httpReconnect(http);*/
+
+ check_printer_state(http, language, charset, uri, resource, argv[2],
+ version);
+
+ /*
+ * Wait 10 seconds before polling again...
+ */
+
+ sleep(10);
+ }
+ }
+
+ /*
+ * Check the printer state and report it if necessary...
+ */
+
+/* if (!copies_sup)
+ httpReconnect(http);*/
+
+ check_printer_state(http, language, charset, uri, resource, argv[2], version);
+
+ /*
+ * Free memory...
+ */
+
+ httpClose(http);
+
+ if (supported)
+ ippDelete(supported);
+
+ /*
+ * Remove the temporary file(s) if necessary...
+ */
+
+ if (tmpfilename[0])
+ unlink(tmpfilename);
+
+#ifdef __APPLE__
+ if (pstmpname[0])
+ unlink(pstmpname);
+#endif /* __APPLE__ */
+
+ /*
+ * Return the queue status...
+ */
+
+ return (ipp_status > IPP_OK_CONFLICT ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
+}
+
+
+/*
+ * 'check_printer_state()' - Check the printer state...
+ */
+
+void
+check_printer_state(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language,
+ /* I - Language */
+ const char *charset,
+ /* I - Charset */
+ const char *uri, /* I - Printer URI */
+ const char *resource,
+ /* I - Resource path */
+ const char *user, /* I - Username, if any */
+ int version)/* I - IPP version */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+
+
+ /*
+ * Check on the printer state...
+ */
+
+ request = ippNew();
+ request->request.op.version[1] = version;
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, charset);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language->language : "en");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ if (user && user[0])
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, user);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", NULL, "printer-state-reasons");
+
+ /*
+ * Do the request...
+ */
+
+ if ((response = cupsDoRequest(http, request, resource)) != NULL)
+ {
+ report_printer_state(response);
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
+ */
+
+const char * /* O - Password */
+password_cb(const char *prompt) /* I - Prompt (not used) */
+{
+ (void)prompt;
+
+ if (password)
+ return (password);
+ else
+ {
+ /*
+ * If there is no password set in the device URI, return the
+ * "authentication required" exit code...
+ */
+
+ if (tmpfilename[0])
+ unlink(tmpfilename);
+
+#ifdef __APPLE__
+ if (pstmpname[0])
+ unlink(pstmpname);
+#endif /* __APPLE__ */
+
+ exit(CUPS_BACKEND_AUTH_REQUIRED);
+ }
+}
+
+
+/*
+ * 'report_printer_state()' - Report the printer state.
+ */
+
+int /* O - Number of reasons shown */
+report_printer_state(ipp_t *ipp) /* I - IPP response */
+{
+ int i; /* Looping var */
+ int count; /* Count of reasons shown... */
+ ipp_attribute_t *reasons; /* printer-state-reasons */
+ const char *reason; /* Current reason */
+ const char *message; /* Message to show */
+ char unknown[1024]; /* Unknown message string */
+ const char *prefix; /* Prefix for STATE: line */
+ char state[1024]; /* State string */
+
+
+ if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
+ IPP_TAG_KEYWORD)) == NULL)
+ return (0);
+
+ state[0] = '\0';
+ prefix = "STATE: ";
+
+ for (i = 0, count = 0; i < reasons->num_values; i ++)
+ {
+ reason = reasons->values[i].string.text;
+
+ strlcat(state, prefix, sizeof(state));
+ strlcat(state, reason, sizeof(state));
+
+ prefix = ",";
+ message = NULL;
+
+ if (!strncmp(reason, "media-needed", 12))
+ message = "Media tray needs to be filled.";
+ else if (!strncmp(reason, "media-jam", 9))
+ message = "Media jam!";
+ else if (!strncmp(reason, "moving-to-paused", 16) ||
+ !strncmp(reason, "paused", 6) ||
+ !strncmp(reason, "shutdown", 8))
+ message = "Printer off-line.";
+ else if (!strncmp(reason, "toner-low", 9))
+ message = "Toner low.";
+ else if (!strncmp(reason, "toner-empty", 11))
+ message = "Out of toner!";
+ else if (!strncmp(reason, "cover-open", 10))
+ message = "Cover open.";
+ else if (!strncmp(reason, "interlock-open", 14))
+ message = "Interlock open.";
+ else if (!strncmp(reason, "door-open", 9))
+ message = "Door open.";
+ else if (!strncmp(reason, "input-tray-missing", 18))
+ message = "Media tray missing!";
+ else if (!strncmp(reason, "media-low", 9))
+ message = "Media tray almost empty.";
+ else if (!strncmp(reason, "media-empty", 11))
+ message = "Media tray empty!";
+ else if (!strncmp(reason, "output-tray-missing", 19))
+ message = "Output tray missing!";
+ else if (!strncmp(reason, "output-area-almost-full", 23))
+ message = "Output bin almost full.";
+ else if (!strncmp(reason, "output-area-full", 16))
+ message = "Output bin full!";
+ else if (!strncmp(reason, "marker-supply-low", 17))
+ message = "Ink/toner almost empty.";
+ else if (!strncmp(reason, "marker-supply-empty", 19))
+ message = "Ink/toner empty!";
+ else if (!strncmp(reason, "marker-waste-almost-full", 24))
+ message = "Ink/toner waste bin almost full.";
+ else if (!strncmp(reason, "marker-waste-full", 17))
+ message = "Ink/toner waste bin full!";
+ else if (!strncmp(reason, "fuser-over-temp", 15))
+ message = "Fuser temperature high!";
+ else if (!strncmp(reason, "fuser-under-temp", 16))
+ message = "Fuser temperature low!";
+ else if (!strncmp(reason, "opc-near-eol", 12))
+ message = "OPC almost at end-of-life.";
+ else if (!strncmp(reason, "opc-life-over", 13))
+ message = "OPC at end-of-life!";
+ else if (!strncmp(reason, "developer-low", 13))
+ message = "Developer almost empty.";
+ else if (!strncmp(reason, "developer-empty", 15))
+ message = "Developer empty!";
+ else if (strstr(reason, "error") != NULL)
+ {
+ message = unknown;
+
+ snprintf(unknown, sizeof(unknown), "Unknown printer error (%s)!",
+ reason);
+ }
+
+ if (message)
+ {
+ count ++;
+ if (strstr(reasons->values[i].string.text, "error"))
+ fprintf(stderr, "ERROR: %s\n", message);
+ else if (strstr(reasons->values[i].string.text, "warning"))
+ fprintf(stderr, "WARNING: %s\n", message);
+ else
+ fprintf(stderr, "INFO: %s\n", message);
+ }
+ }
+
+ fprintf(stderr, "%s\n", state);
+
+ return (count);
+}
+
+
+#ifdef __APPLE__
+/*
+ * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
+ * remotely.
+ *
+ * This step is required because the PICT format is not documented and
+ * subject to change, so developing a filter for other OS's is infeasible.
+ * Also, fonts required by the PICT file need to be embedded on the
+ * client side (which has the fonts), so we run the filter to get a
+ * PostScript file for printing...
+ */
+
+int /* O - Exit status of filter */
+run_pictwps_filter(char **argv, /* I - Command-line arguments */
+ const char *filename) /* I - Filename */
+{
+ struct stat fileinfo; /* Print file information */
+ const char *ppdfile; /* PPD file for destination printer */
+ int pid; /* Child process ID */
+ int fd; /* Temporary file descriptor */
+ int status; /* Exit status of filter */
+ const char *printer; /* PRINTER env var */
+ static char ppdenv[1024]; /* PPD environment variable */
+
+
+ /*
+ * First get the PPD file for the printer...
+ */
+
+ printer = getenv("PRINTER");
+ if (!printer)
+ {
+ fputs("ERROR: PRINTER environment variable not defined!\n", stderr);
+ return (-1);
+ }
+
+ if ((ppdfile = cupsGetPPD(printer)) == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
+ printer, ippErrorString(cupsLastError()));
+ /*return (-1);*/
+ }
+ else
+ {
+ snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
+ putenv(ppdenv);
+ }
+
+ /*
+ * Then create a temporary file for printing...
+ */
+
+ if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to create temporary file - %s.\n",
+ strerror(errno));
+ if (ppdfile)
+ unlink(ppdfile);
+ return (-1);
+ }
+
+ /*
+ * Get the owner of the spool file - it is owned by the user we want to run
+ * as...
+ */
+
+ if (argv[6])
+ stat(argv[6], &fileinfo);
+ else
+ {
+ /*
+ * Use the OSX defaults, as an up-stream filter created the PICT
+ * file...
+ */
+
+ fileinfo.st_uid = 1;
+ fileinfo.st_gid = 80;
+ }
+
+ if (ppdfile)
+ chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
+
+ fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
+
+ /*
+ * Finally, run the filter to convert the file...
+ */
+
+ if ((pid = fork()) == 0)
+ {
+ /*
+ * Child process for pictwpstops... Redirect output of pictwpstops to a
+ * file...
+ */
+
+ close(1);
+ dup(fd);
+ close(fd);
+
+ if (!getuid())
+ {
+ /*
+ * Change to an unpriviledged user...
+ */
+
+ setgid(fileinfo.st_gid);
+ setuid(fileinfo.st_uid);
+ }
+
+ execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
+ filename, NULL);
+ perror("ERROR: Unable to exec pictwpstops");
+ return (errno);
+ }
+
+ close(fd);
+
+ if (pid < 0)
+ {
+ /*
+ * Error!
+ */
+
+ perror("ERROR: Unable to fork pictwpstops");
+ unlink(filename);
+ if (ppdfile)
+ unlink(ppdfile);
+ return (-1);
+ }
+
+ /*
+ * Now wait for the filter to complete...
+ */
+
+ if (wait(&status) < 0)
+ {
+ perror("ERROR: Unable to wait for pictwpstops");
+ close(fd);
+ unlink(filename);
+ if (ppdfile)
+ unlink(ppdfile);
+ return (-1);
+ }
+
+ if (ppdfile)
+ unlink(ppdfile);
+
+ close(fd);
+
+ if (status)
+ {
+ if (status >= 256)
+ fprintf(stderr, "ERROR: pictwpstops exited with status %d!\n",
+ status / 256);
+ else
+ fprintf(stderr, "ERROR: pictwpstops exited on signal %d!\n",
+ status);
+
+ unlink(filename);
+ return (status);
+ }
+
+ /*
+ * Return with no errors..
+ */
+
+ return (0);
+}
+#endif /* __APPLE__ */
+
+
+/*
+ * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
+ */
+
+static void
+sigterm_handler(int sig) /* I - Signal */
+{
+ (void)sig; /* remove compiler warnings... */
+
+ /*
+ * Remove the temporary file(s) if necessary...
+ */
+
+ if (tmpfilename[0])
+ unlink(tmpfilename);
+
+#ifdef __APPLE__
+ if (pstmpname[0])
+ unlink(pstmpname);
+#endif /* __APPLE__ */
+
+ exit(1);
+}
+
+
+/*
+ * End of "$Id: ipp.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/backend/lpd.c b/backend/lpd.c
new file mode 100644
index 000000000..ee6d3deaa
--- /dev/null
+++ b/backend/lpd.c
@@ -0,0 +1,1149 @@
+/*
+ * "$Id: lpd.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * Line Printer Daemon backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ * lpd_command() - Send an LPR command sequence and wait for a reply.
+ * lpd_queue() - Queue a file using the Line Printer Daemon protocol.
+ * lpd_timeout() - Handle timeout alarms...
+ * lpd_write() - Write a buffer of data to an LPD server.
+ * rresvport_af() - A simple implementation of rresvport_af().
+ * sigterm_handler() - Handle 'terminate' signals that stop the backend.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef WIN32
+# include
+#else
+# include
+# include
+# include
+# include
+#endif /* WIN32 */
+
+
+/*
+ * Globals...
+ */
+
+static char tmpfilename[1024] = ""; /* Temporary spool file name */
+static int abort_job = 0; /* Non-zero if we get SIGTERM */
+
+
+/*
+ * The order for control and data files in LPD requests...
+ */
+
+#define ORDER_CONTROL_DATA 0 /* Control file first, then data */
+#define ORDER_DATA_CONTROL 1 /* Data file first, then control */
+
+
+/*
+ * What to reserve...
+ */
+
+#define RESERVE_NONE 0 /* Don't reserve a priviledged port */
+#define RESERVE_RFC1179 1 /* Reserve port 721-731 */
+#define RESERVE_ANY 2 /* Reserve port 1-1023 */
+
+
+/*
+ * Local functions...
+ */
+
+static int lpd_command(int lpd_fd, int timeout, char *format, ...);
+static int lpd_queue(const char *hostname, int port, const char *printer,
+ const char *filename,
+ const char *user, const char *title, int copies,
+ int banner, int format, int order, int reserve,
+ int manual_copies, int timeout);
+static void lpd_timeout(int sig);
+static int lpd_write(int lpd_fd, char *buffer, int length);
+#ifndef HAVE_RRESVPORT_AF
+static int rresvport_af(int *port, int family);
+#endif /* !HAVE_RRESVPORT_AF */
+static void sigterm_handler(int sig);
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info */
+ resource[1024], /* Resource info (printer name) */
+ *options, /* Pointer to options */
+ name[255], /* Name of option */
+ value[255], /* Value of option */
+ *ptr, /* Pointer into name or value */
+ *filename, /* File to print */
+ title[256]; /* Title string */
+ int port; /* Port number */
+ int status; /* Status of LPD job */
+ int banner; /* Print banner page? */
+ int format; /* Print format */
+ int order; /* Order of control/data files */
+ int reserve; /* Reserve priviledged port? */
+ int sanitize_title; /* Sanitize title string? */
+ int manual_copies, /* Do manual copies? */
+ timeout, /* Timeout */
+ copies; /* Number of copies */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore SIGPIPE and catch SIGTERM signals...
+ */
+
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+ sigset(SIGTERM, sigterm_handler);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGTERM);
+ action.sa_handler = sigterm_handler;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGTERM, sigterm_handler);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ puts("network lpd \"Unknown\" \"LPD/LPR Host or Printer\"");
+ return (CUPS_BACKEND_OK);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
+ argv[0]);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, copy stdin to a temporary file and print the temporary
+ * file.
+ */
+
+ if (argc == 6)
+ {
+ /*
+ * Copy stdin to a temporary file...
+ */
+
+ int fd; /* Temporary file */
+ char buffer[8192]; /* Buffer for copying */
+ int bytes; /* Number of bytes read */
+
+
+ if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
+ {
+ perror("ERROR: unable to create temporary file");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
+ if (write(fd, buffer, bytes) < bytes)
+ {
+ perror("ERROR: unable to write to temporary file");
+ close(fd);
+ unlink(tmpfilename);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ close(fd);
+ filename = tmpfilename;
+ }
+ else
+ filename = argv[6];
+
+ /*
+ * Extract the hostname and printer name from the URI...
+ */
+
+ httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+
+ if (!username[0])
+ {
+ /*
+ * If no username is in the device URI, then use the print job user...
+ */
+
+ strlcpy(username, argv[2], sizeof(username));
+ }
+
+ /*
+ * See if there are any options...
+ */
+
+ banner = 0;
+ format = 'l';
+ order = ORDER_CONTROL_DATA;
+ reserve = RESERVE_ANY;
+ manual_copies = 1;
+ timeout = 300;
+ sanitize_title = 1;
+
+#if defined(__APPLE__)
+ /* We want to pass utf-8 characters, not re-map them (3071945) */
+ sanitize_title= 0;
+#endif
+
+ if ((options = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the options...
+ */
+
+ *options++ = '\0';
+
+ /*
+ * Parse options...
+ */
+
+ while (*options)
+ {
+ /*
+ * Get the name...
+ */
+
+ for (ptr = name; *options && *options != '=';)
+ if (ptr < (name + sizeof(name) - 1))
+ *ptr++ = *options++;
+ *ptr = '\0';
+
+ if (*options == '=')
+ {
+ /*
+ * Get the value...
+ */
+
+ options ++;
+
+ for (ptr = value; *options && *options != '+' && *options != '&';)
+ if (ptr < (value + sizeof(value) - 1))
+ *ptr++ = *options++;
+ *ptr = '\0';
+
+ if (*options == '+')
+ options ++;
+ }
+ else
+ value[0] = '\0';
+
+ /*
+ * Process the option...
+ */
+
+ if (strcasecmp(name, "banner") == 0)
+ {
+ /*
+ * Set the banner...
+ */
+
+ banner = !value[0] ||
+ strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "yes") == 0 ||
+ strcasecmp(value, "true") == 0;
+ }
+ else if (strcasecmp(name, "format") == 0 && value[0])
+ {
+ /*
+ * Set output format...
+ */
+
+ if (strchr("cdfglnoprtv", value[0]) != NULL)
+ format = value[0];
+ else
+ fprintf(stderr, "ERROR: Unknown format character \"%c\"\n", value[0]);
+ }
+ else if (strcasecmp(name, "order") == 0 && value[0])
+ {
+ /*
+ * Set control/data order...
+ */
+
+ if (strcasecmp(value, "control,data") == 0)
+ order = ORDER_CONTROL_DATA;
+ else if (strcasecmp(value, "data,control") == 0)
+ order = ORDER_DATA_CONTROL;
+ else
+ fprintf(stderr, "ERROR: Unknown file order \"%s\"\n", value);
+ }
+ else if (strcasecmp(name, "reserve") == 0)
+ {
+ /*
+ * Set port reservation mode...
+ */
+
+ if (!value[0] ||
+ !strcasecmp(value, "on") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "true") ||
+ !strcasecmp(value, "rfc1179"))
+ reserve = RESERVE_RFC1179;
+ else if (!strcasecmp(value, "any"))
+ reserve = RESERVE_ANY;
+ else
+ reserve = RESERVE_NONE;
+ }
+ else if (strcasecmp(name, "manual_copies") == 0)
+ {
+ /*
+ * Set manual copies...
+ */
+
+ manual_copies = !value[0] ||
+ strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "yes") == 0 ||
+ strcasecmp(value, "true") == 0;
+ }
+ else if (strcasecmp(name, "sanitize_title") == 0)
+ {
+ /*
+ * Set sanitize title...
+ */
+
+ sanitize_title = !value[0] ||
+ strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "yes") == 0 ||
+ strcasecmp(value, "true") == 0;
+ }
+ else if (strcasecmp(name, "timeout") == 0)
+ {
+ /*
+ * Set the timeout...
+ */
+
+ if (atoi(value) > 0)
+ timeout = atoi(value);
+ }
+ }
+ }
+
+ /*
+ * Sanitize the document title...
+ */
+
+ strlcpy(title, argv[3], sizeof(title));
+
+ if (sanitize_title)
+ {
+ /*
+ * Sanitize the title string so that we don't cause problems on
+ * the remote end...
+ */
+
+ for (ptr = title; *ptr; ptr ++)
+ if (!isalnum(*ptr & 255) && !isspace(*ptr & 255))
+ *ptr = '_';
+ }
+
+ /*
+ * Queue the job...
+ */
+
+ if (argc > 6)
+ {
+ if (manual_copies)
+ {
+ manual_copies = atoi(argv[4]);
+ copies = 1;
+ }
+ else
+ {
+ manual_copies = 1;
+ copies = atoi(argv[4]);
+ }
+
+ status = lpd_queue(hostname, port, resource + 1, filename,
+ username, title, copies,
+ banner, format, order, reserve, manual_copies, timeout);
+
+ if (!status)
+ fprintf(stderr, "PAGE: 1 %d\n", atoi(argv[4]));
+ }
+ else
+ status = lpd_queue(hostname, port, resource + 1, filename,
+ username, title, 1,
+ banner, format, order, reserve, 1, timeout);
+
+ /*
+ * Remove the temporary file if necessary...
+ */
+
+ if (tmpfilename[0])
+ unlink(tmpfilename);
+
+ /*
+ * Return the queue status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * 'lpd_command()' - Send an LPR command sequence and wait for a reply.
+ */
+
+static int /* O - Status of command */
+lpd_command(int fd, /* I - Socket connection to LPD host */
+ int timeout, /* I - Seconds to wait for a response */
+ char *format, /* I - printf()-style format string */
+ ...) /* I - Additional args as necessary */
+{
+ va_list ap; /* Argument pointer */
+ char buf[1024]; /* Output buffer */
+ int bytes; /* Number of bytes to output */
+ char status; /* Status from command */
+
+
+ /*
+ * Don't try to send commands if the job has been cancelled...
+ */
+
+ if (abort_job)
+ return (-1);
+
+ /*
+ * Format the string...
+ */
+
+ va_start(ap, format);
+ bytes = vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+
+ fprintf(stderr, "DEBUG: lpd_command %2.2x %s", buf[0], buf + 1);
+
+ /*
+ * Send the command...
+ */
+
+ fprintf(stderr, "DEBUG: Sending command string (%d bytes)...\n", bytes);
+
+ if (lpd_write(fd, buf, bytes) < bytes)
+ {
+ perror("ERROR: Unable to send LPD command");
+ return (-1);
+ }
+
+ /*
+ * Read back the status from the command and return it...
+ */
+
+ fprintf(stderr, "DEBUG: Reading command status...\n");
+
+ alarm(timeout);
+
+ if (recv(fd, &status, 1, 0) < 1)
+ {
+ fprintf(stderr, "WARNING: Remote host did not respond with command "
+ "status byte after %d seconds!\n", timeout);
+ status = errno;
+ }
+
+ alarm(0);
+
+ fprintf(stderr, "DEBUG: lpd_command returning %d\n", status);
+
+ return (status);
+}
+
+
+/*
+ * 'lpd_queue()' - Queue a file using the Line Printer Daemon protocol.
+ */
+
+static int /* O - Zero on success, non-zero on failure */
+lpd_queue(const char *hostname, /* I - Host to connect to */
+ int port, /* I - Port to connect on */
+ const char *printer, /* I - Printer/queue name */
+ const char *filename, /* I - File to print */
+ const char *user, /* I - Requesting user */
+ const char *title, /* I - Job title */
+ int copies, /* I - Number of copies */
+ int banner, /* I - Print LPD banner? */
+ int format, /* I - Format specifier */
+ int order, /* I - Order of data/control files */
+ int reserve, /* I - Reserve ports? */
+ int manual_copies, /* I - Do copies by hand... */
+ int timeout) /* I - Timeout... */
+{
+ FILE *fp; /* Job file */
+ char localhost[255]; /* Local host name */
+ int error; /* Error number */
+ struct stat filestats; /* File statistics */
+ int lport; /* LPD connection local port */
+ int fd; /* LPD socket */
+ char control[10240], /* LPD control 'file' */
+ *cptr; /* Pointer into control file string */
+ char status; /* Status byte from command */
+ char portname[255]; /* Port name */
+ http_addrlist_t *addrlist, /* Address list */
+ *addr; /* Socket address */
+ int copy; /* Copies written */
+ size_t nbytes; /* Number of bytes written */
+ off_t tbytes; /* Total bytes written */
+ char buffer[65536]; /* Output buffer */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Setup an alarm handler for timeouts...
+ */
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGALRM, lpd_timeout);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = lpd_timeout;
+ sigaction(SIGALRM, &action, NULL);
+#else
+ signal(SIGALRM, lpd_timeout);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Find the printer...
+ */
+
+ sprintf(portname, "%d", port);
+
+ if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to locate printer \'%s\'!\n",
+ hostname);
+ return (CUPS_BACKEND_STOP);
+ }
+
+ /*
+ * Loop forever trying to print the file...
+ */
+
+ while (!abort_job)
+ {
+ /*
+ * First try to reserve a port for this connection...
+ */
+
+ fprintf(stderr, "INFO: Attempting to connect to host %s for printer %s\n",
+ hostname, printer);
+
+ for (lport = reserve == RESERVE_RFC1179 ? 732 : 1024, addr = addrlist;;
+ addr = addr->next)
+ {
+ /*
+ * Stop if this job has been cancelled...
+ */
+
+ if (abort_job)
+ {
+ httpAddrFreeList(addrlist);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * Choose the next priviledged port...
+ */
+
+ if (!addr)
+ addr = addrlist;
+
+ lport --;
+
+ if (lport < 721 && reserve == RESERVE_RFC1179)
+ lport = 731;
+ else if (lport < 1)
+ lport = 1023;
+
+#ifdef HAVE_GETEUID
+ if (geteuid() || !reserve)
+#else
+ if (getuid() || !reserve)
+#endif /* HAVE_GETEUID */
+ {
+ /*
+ * Just create a regular socket...
+ */
+
+ if ((fd = socket(addr->addr.addr.sa_family, SOCK_STREAM, 0)) < 0)
+ {
+ perror("ERROR: Unable to create socket");
+ sleep(1);
+
+ continue;
+ }
+
+ lport = 0;
+ }
+ else
+ {
+ /*
+ * We're running as root and want to comply with RFC 1179. Reserve a
+ * priviledged lport between 721 and 731...
+ */
+
+ if ((fd = rresvport_af(&lport, addr->addr.addr.sa_family)) < 0)
+ {
+ perror("ERROR: Unable to reserve port");
+ sleep(1);
+
+ continue;
+ }
+ }
+
+ /*
+ * Connect to the printer or server...
+ */
+
+ if (abort_job)
+ {
+ httpAddrFreeList(addrlist);
+
+ close(fd);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (!connect(fd, &(addr->addr.addr), httpAddrLength(&(addr->addr))))
+ break;
+
+ error = errno;
+ close(fd);
+ fd = -1;
+
+ if (addr->next)
+ continue;
+
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fprintf(stderr, "INFO: Unable to connect to %s, queuing on next printer in class...\n",
+ hostname);
+
+ httpAddrFreeList(addrlist);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (error == ECONNREFUSED || error == EHOSTDOWN ||
+ error == EHOSTUNREACH)
+ {
+ fprintf(stderr, "WARNING: Network host \'%s\' is busy, down, or unreachable; will retry in 30 seconds...\n",
+ hostname);
+ sleep(30);
+ }
+ else if (error == EADDRINUSE)
+ {
+ /*
+ * Try on another port...
+ */
+
+ sleep(1);
+ }
+ else
+ {
+ perror("ERROR: Unable to connect to printer; will retry in 30 seconds...");
+ sleep(30);
+ }
+ }
+
+ fprintf(stderr, "INFO: Connected to %s...\n", hostname);
+ fprintf(stderr, "DEBUG: Connected on ports %d (local %d)...\n", port,
+ lport);
+
+ /*
+ * Next, open the print file and figure out its size...
+ */
+
+ if (stat(filename, &filestats))
+ {
+ httpAddrFreeList(addrlist);
+ close(fd);
+
+ perror("ERROR: unable to stat print file");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ filestats.st_size *= manual_copies;
+
+ if ((fp = fopen(filename, "rb")) == NULL)
+ {
+ httpAddrFreeList(addrlist);
+ close(fd);
+
+ perror("ERROR: unable to open print file for reading");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * Send a job header to the printer, specifying no banner page and
+ * literal output...
+ */
+
+ if (lpd_command(fd, timeout, "\002%s\n",
+ printer)) /* Receive print job(s) */
+ {
+ httpAddrFreeList(addrlist);
+ close(fd);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ httpGetHostname(localhost, sizeof(localhost));
+
+ snprintf(control, sizeof(control),
+ "H%.31s\n" /* RFC 1179, Section 7.2 - host name <= 31 chars */
+ "P%.31s\n" /* RFC 1179, Section 7.2 - user name <= 31 chars */
+ "J%.99s\n", /* RFC 1179, Section 7.2 - job name <= 99 chars */
+ localhost, user, title);
+ cptr = control + strlen(control);
+
+ if (banner)
+ {
+ snprintf(cptr, sizeof(control) - (cptr - control),
+ "C%.31s\n" /* RFC 1179, Section 7.2 - class name <= 31 chars */
+ "L%s\n",
+ localhost, user);
+ cptr += strlen(cptr);
+ }
+
+ while (copies > 0)
+ {
+ snprintf(cptr, sizeof(control) - (cptr - control), "%cdfA%03d%.15s\n",
+ format, (int)getpid() % 1000, localhost);
+ cptr += strlen(cptr);
+ copies --;
+ }
+
+ snprintf(cptr, sizeof(control) - (cptr - control),
+ "UdfA%03d%.15s\n"
+ "N%.131s\n", /* RFC 1179, Section 7.2 - sourcefile name <= 131 chars */
+ (int)getpid() % 1000, localhost, title);
+
+ fprintf(stderr, "DEBUG: Control file is:\n%s", control);
+
+ if (order == ORDER_CONTROL_DATA)
+ {
+ if (lpd_command(fd, timeout, "\002%d cfA%03.3d%.15s\n", strlen(control),
+ (int)getpid() % 1000, localhost))
+ {
+ httpAddrFreeList(addrlist);
+ close(fd);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ fprintf(stderr, "INFO: Sending control file (%u bytes)\n",
+ (unsigned)strlen(control));
+
+ if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1))
+ {
+ status = errno;
+ perror("ERROR: Unable to write control file");
+ }
+ else
+ {
+ alarm(timeout);
+
+ if (read(fd, &status, 1) < 1)
+ {
+ fprintf(stderr, "WARNING: Remote host did not respond with control "
+ "status byte after %d seconds!\n", timeout);
+ status = errno;
+ }
+
+ alarm(0);
+ }
+
+ if (status != 0)
+ fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n",
+ status);
+ else
+ fputs("INFO: Control file sent successfully\n", stderr);
+ }
+ else
+ status = 0;
+
+ if (status == 0)
+ {
+ /*
+ * Send the print file...
+ */
+
+ if (lpd_command(fd, timeout, "\003" CUPS_LLFMT " dfA%03.3d%.15s\n",
+ CUPS_LLCAST filestats.st_size, (int)getpid() % 1000,
+ localhost))
+ {
+ httpAddrFreeList(addrlist);
+ close(fd);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ fprintf(stderr, "INFO: Sending data file (" CUPS_LLFMT " bytes)\n",
+ CUPS_LLCAST filestats.st_size);
+
+ tbytes = 0;
+ for (copy = 0; copy < manual_copies; copy ++)
+ {
+ rewind(fp);
+
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ fprintf(stderr, "INFO: Spooling LPR job, %.0f%% complete...\n",
+ 100.0 * tbytes / filestats.st_size);
+
+ if (lpd_write(fd, buffer, nbytes) < nbytes)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ else
+ tbytes += nbytes;
+ }
+ }
+
+ if (tbytes < filestats.st_size)
+ status = errno;
+ else if (lpd_write(fd, "", 1) < 1)
+ {
+ perror("ERROR: Unable to send trailing nul to printer");
+ status = errno;
+ }
+ else
+ {
+ /*
+ * Read the status byte from the printer; if we can't read the byte
+ * back now, we should set status to "errno", however at this point
+ * we know the printer got the whole file and we don't necessarily
+ * want to requeue it over and over...
+ */
+
+ alarm(timeout);
+
+ if (recv(fd, &status, 1, 0) < 1)
+ {
+ fprintf(stderr, "WARNING: Remote host did not respond with data "
+ "status byte after %d seconds!\n", timeout);
+ status = 0;
+ }
+
+ alarm(0);
+ }
+
+ if (status != 0)
+ fprintf(stderr, "ERROR: Remote host did not accept data file (%d)\n",
+ status);
+ else
+ fputs("INFO: Data file sent successfully\n", stderr);
+ }
+
+ if (status == 0 && order == ORDER_DATA_CONTROL)
+ {
+ if (lpd_command(fd, timeout, "\002%d cfA%03.3d%.15s\n", strlen(control),
+ (int)getpid() % 1000, localhost))
+ {
+ httpAddrFreeList(addrlist);
+ close(fd);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ fprintf(stderr, "INFO: Sending control file (%lu bytes)\n",
+ (unsigned long)strlen(control));
+
+ if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1))
+ {
+ status = errno;
+ perror("ERROR: Unable to write control file");
+ }
+ else
+ {
+ alarm(timeout);
+
+ if (read(fd, &status, 1) < 1)
+ {
+ fprintf(stderr, "WARNING: Remote host did not respond with control "
+ "status byte after %d seconds!\n", timeout);
+ status = errno;
+ }
+
+ alarm(0);
+ }
+
+ if (status != 0)
+ fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n",
+ status);
+ else
+ fputs("INFO: Control file sent successfully\n", stderr);
+ }
+
+ /*
+ * Close the socket connection and input file...
+ */
+
+ close(fd);
+ fclose(fp);
+
+ if (status == 0)
+ {
+ httpAddrFreeList(addrlist);
+
+ return (CUPS_BACKEND_OK);
+ }
+
+ /*
+ * Waiting for a retry...
+ */
+
+ sleep(30);
+ }
+
+ httpAddrFreeList(addrlist);
+
+ /*
+ * If we get here, then the job has been cancelled...
+ */
+
+ return (CUPS_BACKEND_FAILED);
+}
+
+
+/*
+ * 'lpd_timeout()' - Handle timeout alarms...
+ */
+
+static void
+lpd_timeout(int sig) /* I - Signal number */
+{
+ (void)sig;
+
+#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
+ signal(SIGALRM, lpd_timeout);
+#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
+}
+
+
+/*
+ * 'lpd_write()' - Write a buffer of data to an LPD server.
+ */
+
+static int /* O - Number of bytes written or -1 on error */
+lpd_write(int lpd_fd, /* I - LPD socket */
+ char *buffer, /* I - Buffer to write */
+ int length) /* I - Number of bytes to write */
+{
+ int bytes, /* Number of bytes written */
+ total; /* Total number of bytes written */
+
+
+ if (abort_job)
+ return (-1);
+
+ total = 0;
+ while ((bytes = send(lpd_fd, buffer, length - total, 0)) >= 0)
+ {
+ total += bytes;
+ buffer += bytes;
+
+ if (total == length)
+ break;
+ }
+
+ if (bytes < 0)
+ return (-1);
+ else
+ return (length);
+}
+
+
+#ifndef HAVE_RRESVPORT_AF
+/*
+ * 'rresvport_af()' - A simple implementation of rresvport_af().
+ */
+
+static int /* O - Socket or -1 on error */
+rresvport_af(int *port, /* IO - Port number to bind to */
+ int family) /* I - Address family */
+{
+ http_addr_t addr; /* Socket address */
+ int fd; /* Socket file descriptor */
+
+
+ /*
+ * Try to create an IPv4 socket...
+ */
+
+ if ((fd = socket(family, SOCK_STREAM, 0)) < 0)
+ return (-1);
+
+ /*
+ * Initialize the address buffer...
+ */
+
+ memset(&addr, 0, sizeof(addr));
+ addr.addr.sa_family = family;
+
+ /*
+ * Try to bind the socket to a reserved port...
+ */
+
+ while (*port > 511)
+ {
+ /*
+ * Set the port number...
+ */
+
+# ifdef AF_INET6
+ if (family == AF_INET6)
+ addr.ipv6.sin6_port = htons(*port);
+ else
+# endif /* AF_INET6 */
+ addr.ipv4.sin_port = htons(*port);
+
+ /*
+ * Try binding the port to the socket; return if all is OK...
+ */
+
+ if (!bind(fd, (struct sockaddr *)&addr, sizeof(addr)))
+ return (fd);
+
+ /*
+ * Stop if we have any error other than "address already in use"...
+ */
+
+ if (errno != EADDRINUSE)
+ {
+# ifdef WIN32
+ closesocket(fd);
+# else
+ close(fd);
+# endif /* WIN32 */
+
+ return (-1);
+ }
+
+ /*
+ * Try the next port...
+ */
+
+ (*port)--;
+ }
+
+ /*
+ * Wasn't able to bind to a reserved port, so close the socket and return
+ * -1...
+ */
+
+# ifdef WIN32
+ closesocket(fd);
+# else
+ close(fd);
+# endif /* WIN32 */
+
+ return (-1);
+}
+#endif /* !HAVE_RRESVPORT_AF */
+
+
+/*
+ * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
+ */
+
+static void
+sigterm_handler(int sig) /* I - Signal */
+{
+ (void)sig; /* remove compiler warnings... */
+
+ abort_job = 1;
+}
+
+
+/*
+ * End of "$Id: lpd.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/backend/parallel.c b/backend/parallel.c
new file mode 100644
index 000000000..d80f8e214
--- /dev/null
+++ b/backend/parallel.c
@@ -0,0 +1,731 @@
+/*
+ * "$Id: parallel.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * Parallel port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the specified parallel port.
+ * list_devices() - List all parallel devices.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "ieee1284.c"
+
+#ifdef WIN32
+# include
+#else
+# include
+# include
+# include
+# include
+#endif /* WIN32 */
+
+#ifdef __sgi
+# include
+# ifndef INV_EPP_ECP_PLP
+# define INV_EPP_ECP_PLP 6 /* From 6.3/6.4/6.5 sys/invent.h */
+# define INV_ASO_SERIAL 14 /* serial portion of SGI ASO board */
+# define INV_IOC3_DMA 16 /* DMA mode IOC3 serial */
+# define INV_IOC3_PIO 17 /* PIO mode IOC3 serial */
+# define INV_ISA_DMA 19 /* DMA mode ISA serial -- O2 */
+# endif /* !INV_EPP_ECP_PLP */
+#endif /* __sgi */
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+
+
+/*
+ * 'main()' - Send a file to the specified parallel port.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024], /* Resource info (device and options) */
+ *options; /* Pointer to options */
+ int port; /* Port number (not used) */
+ int fp; /* Print file */
+ int copies; /* Number of copies to print */
+ int fd; /* Parallel device */
+ int rbytes; /* Number of bytes read */
+ int wbytes; /* Number of bytes written */
+ size_t nbytes, /* Number of bytes read */
+ tbytes; /* Total number of bytes written */
+ char buffer[8192], /* Output buffer */
+ *bufptr; /* Pointer into buffer */
+ struct termios opts; /* Parallel port options */
+ fd_set input, /* Input set for select() */
+ output; /* Output set for select() */
+ int paperout; /* Paper out? */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+#ifdef __linux
+ unsigned int status; /* Port status (off-line, out-of-paper, etc.) */
+#endif /* __linux */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore SIGPIPE signals...
+ */
+
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ list_devices();
+ return (CUPS_BACKEND_OK);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fputs("Usage: parallel job-id user title copies options [file]\n", stderr);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ {
+ fp = 0;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = open(argv[6], O_RDONLY)) < 0)
+ {
+ perror("ERROR: unable to open print file");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * Extract the device name and options from the URI...
+ */
+
+ httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+
+ /*
+ * See if there are any options...
+ */
+
+ if ((options = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the options...
+ */
+
+ *options++ = '\0';
+ }
+
+ /*
+ * Open the parallel port device...
+ */
+
+ do
+ {
+ if ((fd = open(resource, O_WRONLY | O_EXCL)) == -1)
+ {
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fputs("INFO: Unable to open parallel port, queuing on next printer in class...\n",
+ stderr);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (errno == EBUSY)
+ {
+ fputs("INFO: Parallel port busy; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else if (errno == ENXIO || errno == EIO || errno == ENOENT)
+ {
+ fputs("INFO: Printer not connected; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to open parallel port device file \"%s\": %s\n",
+ resource, strerror(errno));
+ return (CUPS_BACKEND_FAILED);
+ }
+ }
+ }
+ while (fd < 0);
+
+ /*
+ * Set any options provided...
+ */
+
+ tcgetattr(fd, &opts);
+
+ opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
+
+ /**** No options supported yet ****/
+
+ tcsetattr(fd, TCSANOW, &opts);
+
+ /*
+ * Check printer status...
+ */
+
+ paperout = 0;
+
+#if defined(__linux) && defined(LP_POUTPA)
+ /*
+ * Show the printer status before we send the file...
+ */
+
+ while (!ioctl(fd, LPGETSTATUS, &status))
+ {
+ fprintf(stderr, "DEBUG: LPGETSTATUS returned a port status of %02X...\n", status);
+
+ if (status & LP_POUTPA)
+ {
+ fputs("WARNING: Media tray empty!\n", stderr);
+ fputs("STATUS: +media-tray-empty-error\n", stderr);
+
+ paperout = 1;
+ }
+
+ if (!(status & LP_PERRORP))
+ fputs("WARNING: Printer fault!\n", stderr);
+ else if (!(status & LP_PSELECD))
+ fputs("WARNING: Printer off-line.\n", stderr);
+ else
+ break;
+
+ sleep(5);
+ }
+#endif /* __linux && LP_POUTPA */
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (argc < 7)
+ {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ wbytes = 0;
+
+ while (copies > 0)
+ {
+ copies --;
+
+ if (fp != 0)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ lseek(fp, 0, SEEK_SET);
+ }
+
+ tbytes = 0;
+ while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ tbytes += nbytes;
+ bufptr = buffer;
+
+ while (nbytes > 0)
+ {
+ /*
+ * See if we are ready to read or write...
+ */
+
+ do
+ {
+ FD_ZERO(&input);
+ FD_SET(fd, &input);
+ FD_ZERO(&output);
+ FD_SET(fd, &output);
+ }
+ while (select(fd + 1, &input, &output, NULL, NULL) < 0);
+
+ if (FD_ISSET(fd, &input))
+ {
+ /*
+ * Read backchannel data...
+ */
+
+ if ((rbytes = read(fd, resource, sizeof(resource))) > 0)
+ {
+ fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
+ rbytes);
+ cupsBackchannelWrite(resource, rbytes, 1.0);
+ }
+ }
+
+ if (FD_ISSET(fd, &output))
+ {
+ /*
+ * Write print data...
+ */
+
+ if ((wbytes = write(fd, bufptr, nbytes)) < 0)
+ if (errno == ENOTTY)
+ wbytes = write(fd, bufptr, nbytes);
+
+ if (wbytes < 0)
+ {
+ /*
+ * Check for retryable errors...
+ */
+
+ if (errno == ENOSPC)
+ {
+ paperout = 1;
+ fputs("ERROR: Out of paper!\n", stderr);
+ fputs("STATUS: +media-tray-empty-error\n", stderr);
+ }
+ else if (errno != EAGAIN && errno != EINTR)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Update count and pointer...
+ */
+
+ if (paperout)
+ {
+ fputs("STATUS: -media-tray-empty-error\n", stderr);
+ paperout = 0;
+ }
+
+ nbytes -= wbytes;
+ bufptr += wbytes;
+ }
+ }
+ }
+
+ if (wbytes < 0)
+ break;
+
+ if (argc > 6)
+ fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
+ (unsigned long)tbytes);
+ }
+ }
+
+ /*
+ * Close the socket connection and input file and return...
+ */
+
+ close(fd);
+ if (fp != 0)
+ close(fp);
+
+ return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
+}
+
+
+/*
+ * 'list_devices()' - List all parallel devices.
+ */
+
+void
+list_devices(void)
+{
+#if defined(__hpux) || defined(__sgi) || defined(__sun)
+ static char *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
+ /* Funky hex numbering used for some devices */
+#endif /* __hpux || __sgi || __sun */
+
+#ifdef __linux
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255], /* Device filename */
+ basedevice[255], /* Base device filename for ports */
+ device_id[1024], /* Device ID string */
+ make_model[1024]; /* Make and model */
+
+
+ if (!access("/dev/parallel/", 0))
+ strcpy(basedevice, "/dev/parallel/");
+ else if (!access("/dev/printers/", 0))
+ strcpy(basedevice, "/dev/printers/");
+ else if (!access("/dev/par0", 0))
+ strcpy(basedevice, "/dev/par");
+ else
+ strcpy(basedevice, "/dev/lp");
+
+ for (i = 0; i < 4; i ++)
+ {
+ /*
+ * Open the port, if available...
+ */
+
+ sprintf(device, "%s%d", basedevice, i);
+ if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
+ fd = open(device, O_WRONLY);
+
+ if (fd >= 0)
+ {
+ /*
+ * Now grab the IEEE 1284 device ID string...
+ */
+
+ if (!get_device_id(fd, device_id, sizeof(device_id),
+ make_model, sizeof(make_model),
+ NULL, NULL, 0))
+ printf("direct parallel:%s \"%s\" \"%s LPT #%d\" \"%s\"\n", device,
+ make_model, make_model, i + 1, device_id);
+ else
+ printf("direct parallel:%s \"Unknown\" \"LPT #%d\"\n", device, i + 1);
+
+ close(fd);
+ }
+ }
+#elif defined(__sgi)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+ inventory_t *inv; /* Hardware inventory info */
+
+
+ /*
+ * IRIX maintains a hardware inventory of most devices...
+ */
+
+ setinvent();
+
+ while ((inv = getinvent()) != NULL)
+ {
+ if (inv->inv_class == INV_PARALLEL &&
+ (inv->inv_type == INV_ONBOARD_PLP ||
+ inv->inv_type == INV_EPP_ECP_PLP))
+ {
+ /*
+ * Standard parallel port...
+ */
+
+ puts("direct parallel:/dev/plp \"Unknown\" \"Onboard Parallel Port\"");
+ }
+ else if (inv->inv_class == INV_PARALLEL &&
+ inv->inv_type == INV_EPC_PLP)
+ {
+ /*
+ * EPC parallel port...
+ */
+
+ printf("direct parallel:/dev/plp%d \"Unknown\" \"Integral EPC parallel port, Ebus slot %d\"\n",
+ inv->inv_controller, inv->inv_controller);
+ }
+ }
+
+ endinvent();
+
+ /*
+ * Central Data makes serial and parallel "servers" that can be
+ * connected in a number of ways. Look for ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/lpn%d%c", j, funky_hex[n]);
+ else if (i == 9) /* PCI */
+ sprintf(device, "/dev/lpp%d%c", j, funky_hex[n]);
+ else /* SCSI */
+ sprintf(device, "/dev/lp%d%d%c", i, j, funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else if (i == 9)
+ printf("direct parallel:%s \"Unknown\" \"Central Data PCI Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__sun)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard parallel ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ {
+ sprintf(device, "/dev/ecpp%d", i);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"Sun IEEE-1284 Parallel Port #%d\"\n",
+ device, i + 1);
+ }
+
+ for (i = 0; i < 10; i ++)
+ {
+ sprintf(device, "/dev/bpp%d", i);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"Sun Standard Parallel Port #%d\"\n",
+ device, i + 1);
+ }
+
+ for (i = 0; i < 3; i ++)
+ {
+ sprintf(device, "/dev/lp%d", i);
+
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"PC Parallel Port #%d\"\n",
+ device, i + 1);
+ }
+
+ /*
+ * MAGMA parallel ports...
+ */
+
+ for (i = 0; i < 40; i ++)
+ {
+ sprintf(device, "/dev/pm%02d", i);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"MAGMA Parallel Board #%d Port #%d\"\n",
+ device, (i / 10) + 1, (i % 10) + 1);
+ }
+
+ /*
+ * Central Data parallel ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/sts/lpN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/sts/lp%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__hpux)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard parallel ports...
+ */
+
+ if (access("/dev/rlp", 0) == 0)
+ puts("direct parallel:/dev/rlp \"Unknown\" \"Standard Parallel Port (/dev/rlp)\"");
+
+ for (i = 0; i < 7; i ++)
+ for (j = 0; j < 7; j ++)
+ {
+ sprintf(device, "/dev/c%dt%dd0_lp", i, j);
+ if (access(device, 0) == 0)
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d,%d\"\n",
+ device, i, j);
+ }
+
+ /*
+ * Central Data parallel ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/lpN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/lp%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__osf__)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 3; i ++)
+ {
+ sprintf(device, "/dev/lp%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
+ }
+ }
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 3; i ++)
+ {
+ sprintf(device, "/dev/lpt%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (interrupt-driven)\"\n", device, i + 1);
+ }
+
+ sprintf(device, "/dev/lpa%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (polled)\"\n", device, i + 1);
+ }
+ }
+#elif defined(_AIX)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 8; i ++)
+ {
+ sprintf(device, "/dev/lp%d", i);
+ if ((fd = open(device, O_WRONLY)) >= 0)
+ {
+ close(fd);
+ printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
+ }
+ }
+#endif
+}
+
+
+/*
+ * End of "$Id: parallel.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/backend/scsi-irix.c b/backend/scsi-irix.c
new file mode 100644
index 000000000..097071c23
--- /dev/null
+++ b/backend/scsi-irix.c
@@ -0,0 +1,231 @@
+/*
+ * "$Id: scsi-irix.c 4703 2005-09-26 19:33:58Z mike $"
+ *
+ * IRIX SCSI printer support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2003-2005 by Easy Software Products, all rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use
+ * of this software must display the following
+ * acknowledgement:
+ *
+ * This product includes software developed by Easy
+ * Software Products.
+ *
+ * 4. The name of Easy Software Products may not be used to
+ * endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Contents:
+ *
+ * list_devices() - List the available SCSI printer devices.
+ * print_device() - Print a file to a SCSI device.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include /* memcpy() and friends */
+#include /* SCSI interface stuff */
+
+
+/*
+ * 'list_devices()' - List the available SCSI printer devices.
+ */
+
+void
+list_devices(void)
+{
+ puts("direct scsi \"Unknown\" \"SCSI Printer\"");
+}
+
+
+/*
+ * 'print_device()' - Print a file to a SCSI device.
+ */
+
+int /* O - Print status */
+print_device(const char *resource, /* I - SCSI device */
+ int fd, /* I - File to print */
+ int copies) /* I - Number of copies to print */
+{
+ int scsi_fd; /* SCSI file descriptor */
+ char buffer[8192]; /* Data buffer */
+ int bytes; /* Number of bytes */
+ int try; /* Current try */
+ dsreq_t scsi_req; /* SCSI request */
+ char scsi_cmd[6]; /* SCSI command data */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure we have a valid resource name...
+ */
+
+ if (strncmp(resource, "/dev/scsi/", 10) != 0)
+ {
+ fprintf(stderr, "ERROR: Bad SCSI device file \"%s\"!\n", resource);
+ return (CUPS_BACKEND_STOP);
+ }
+
+ /*
+ * Open the SCSI device file...
+ */
+
+ do
+ {
+ if ((scsi_fd = open(resource, O_RDWR | O_EXCL)) == -1)
+ {
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fputs("INFO: Unable to open SCSI device, queuing on next printer in class...\n",
+ stderr);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (1);
+ }
+
+ if (errno != EAGAIN && errno != EBUSY)
+ {
+ fprintf(stderr, "ERROR: Unable to open SCSI device \"%s\" - %s\n",
+ resource, strerror(errno));
+ return (CUPS_BACKEND_FAILED);
+ }
+ else
+ {
+ fprintf(stderr, "INFO: SCSI device \"%s\" busy; retrying...\n",
+ resource);
+ sleep(30);
+ }
+ }
+ }
+ while (scsi_fd == -1);
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (fd != 0)
+ {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ /*
+ * Copy the print file to the device...
+ */
+
+ while (copies > 0)
+ {
+ if (fd != 0)
+ lseek(fd, 0, SEEK_SET);
+
+ while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
+ {
+ memset(&scsi_req, 0, sizeof(scsi_req));
+
+ scsi_req.ds_flags = DSRQ_WRITE;
+ scsi_req.ds_time = 60 * 1000;
+ scsi_req.ds_cmdbuf = scsi_cmd;
+ scsi_req.ds_cmdlen = 6;
+ scsi_req.ds_databuf = buffer;
+ scsi_req.ds_datalen = bytes;
+
+ scsi_cmd[0] = 0x0a; /* Group 0 print command */
+ scsi_cmd[1] = 0x00;
+ scsi_cmd[2] = bytes / 65536;
+ scsi_cmd[3] = bytes / 256;
+ scsi_cmd[4] = bytes;
+ scsi_cmd[5] = 0x00;
+
+ for (try = 0; try < 10; try ++)
+ if (ioctl(scsi_fd, DS_ENTER, &scsi_req) < 0 ||
+ scsi_req.ds_status != 0)
+ {
+ fprintf(stderr, "WARNING: SCSI command timed out (%d); retrying...\n",
+ scsi_req.ds_status);
+ sleep(try + 1);
+ }
+ else
+ break;
+
+ if (try >= 10)
+ {
+ fprintf(stderr, "ERROR: Unable to send print data (%d)\n",
+ scsi_req.ds_status);
+ close(scsi_fd);
+ return (CUPS_BACKEND_FAILED);
+ }
+ }
+
+ copies --;
+ }
+
+ /*
+ * Close the device and return...
+ */
+
+ close(fd);
+
+ return (CUPS_BACKEND_OK);
+}
+
+
+/*
+ * End of "$Id: scsi-irix.c 4703 2005-09-26 19:33:58Z mike $".
+ */
diff --git a/backend/scsi-linux.c b/backend/scsi-linux.c
new file mode 100644
index 000000000..b5c1d1bef
--- /dev/null
+++ b/backend/scsi-linux.c
@@ -0,0 +1,249 @@
+/*
+ * "$Id: scsi-linux.c 4703 2005-09-26 19:33:58Z mike $"
+ *
+ * Linux SCSI printer support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2003-2005 by Easy Software Products, all rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use
+ * of this software must display the following
+ * acknowledgement:
+ *
+ * This product includes software developed by Easy
+ * Software Products.
+ *
+ * 4. The name of Easy Software Products may not be used to
+ * endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Contents:
+ *
+ * list_devices() - List the available SCSI printer devices.
+ * print_device() - Print a file to a SCSI device.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+
+
+/*
+ * We currently only support the Linux 2.4 generic SCSI interface.
+ */
+
+#ifndef SG_DXFER_TO_DEV
+/*
+ * Dummy functions that do nothing on unsupported platforms...
+ */
+void list_devices(void) {}
+int print_device(const char *resource, int fd, int copies) { return (1); }
+#else
+
+
+/*
+ * 'list_devices()' - List the available SCSI printer devices.
+ */
+
+void
+list_devices(void)
+{
+ puts("direct scsi \"Unknown\" \"SCSI Printer\"");
+}
+
+
+/*
+ * 'print_device()' - Print a file to a SCSI device.
+ */
+
+int /* O - Print status */
+print_device(const char *resource, /* I - SCSI device */
+ int fd, /* I - File to print */
+ int copies) /* I - Number of copies to print */
+{
+ int scsi_fd; /* SCSI file descriptor */
+ char buffer[8192]; /* Data buffer */
+ int bytes; /* Number of bytes */
+ int try; /* Current try */
+ sg_io_hdr_t scsi_req; /* SCSI request */
+ unsigned char scsi_cmd[6], /* SCSI command data */
+ scsi_sense[32]; /* SCSI sense data */
+# if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+# endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure we have a valid resource name...
+ */
+
+ if (strncmp(resource, "/dev/sg", 7) != 0)
+ {
+ fprintf(stderr, "ERROR: Bad SCSI device file \"%s\"!\n", resource);
+ return (CUPS_BACKEND_STOP);
+ }
+
+ /*
+ * Open the SCSI device file...
+ */
+
+ do
+ {
+ if ((scsi_fd = open(resource, O_RDWR | O_EXCL)) == -1)
+ {
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fputs("INFO: Unable to open SCSI device, queuing on next printer in class...\n",
+ stderr);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (errno != EAGAIN && errno != EBUSY)
+ {
+ fprintf(stderr, "ERROR: Unable to open SCSI device \"%s\" - %s\n",
+ resource, strerror(errno));
+ return (CUPS_BACKEND_FAILED);
+ }
+ else
+ {
+ fprintf(stderr, "INFO: SCSI device \"%s\" busy; retrying...\n",
+ resource);
+ sleep(30);
+ }
+ }
+ }
+ while (scsi_fd == -1);
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (fd != 0)
+ {
+# ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+# elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+# else
+ signal(SIGTERM, SIG_IGN);
+# endif /* HAVE_SIGSET */
+ }
+
+ /*
+ * Copy the print file to the device...
+ */
+
+ while (copies > 0)
+ {
+ if (fd != 0)
+ lseek(fd, 0, SEEK_SET);
+
+ while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
+ {
+ memset(&scsi_req, 0, sizeof(scsi_req));
+
+ scsi_req.interface_id = 'S';
+ scsi_req.dxfer_direction = SG_DXFER_TO_DEV;
+ scsi_req.cmd_len = 6;
+ scsi_req.mx_sb_len = sizeof(scsi_sense);
+ scsi_req.iovec_count = 0;
+ scsi_req.dxfer_len = bytes;
+ scsi_req.dxferp = buffer;
+ scsi_req.cmdp = scsi_cmd;
+ scsi_req.sbp = scsi_sense;
+ scsi_req.timeout = 60 * 1000;
+
+ scsi_cmd[0] = 0x0a; /* Group 0 print command */
+ scsi_cmd[1] = 0x00;
+ scsi_cmd[2] = bytes / 65536;
+ scsi_cmd[3] = bytes / 256;
+ scsi_cmd[4] = bytes;
+ scsi_cmd[5] = 0x00;
+
+ for (try = 0; try < 10; try ++)
+ if (ioctl(scsi_fd, SG_IO, &scsi_req) < 0 ||
+ scsi_req.status != 0)
+ {
+ fprintf(stderr, "WARNING: SCSI command timed out (%d); retrying...\n",
+ scsi_req.status);
+ sleep(try + 1);
+ }
+ else
+ break;
+
+ if (try >= 10)
+ {
+ fprintf(stderr, "ERROR: Unable to send print data (%d)\n",
+ scsi_req.status);
+ close(scsi_fd);
+ return (CUPS_BACKEND_FAILED);
+ }
+ }
+
+ copies --;
+ }
+
+ /*
+ * Close the device and return...
+ */
+
+ close(fd);
+
+ return (CUPS_BACKEND_OK);
+}
+#endif /* !SG_DXFER_TO_DEV */
+
+
+/*
+ * End of "$Id: scsi-linux.c 4703 2005-09-26 19:33:58Z mike $".
+ */
diff --git a/backend/scsi.c b/backend/scsi.c
new file mode 100644
index 000000000..fdbc7f2f3
--- /dev/null
+++ b/backend/scsi.c
@@ -0,0 +1,223 @@
+/*
+ * "$Id: scsi.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * SCSI printer backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2003-2006 by Easy Software Products, all rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use
+ * of this software must display the following
+ * acknowledgement:
+ *
+ * This product includes software developed by Easy
+ * Software Products.
+ *
+ * 4. The name of Easy Software Products may not be used to
+ * endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the specified SCSI printer.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef WIN32
+# include
+#else
+# include
+# include
+# ifdef HAVE_SYS_IOCTL_H
+# include
+# endif /* HAVE_SYS_IOCTL_H */
+#endif /* WIN32 */
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+int print_device(const char *resource, int fd, int copies);
+
+
+#ifdef __linux__
+# include "scsi-linux.c"
+#elif defined(__sgi)
+# include "scsi-irix.c"
+#else
+/*
+ * Dummy functions that do nothing on unsupported platforms...
+ */
+void list_devices(void) {}
+int print_device(const char *resource, int fd, int copies) { return (CUPS_BACKEND_FAILED); }
+#endif /* __linux */
+
+
+/*
+ * 'main()' - Send a file to the specified SCSI printer.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024], /* Resource info (device and options) */
+ *options; /* Pointer to options */
+ int port; /* Port number (not used) */
+ int fp; /* Print file */
+ int copies; /* Number of copies to print */
+ int status; /* Exit status */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore SIGPIPE signals...
+ */
+
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ list_devices();
+ return (CUPS_BACKEND_OK);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fputs("Usage: scsi:/dev/file job-id user title copies options [file]\n", stderr);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ {
+ fp = 0;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = open(argv[6], O_RDONLY)) < 0)
+ {
+ perror("ERROR: unable to open print file");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * Extract the device name and options from the URI...
+ */
+
+ httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+
+ /*
+ * See if there are any options...
+ */
+
+ if ((options = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the options...
+ */
+
+ *options++ = '\0';
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ status = print_device(resource, fp, copies);
+
+ /*
+ * Close input file and return...
+ */
+
+ if (fp != 0)
+ close(fp);
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: scsi.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/backend/serial.c b/backend/serial.c
new file mode 100644
index 000000000..3079eef5e
--- /dev/null
+++ b/backend/serial.c
@@ -0,0 +1,1114 @@
+/*
+ * "$Id: serial.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * Serial port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ * list_devices() - List all serial devices.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef __hpux
+# include
+#endif /* __hpux */
+
+#ifdef WIN32
+# include
+#else
+# include
+# include
+# include
+# include
+# ifdef HAVE_SYS_IOCTL_H
+# include
+# endif /* HAVE_SYS_IOCTL_H */
+#endif /* WIN32 */
+
+#ifdef __sgi
+# include
+# ifndef INV_EPP_ECP_PLP
+# define INV_EPP_ECP_PLP 6 /* From 6.3/6.4/6.5 sys/invent.h */
+# define INV_ASO_SERIAL 14 /* serial portion of SGI ASO board */
+# define INV_IOC3_DMA 16 /* DMA mode IOC3 serial */
+# define INV_IOC3_PIO 17 /* PIO mode IOC3 serial */
+# define INV_ISA_DMA 19 /* DMA mode ISA serial -- O2 */
+# endif /* !INV_EPP_ECP_PLP */
+#endif /* __sgi */
+
+#ifndef CRTSCTS
+# ifdef CNEW_RTSCTS
+# define CRTSCTS CNEW_RTSCTS
+# else
+# define CRTSCTS 0
+# endif /* CNEW_RTSCTS */
+#endif /* !CRTSCTS */
+
+#if defined(__APPLE__)
+# include
+# include
+# include
+# include
+#endif /* __APPLE__ */
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024], /* Resource info (device and options) */
+ *options, /* Pointer to options */
+ name[255], /* Name of option */
+ value[255], /* Value of option */
+ *ptr; /* Pointer into name or value */
+ int port; /* Port number (not used) */
+ int fp; /* Print file */
+ int copies; /* Number of copies to print */
+ int fd; /* Parallel device */
+ int rbytes; /* Number of bytes read */
+ int wbytes; /* Number of bytes written */
+ size_t nbytes, /* Number of bytes read */
+ tbytes; /* Total number of bytes written */
+ int dtrdsr; /* Do dtr/dsr flow control? */
+ int bufsize; /* Size of output buffer for writes */
+ char buffer[8192], /* Output buffer */
+ *bufptr; /* Pointer into buffer */
+ struct termios opts; /* Serial port options */
+ struct termios origopts; /* Original port options */
+ fd_set input, /* Input set for select() */
+ output; /* Output set for select() */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore SIGPIPE signals...
+ */
+
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ list_devices();
+ return (CUPS_BACKEND_OK);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fputs("Usage: serial job-id user title copies options [file]\n", stderr);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ {
+ fp = 0;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = open(argv[6], O_RDONLY)) < 0)
+ {
+ perror("ERROR: unable to open print file");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * Extract the device name and options from the URI...
+ */
+
+ httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+
+ /*
+ * See if there are any options...
+ */
+
+ if ((options = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the options...
+ */
+
+ *options++ = '\0';
+ }
+
+ /*
+ * Open the serial port device...
+ */
+
+ do
+ {
+ if ((fd = open(resource, O_WRONLY | O_NOCTTY | O_EXCL | O_NDELAY)) == -1)
+ {
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fputs("INFO: Unable to open serial port, queuing on next printer in class...\n",
+ stderr);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (errno == EBUSY)
+ {
+ fputs("INFO: Serial port busy; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to open serial port device file \"%s\": %s\n",
+ resource, strerror(errno));
+ return (CUPS_BACKEND_FAILED);
+ }
+ }
+ }
+ while (fd < 0);
+
+ /*
+ * Set any options provided...
+ */
+
+ tcgetattr(fd, &origopts);
+ tcgetattr(fd, &opts);
+
+ opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
+ opts.c_oflag &= ~OPOST; /* Don't post-process */
+
+ bufsize = 96; /* 9600 baud / 10 bits/char / 10Hz */
+ dtrdsr = 0; /* No dtr/dsr flow control */
+
+ if (options != NULL)
+ while (*options)
+ {
+ /*
+ * Get the name...
+ */
+
+ for (ptr = name; *options && *options != '=';)
+ if (ptr < (name + sizeof(name) - 1))
+ *ptr++ = *options++;
+ *ptr = '\0';
+
+ if (*options == '=')
+ {
+ /*
+ * Get the value...
+ */
+
+ options ++;
+
+ for (ptr = value; *options && *options != '+' && *options != '&';)
+ if (ptr < (value + sizeof(value) - 1))
+ *ptr++ = *options++;
+ *ptr = '\0';
+
+ if (*options == '+')
+ options ++;
+ }
+ else
+ value[0] = '\0';
+
+ /*
+ * Process the option...
+ */
+
+ if (!strcasecmp(name, "baud"))
+ {
+ /*
+ * Set the baud rate...
+ */
+
+ bufsize = atoi(value) / 100;
+
+#if B19200 == 19200
+ cfsetispeed(&opts, atoi(value));
+ cfsetospeed(&opts, atoi(value));
+#else
+ switch (atoi(value))
+ {
+ case 1200 :
+ cfsetispeed(&opts, B1200);
+ cfsetospeed(&opts, B1200);
+ break;
+ case 2400 :
+ cfsetispeed(&opts, B2400);
+ cfsetospeed(&opts, B2400);
+ break;
+ case 4800 :
+ cfsetispeed(&opts, B4800);
+ cfsetospeed(&opts, B4800);
+ break;
+ case 9600 :
+ cfsetispeed(&opts, B9600);
+ cfsetospeed(&opts, B9600);
+ break;
+ case 19200 :
+ cfsetispeed(&opts, B19200);
+ cfsetospeed(&opts, B19200);
+ break;
+ case 38400 :
+ cfsetispeed(&opts, B38400);
+ cfsetospeed(&opts, B38400);
+ break;
+# ifdef B57600
+ case 57600 :
+ cfsetispeed(&opts, B57600);
+ cfsetospeed(&opts, B57600);
+ break;
+# endif /* B57600 */
+# ifdef B115200
+ case 115200 :
+ cfsetispeed(&opts, B115200);
+ cfsetospeed(&opts, B115200);
+ break;
+# endif /* B115200 */
+# ifdef B230400
+ case 230400 :
+ cfsetispeed(&opts, B230400);
+ cfsetospeed(&opts, B230400);
+ break;
+# endif /* B230400 */
+ default :
+ fprintf(stderr, "WARNING: Unsupported baud rate %s!\n", value);
+ break;
+ }
+#endif /* B19200 == 19200 */
+ }
+ else if (!strcasecmp(name, "bits"))
+ {
+ /*
+ * Set number of data bits...
+ */
+
+ switch (atoi(value))
+ {
+ case 7 :
+ opts.c_cflag &= ~CSIZE;
+ opts.c_cflag |= CS7;
+ opts.c_cflag |= PARENB;
+ opts.c_cflag &= ~PARODD;
+ break;
+ case 8 :
+ opts.c_cflag &= ~CSIZE;
+ opts.c_cflag |= CS8;
+ opts.c_cflag &= ~PARENB;
+ break;
+ }
+ }
+ else if (!strcasecmp(name, "parity"))
+ {
+ /*
+ * Set parity checking...
+ */
+
+ if (!strcasecmp(value, "even"))
+ {
+ opts.c_cflag |= PARENB;
+ opts.c_cflag &= ~PARODD;
+ }
+ else if (!strcasecmp(value, "odd"))
+ {
+ opts.c_cflag |= PARENB;
+ opts.c_cflag |= PARODD;
+ }
+ else if (!strcasecmp(value, "none"))
+ opts.c_cflag &= ~PARENB;
+ else if (!strcasecmp(value, "space"))
+ {
+ /*
+ * Note: we only support space parity with 7 bits per character...
+ */
+
+ opts.c_cflag &= ~CSIZE;
+ opts.c_cflag |= CS8;
+ opts.c_cflag &= ~PARENB;
+ }
+ else if (!strcasecmp(value, "mark"))
+ {
+ /*
+ * Note: we only support mark parity with 7 bits per character
+ * and 1 stop bit...
+ */
+
+ opts.c_cflag &= ~CSIZE;
+ opts.c_cflag |= CS7;
+ opts.c_cflag &= ~PARENB;
+ opts.c_cflag |= CSTOPB;
+ }
+ }
+ else if (!strcasecmp(name, "flow"))
+ {
+ /*
+ * Set flow control...
+ */
+
+ if (!strcasecmp(value, "none"))
+ {
+ opts.c_iflag &= ~(IXON | IXOFF);
+ opts.c_cflag &= ~CRTSCTS;
+ }
+ else if (!strcasecmp(value, "soft"))
+ {
+ opts.c_iflag |= IXON | IXOFF;
+ opts.c_cflag &= ~CRTSCTS;
+ }
+ else if (!strcasecmp(value, "hard") ||
+ !strcasecmp(value, "rtscts"))
+ {
+ opts.c_iflag &= ~(IXON | IXOFF);
+ opts.c_cflag |= CRTSCTS;
+ }
+ else if (!strcasecmp(value, "dtrdsr"))
+ {
+ opts.c_iflag &= ~(IXON | IXOFF);
+ opts.c_cflag &= ~CRTSCTS;
+
+ dtrdsr = 1;
+ }
+ }
+ else if (!strcasecmp(name, "stop"))
+ {
+ switch (atoi(value))
+ {
+ case 1 :
+ opts.c_cflag &= ~CSTOPB;
+ break;
+
+ case 2 :
+ opts.c_cflag |= CSTOPB;
+ break;
+ }
+ }
+ }
+
+ tcsetattr(fd, TCSANOW, &opts);
+ fcntl(fd, F_SETFL, 0);
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (argc < 7)
+ {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ if (bufsize > sizeof(buffer))
+ bufsize = sizeof(buffer);
+
+ wbytes = 0;
+
+ while (copies > 0)
+ {
+ copies --;
+
+ if (fp != 0)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ lseek(fp, 0, SEEK_SET);
+ }
+
+ if (dtrdsr)
+ {
+ /*
+ * Check the port and sleep until DSR is set...
+ */
+
+ int status;
+
+
+ if (!ioctl(fd, TIOCMGET, &status))
+ if (!(status & TIOCM_DSR))
+ {
+ /*
+ * Wait for DSR to go high...
+ */
+
+ fputs("DEBUG: DSR is low; waiting for device...\n", stderr);
+
+ do
+ {
+ sleep(1);
+ if (ioctl(fd, TIOCMGET, &status))
+ break;
+ }
+ while (!(status & TIOCM_DSR));
+
+ fputs("DEBUG: DSR is high; writing to device...\n", stderr);
+ }
+ }
+
+ tbytes = 0;
+ while ((nbytes = read(fp, buffer, bufsize)) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ tbytes += nbytes;
+ bufptr = buffer;
+
+ while (nbytes > 0)
+ {
+ /*
+ * See if we are ready to read or write...
+ */
+
+ do
+ {
+ FD_ZERO(&input);
+ FD_SET(fd, &input);
+ FD_ZERO(&output);
+ FD_SET(fd, &output);
+ }
+ while (select(fd + 1, &input, &output, NULL, NULL) < 0);
+
+ if (FD_ISSET(fd, &input))
+ {
+ /*
+ * Read backchannel data...
+ */
+
+ if ((rbytes = read(fd, resource, sizeof(resource))) > 0)
+ {
+ fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
+ rbytes);
+ cupsBackchannelWrite(resource, rbytes, 1.0);
+ }
+ }
+
+ if (FD_ISSET(fd, &output))
+ {
+ /*
+ * Write print data...
+ */
+
+ if ((wbytes = write(fd, bufptr, nbytes)) < 0)
+ if (errno == ENOTTY)
+ wbytes = write(fd, bufptr, nbytes);
+
+ if (wbytes < 0)
+ {
+ /*
+ * Check for retryable errors...
+ */
+
+ if (errno != EAGAIN && errno != EINTR)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Update count and pointer...
+ */
+
+ nbytes -= wbytes;
+ bufptr += wbytes;
+ }
+ }
+ }
+
+ if (wbytes < 0)
+ break;
+
+ if (argc > 6)
+ fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
+ (unsigned long)tbytes);
+ }
+ }
+
+ /*
+ * Close the serial port and input file and return...
+ */
+
+ tcsetattr(fd, TCSADRAIN, &origopts);
+
+ close(fd);
+ if (fp != 0)
+ close(fp);
+
+ return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
+}
+
+
+/*
+ * 'list_devices()' - List all serial devices.
+ */
+
+void
+list_devices(void)
+{
+#if defined(__hpux) || defined(__sgi) || defined(__sun) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ static char *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
+ /* Funky hex numbering used for some devices */
+#endif /* __hpux || __sgi || __sun || __FreeBSD__ || __OpenBSD__ */
+
+#if defined(__linux) || defined(linux) || defined(__linux__)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 100; i ++)
+ {
+ sprintf(device, "/dev/ttyS%d", i);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+# if defined(_ARCH_PPC) || defined(powerpc) || defined(__powerpc)
+ printf("serial serial:%s?baud=230400 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+# else
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+# endif /* _ARCH_PPC || powerpc || __powerpc */
+ }
+ }
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(device, "/dev/usb/ttyUSB%d", i);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=230400 \"Unknown\" \"USB Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+#elif defined(__sgi)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+ inventory_t *inv; /* Hardware inventory info */
+
+
+ /*
+ * IRIX maintains a hardware inventory of most devices...
+ */
+
+ setinvent();
+
+ while ((inv = getinvent()) != NULL)
+ {
+ if (inv->inv_class == INV_SERIAL)
+ {
+ /*
+ * Some sort of serial port...
+ */
+
+ if (inv->inv_type == INV_CDSIO || inv->inv_type == INV_CDSIO_E)
+ {
+ /*
+ * CDSIO port...
+ */
+
+ for (n = 0; n < 6; n ++)
+ printf("serial serial:/dev/ttyd%d?baud=38400 \"Unknown\" \"CDSIO Board %d Serial Port #%d\"\n",
+ n + 5 + 8 * inv->inv_controller, inv->inv_controller, n + 1);
+ }
+ else if (inv->inv_type == INV_EPC_SERIAL)
+ {
+ /*
+ * Everest serial port...
+ */
+
+ if (inv->inv_unit == 0)
+ i = 1;
+ else
+ i = 41 + 4 * (int)inv->inv_controller;
+
+ for (n = 0; n < (int)inv->inv_state; n ++)
+ printf("serial serial:/dev/ttyd%d?baud=38400 \"Unknown\" \"EPC Serial Port %d, Ebus slot %d\"\n",
+ n + i, n + 1, (int)inv->inv_controller);
+ }
+ else if (inv->inv_state > 1)
+ {
+ /*
+ * Standard serial port under IRIX 6.4 and earlier...
+ */
+
+ for (n = 0; n < (int)inv->inv_state; n ++)
+ printf("serial serial:/dev/ttyd%d?baud=38400 \"Unknown\" \"Onboard Serial Port %d\"\n",
+ n + (int)inv->inv_unit + 1, n + (int)inv->inv_unit + 1);
+ }
+ else
+ {
+ /*
+ * Standard serial port under IRIX 6.5 and beyond...
+ */
+
+ printf("serial serial:/dev/ttyd%d?baud=115200 \"Unknown\" \"Onboard Serial Port %d\"\n",
+ (int)inv->inv_controller, (int)inv->inv_controller);
+ }
+ }
+ }
+
+ endinvent();
+
+ /*
+ * Central Data makes serial and parallel "servers" that can be
+ * connected in a number of ways. Look for ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/ttydn%d%c", j, funky_hex[n]);
+ else if (i == 9) /* PCI */
+ sprintf(device, "/dev/ttydp%d%c", j, funky_hex[n]);
+ else /* SCSI */
+ sprintf(device, "/dev/ttyd%d%d%c", i, j, funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data EtherLite Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else if (i == 9)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data PCI Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data SCSI Serial Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__sun)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard serial ports...
+ */
+
+ for (i = 0; i < 26; i ++)
+ {
+ sprintf(device, "/dev/cua/%c", 'a' + i);
+ if (access(device, 0) == 0)
+#ifdef B115200
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+#else
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+#endif /* B115200 */
+ }
+
+ /*
+ * MAGMA serial ports...
+ */
+
+ for (i = 0; i < 40; i ++)
+ {
+ sprintf(device, "/dev/term/%02d", i);
+ if (access(device, 0) == 0)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"MAGMA Serial Board #%d Port #%d\"\n",
+ device, (i / 10) + 1, (i % 10) + 1);
+ }
+
+ /*
+ * Central Data serial ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/sts/ttyN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/sts/tty%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data EtherLite Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data SCSI Serial Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__hpux)
+ int i, j, n; /* Looping vars */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard serial ports...
+ */
+
+ for (i = 0; i < 10; i ++)
+ {
+ sprintf(device, "/dev/tty%dp0", i);
+ if (access(device, 0) == 0)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+ }
+
+ /*
+ * Central Data serial ports...
+ */
+
+ for (i = 0; i < 9; i ++)
+ for (j = 0; j < 8; j ++)
+ for (n = 0; n < 32; n ++)
+ {
+ if (i == 8) /* EtherLite */
+ sprintf(device, "/dev/ttyN%d%c", j, funky_hex[n]);
+ else
+ sprintf(device, "/dev/tty%c%d%c", i + 'C', j,
+ funky_hex[n]);
+
+ if (access(device, 0) == 0)
+ {
+ if (i == 8)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data EtherLite Serial Port, ID %d, port %d\"\n",
+ device, j, n);
+ else
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data SCSI Serial Port, logical bus %d, ID %d, port %d\"\n",
+ device, i, j, n);
+ }
+ }
+#elif defined(__osf__)
+ int i; /* Looping var */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard serial ports...
+ */
+
+ for (i = 0; i < 100; i ++)
+ {
+ sprintf(device, "/dev/tty%02d", i);
+ if (access(device, 0) == 0)
+ printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+ }
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+ int i, j; /* Looping vars */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * SIO ports...
+ */
+
+ for (i = 0; i < 32; i ++)
+ {
+ sprintf(device, "/dev/ttyd%c", funky_hex[i]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Standard Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+
+ /*
+ * Cyclades ports...
+ */
+
+ for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
+ for (j = 0; j < 32; j ++)
+ {
+ sprintf(device, "/dev/ttyc%d%c", i, funky_hex[j]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Port #%d\"\n",
+ device, i, j + 1);
+ }
+
+ sprintf(device, "/dev/ttyC%d%c", i, funky_hex[j]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Port #%d\"\n",
+ device, i, j + 1);
+ }
+ }
+
+ /*
+ * Digiboard ports...
+ */
+
+ for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
+ for (j = 0; j < 32; j ++)
+ {
+ sprintf(device, "/dev/ttyD%d%c", i, funky_hex[j]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Digiboard #%d Serial Port #%d\"\n",
+ device, i, j + 1);
+ }
+ }
+
+ /*
+ * Stallion ports...
+ */
+
+ for (i = 0; i < 32; i ++)
+ {
+ sprintf(device, "/dev/ttyE%c", funky_hex[i]);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Stallion Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+
+ /*
+ * SX ports...
+ */
+
+ for (i = 0; i < 128; i ++)
+ {
+ sprintf(device, "/dev/ttyA%d", i + 1);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"SX Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+#elif defined(__NetBSD__)
+ int i, j; /* Looping vars */
+ int fd; /* File descriptor */
+ char device[255]; /* Device filename */
+
+
+ /*
+ * Standard serial ports...
+ */
+
+ for (i = 0; i < 4; i ++)
+ {
+ sprintf(device, "/dev/tty%02d", i);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Serial Port #%d\"\n",
+ device, i + 1);
+ }
+ }
+
+ /*
+ * Cyclades-Z ports...
+ */
+
+ for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
+ for (j = 0; j < 64; j ++)
+ {
+ sprintf(device, "/dev/ttyCZ%02d%02d", i, j);
+ if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+ {
+ close(fd);
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Prt #%d\"\n",
+ device, i, j + 1);
+ }
+ }
+#elif defined(__APPLE__)
+ /*
+ * Standard serial ports on MacOS X...
+ */
+
+ kern_return_t kernResult;
+ mach_port_t masterPort;
+ io_iterator_t serialPortIterator;
+ CFMutableDictionaryRef classesToMatch;
+ io_object_t serialService;
+
+ printf("serial serial \"Unknown\" \"Serial Printer (serial)\"\n");
+
+ kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
+ if (KERN_SUCCESS != kernResult)
+ return;
+
+ /*
+ * Serial devices are instances of class IOSerialBSDClient.
+ */
+
+ classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if (classesToMatch != NULL)
+ {
+ CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDRS232Type));
+
+ kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch,
+ &serialPortIterator);
+ if (kernResult == KERN_SUCCESS)
+ {
+ while ((serialService = IOIteratorNext(serialPortIterator)))
+ {
+ CFTypeRef serialNameAsCFString;
+ CFTypeRef bsdPathAsCFString;
+ char serialName[128];
+ char bsdPath[1024];
+ Boolean result;
+
+
+ serialNameAsCFString =
+ IORegistryEntryCreateCFProperty(serialService,
+ CFSTR(kIOTTYDeviceKey),
+ kCFAllocatorDefault, 0);
+ if (serialNameAsCFString)
+ {
+ result = CFStringGetCString(serialNameAsCFString, serialName,
+ sizeof(serialName),
+ kCFStringEncodingASCII);
+ CFRelease(serialNameAsCFString);
+
+ if (result)
+ {
+ bsdPathAsCFString =
+ IORegistryEntryCreateCFProperty(serialService,
+ CFSTR(kIOCalloutDeviceKey),
+ kCFAllocatorDefault, 0);
+ if (bsdPathAsCFString)
+ {
+ result = CFStringGetCString(bsdPathAsCFString, bsdPath,
+ sizeof(bsdPath),
+ kCFStringEncodingASCII);
+ CFRelease(bsdPathAsCFString);
+
+ if (result)
+ printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n", bsdPath,
+ serialName);
+ }
+ }
+ }
+
+ IOObjectRelease(serialService);
+ }
+
+ IOObjectRelease(serialPortIterator); /* Release the iterator. */
+ }
+ }
+#endif
+}
+
+
+/*
+ * End of "$Id: serial.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/backend/socket.c b/backend/socket.c
new file mode 100644
index 000000000..54b6a7796
--- /dev/null
+++ b/backend/socket.c
@@ -0,0 +1,418 @@
+/*
+ * "$Id: socket.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * AppSocket backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef WIN32
+# include
+#else
+# include
+# include
+# include
+# include
+# include
+# include
+#endif /* WIN32 */
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024]; /* Resource info (not used) */
+ int fp; /* Print file */
+ int copies; /* Number of copies to print */
+ int port; /* Port number */
+ char portname[255]; /* Port name */
+ int delay; /* Delay for retries... */
+ int fd; /* AppSocket */
+ int error; /* Error code (if any) */
+ http_addrlist_t *addrlist; /* Address list */
+ int rbytes; /* Number of bytes read */
+ int wbytes; /* Number of bytes written */
+ int nbytes; /* Number of bytes read */
+ size_t tbytes; /* Total number of bytes written */
+ char buffer[8192], /* Output buffer */
+ *bufptr; /* Pointer into buffer */
+ struct timeval timeout; /* Timeout for select() */
+ fd_set input, /* Input set for select() */
+ output; /* Output set for select() */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore SIGPIPE signals...
+ */
+
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ puts("network socket \"Unknown\" \"AppSocket/HP JetDirect\"");
+ return (CUPS_BACKEND_OK);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
+ argv[0]);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ {
+ fp = 0;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = open(argv[6], O_RDONLY)) < 0)
+ {
+ perror("ERROR: unable to open print file");
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * Extract the hostname and port number from the URI...
+ */
+
+ httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+
+ if (port == 0)
+ port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
+
+ /*
+ * Then try to connect to the remote host...
+ */
+
+ sprintf(portname, "%d", port);
+
+ if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to locate printer \'%s\'!\n", hostname);
+ return (CUPS_BACKEND_STOP);
+ }
+
+ fprintf(stderr, "INFO: Attempting to connect to host %s on port %d\n",
+ hostname, port);
+
+ wbytes = 0;
+
+ while (copies > 0)
+ {
+ for (delay = 5;;)
+ {
+ if (!httpAddrConnect(addrlist, &fd))
+ {
+ error = errno;
+ fd = -1;
+
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fprintf(stderr, "INFO: Unable to connect to \"%s\", queuing on next printer in class...\n",
+ hostname);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (error == ECONNREFUSED || error == EHOSTDOWN ||
+ error == EHOSTUNREACH)
+ {
+ fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in %d seconds...\n",
+ hostname, delay);
+ sleep(delay);
+
+ if (delay < 30)
+ delay += 5;
+ }
+ else
+ {
+ perror("ERROR: Unable to connect to printer (retrying in 30 seconds)");
+ sleep(30);
+ }
+ }
+ else
+ break;
+ }
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (argc < 7)
+ {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ copies --;
+
+ if (fp != 0)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ lseek(fp, 0, SEEK_SET);
+ }
+
+ fputs("INFO: Connected to host, sending print job...\n", stderr);
+
+ tbytes = 0;
+ while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ tbytes += nbytes;
+ bufptr = buffer;
+
+ while (nbytes > 0)
+ {
+ /*
+ * See if we are ready to read or write...
+ */
+
+ do
+ {
+ FD_ZERO(&input);
+ FD_SET(fd, &input);
+ FD_ZERO(&output);
+ FD_SET(fd, &output);
+ }
+ while (select(fd + 1, &input, &output, NULL, NULL) < 0);
+
+ if (FD_ISSET(fd, &input))
+ {
+ /*
+ * Read backchannel data...
+ */
+
+ if ((rbytes = recv(fd, resource, sizeof(resource), 0)) > 0)
+ {
+ fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
+ rbytes);
+ cupsBackchannelWrite(resource, rbytes, 1.0);
+ }
+ }
+
+ if (FD_ISSET(fd, &output))
+ {
+ /*
+ * Write print data...
+ */
+
+ if ((wbytes = send(fd, bufptr, nbytes, 0)) < 0)
+ {
+ /*
+ * Check for retryable errors...
+ */
+
+ if (errno != EAGAIN && errno != EINTR)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Update count and pointer...
+ */
+
+ nbytes -= wbytes;
+ bufptr += wbytes;
+ }
+ }
+ }
+
+ if (wbytes < 0)
+ break;
+
+ if (argc > 6)
+ fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
+ (unsigned long)tbytes);
+ }
+
+ /*
+ * Shutdown the socket and wait for the other end to finish...
+ */
+
+ fputs("INFO: Print file sent, waiting for printer to finish...\n", stderr);
+
+ shutdown(fd, 1);
+
+ for (;;)
+ {
+ /*
+ * Wait a maximum of 90 seconds for backchannel data or a closed
+ * connection...
+ */
+
+ timeout.tv_sec = 90;
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&input);
+ FD_SET(fd, &input);
+
+#ifdef __hpux
+ if (select(fd + 1, (int *)&input, NULL, NULL, &timeout) > 0)
+#else
+ if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
+#endif /* __hpux */
+ {
+ /*
+ * Grab the data coming back and spit it out to stderr...
+ */
+
+ if ((rbytes = recv(fd, resource, sizeof(resource), 0)) > 0)
+ {
+ fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
+ rbytes);
+ cupsBackchannelWrite(resource, rbytes, 1.0);
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+
+ /*
+ * Close the socket connection...
+ */
+
+ close(fd);
+ }
+
+ httpAddrFreeList(addrlist);
+
+ /*
+ * Close the input file and return...
+ */
+
+ if (fp != 0)
+ close(fp);
+
+ if (wbytes >= 0)
+ fputs("INFO: Ready to print.\n", stderr);
+
+ return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
+}
+
+
+/*
+ * End of "$Id: socket.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/backend/test1284.c b/backend/test1284.c
new file mode 100644
index 000000000..20a505d39
--- /dev/null
+++ b/backend/test1284.c
@@ -0,0 +1,95 @@
+/*
+ * "$Id: test1284.c 4903 2006-01-10 20:02:46Z mike $"
+ *
+ * IEEE-1284 support functions test program for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include
+#include
+#include
+#ifdef WIN32
+# include
+#else
+# include
+# include
+#endif /* WIN32 */
+#define DEBUG
+#include "ieee1284.c"
+
+
+/*
+ * 'main()' - Test the device-ID functions.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i, /* Looping var */
+ fd; /* File descriptor */
+ char device_id[1024], /* 1284 device ID string */
+ make_model[1024], /* make-and-model string */
+ uri[1024]; /* URI string */
+
+
+ if (argc < 2)
+ {
+ puts("Usage: test1284 device-file [... device-file-N]");
+ exit(1);
+ }
+
+ for (i = 1; i < argc; i ++)
+ {
+ if ((fd = open(argv[i], O_RDWR)) < 0)
+ {
+ perror(argv[i]);
+ return (errno);
+ }
+
+ printf("%s:\n", argv[i]);
+
+ get_device_id(fd, device_id, sizeof(device_id), make_model,
+ sizeof(make_model), "test", uri, sizeof(uri));
+
+ printf(" device_id=\"%s\"\n", device_id);
+ printf(" make_model=\"%s\"\n", make_model);
+ printf(" uri=\"%s\"\n", uri);
+
+ close(fd);
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: test1284.c 4903 2006-01-10 20:02:46Z mike $".
+ */
diff --git a/backend/usb-darwin.c b/backend/usb-darwin.c
new file mode 100644
index 000000000..5c2ee4f4f
--- /dev/null
+++ b/backend/usb-darwin.c
@@ -0,0 +1,1899 @@
+/*
+ * "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $"
+ *
+ * USB port on Darwin backend for the Common UNIX Printing System (CUPS).
+ *
+ * This file is included from "usb.c" when compiled on MacOS X or Darwin.
+ *
+ * Copyright 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * IMPORTANT: This Apple software is supplied to you by Apple Computer,
+ * Inc. ("Apple") in consideration of your agreement to the following
+ * terms, and your use, installation, modification or redistribution of
+ * this Apple software constitutes acceptance of these terms. If you do
+ * not agree with these terms, please do not use, install, modify or
+ * redistribute this Apple software.
+ *
+ * In consideration of your agreement to abide by the following terms, and
+ * subject to these terms, Apple grants you a personal, non-exclusive
+ * license, under Apple/s copyrights in this original Apple software (the
+ * "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ * Software, with or without modifications, in source and/or binary forms;
+ * provided that if you redistribute the Apple Software in its entirety and
+ * without modifications, you must retain this notice and the following
+ * text and disclaimers in all such redistributions of the Apple Software.
+ * Neither the name, trademarks, service marks or logos of Apple Computer,
+ * Inc. may be used to endorse or promote products derived from the Apple
+ * Software without specific prior written permission from Apple. Except
+ * as expressly stated in this notice, no other rights or licenses, express
+ * or implied, are granted by Apple herein, including but not limited to
+ * any patent rights that may be infringed by your derivative works or by
+ * other works in which the Apple Software may be incorporated.
+ *
+ * The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+ *
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Contents:
+ *
+ * print_device() - Send a file to the specified USB port.
+ * list_devices() - List all USB devices.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* Used for writegReadMutex */
+
+#ifndef kPMPrinterURI
+# define kPMPrinterURI CFSTR("Printer URI")
+#endif
+
+/*
+ * Panther/10.3 kIOUSBInterfaceInterfaceID190
+ * Jaguar/10.2 kIOUSBInterfaceInterfaceID182
+ */
+
+#define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
+#define kUSBLanguageEnglish 0x409
+
+/*
+ * Section 5.3 USB Printing Class spec
+ */
+
+#define kUSBPrintingSubclass 1
+#define kUSBPrintingProtocolNoOpen 0
+#define kUSBPrintingProtocolUnidirectional 1
+#define kUSBPrintingProtocolBidirectional 2
+
+#define kUSBPrintClassGetDeviceID 0
+#define kUSBPrintClassGetCentronicsStatus 1
+#define kUSBPrintClassSoftReset 2
+
+/*
+ * Apple MacOS X printer-class plugins
+ */
+
+#define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
+
+#define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
+
+#define kUSBGenericPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin")
+#define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
+
+#define kUSBClassDriverProperty CFSTR("USB Printing Class")
+#define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
+
+typedef union
+{
+ char b;
+ struct
+ {
+ unsigned reserved0 : 2;
+ unsigned paperError : 1;
+ unsigned select : 1;
+ unsigned notError : 1;
+ unsigned reserved1 : 3;
+ } status;
+} CentronicsStatusByte;
+
+typedef struct
+{
+ CFStringRef manufacturer; /* manufacturer name */
+ CFStringRef product; /* product name */
+ CFStringRef compatible; /* compatible product name */
+ CFStringRef serial; /* serial number */
+ CFStringRef command; /* command set */
+ CFStringRef ppdURL; /* url of the selected PPD, if any */
+} USBPrinterAddress;
+
+typedef IOUSBInterfaceInterface190 **USBPrinterInterface;
+
+typedef struct
+{
+ UInt8 requestType;
+ UInt8 request;
+ UInt16 value;
+ UInt16 index;
+ UInt16 length;
+ void *buffer;
+} USBIODeviceRequest;
+
+typedef struct classDriverContext
+{
+ IUNKNOWN_C_GUTS;
+ CFPlugInRef plugin; /* release plugin */
+ IUnknownVTbl **factory;
+ void *vendorReference;/* vendor class specific usage */
+ UInt32 location; /* unique location in bus topology */
+ UInt8 interfaceNumber;
+ UInt16 vendorID;
+ UInt16 productID;
+ USBPrinterInterface interface; /* identify the device to IOKit */
+ UInt8 outpipe; /* mandatory bulkOut pipe */
+ UInt8 inpipe; /* optional bulkIn pipe */
+ /*
+ ** general class requests
+ */
+ kern_return_t (*DeviceRequest)( struct classDriverContext **printer, USBIODeviceRequest *iorequest, UInt16 timeout );
+ kern_return_t (*GetString)( struct classDriverContext **printer, UInt8 whichString, UInt16 language, UInt16 timeout, CFStringRef *result );
+ /*
+ ** standard printer class requests
+ */
+ kern_return_t (*SoftReset)( struct classDriverContext **printer, UInt16 timeout );
+ kern_return_t (*GetCentronicsStatus)( struct classDriverContext **printer, CentronicsStatusByte *result, UInt16 timeout );
+ kern_return_t (*GetDeviceID)( struct classDriverContext **printer, CFStringRef *devid, UInt16 timeout );
+ /*
+ ** standard bulk device requests
+ */
+ kern_return_t (*ReadPipe)( struct classDriverContext **printer, UInt8 *buffer, UInt32 *count );
+ kern_return_t (*WritePipe)( struct classDriverContext **printer, UInt8 *buffer, UInt32 *count, Boolean eoj );
+ /*
+ ** interface requests
+ */
+ kern_return_t (*Open)( struct classDriverContext **printer, UInt32 location, UInt8 protocol );
+ kern_return_t (*Abort)( struct classDriverContext **printer );
+ kern_return_t (*Close)( struct classDriverContext **printer );
+ /*
+ ** initialize and terminate
+ */
+ kern_return_t (*Initialize)( struct classDriverContext **printer, struct classDriverContext **baseclass );
+ kern_return_t (*Terminate)( struct classDriverContext **printer );
+} USBPrinterClassContext;
+
+
+typedef struct usbPrinterClassType
+{
+ USBPrinterClassContext *classdriver;
+ CFUUIDRef factoryID;
+ UInt32 refCount;
+} USBPrinterClassType;
+
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Constants
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+/*
+ Debugging output to Console
+ DEBUG undefined or
+ DEBUG=0 production code: suppress console output
+
+ DEBUG=1 report errors (non-zero results)
+ DEBUG=2 report all results, generate dumps
+*/
+#if DEBUG==2
+#define DEBUG_ERR(c, x) showint(x, c)
+#define DEBUG_DUMP( text, buf, len ) dump( text, buf, len )
+#define DEBUG_CFString( text, a ) showcfstring( text, a )
+#define DEBUG_CFCompareString( text, a, b ) cmpcfs( text, a, b )
+#elif DEBUG==1
+#define DEBUG_ERR(c, x) if (c) fprintf(stderr, x, c)
+#define DEBUG_DUMP( text, buf, len )
+#define DEBUG_CFString( text, a )
+#define DEBUG_CFCompareString( text, a, b )
+#else
+#define DEBUG_ERR(c, x)
+#define DEBUG_DUMP( text, buf, len )
+#define DEBUG_CFString( text, a )
+#define DEBUG_CFCompareString( text, a, b )
+#endif
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Type Definitions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+typedef struct
+{
+ /*
+ * Tagged/Tranparent Binary Communications Protocol
+ * TBCP read
+ */
+ Boolean tbcpQuoteReads; /* enable tbcp on reads */
+ Boolean escapeNextRead; /* last char of last read buffer was escape */
+ UInt8 *tbcpReadData; /* read buffer */
+ UInt32 readLength; /* read buffer length (all used) */
+ int match_endoffset, /* partial match of end TBCP sequence */
+ match_startoffset; /* partial match of start TBCP sequence */
+ /*
+ * TBCP write
+ */
+ UInt8 *tbcpWriteData; /* write buffer */
+ UInt32 tbcpBufferLength, /* write buffer allocated length */
+ tbcpBufferRemaining; /* write buffer not used */
+
+ Boolean sendStatusNextWrite;
+
+} PostScriptData;
+
+typedef struct
+{
+ CFPlugInRef plugin; /* valid until plugin is release */
+ USBPrinterClassContext **classdriver; /* usb printer class in user space */
+ CFStringRef bundle; /* class driver URI */
+ UInt32 location; /* unique location in USB topology */
+ USBPrinterAddress address; /* topology independent bus address */
+ CFURLRef reference; /* internal use */
+} USBPrinterInfo;
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+/*
+** IOKit to CF functions
+*/
+USBPrinterInfo *UsbCopyPrinter( USBPrinterInfo *aPrinter );
+CFMutableArrayRef UsbGetAllPrinters( void );
+void UsbReleasePrinter( USBPrinterInfo *aPrinter );
+void UsbReleaseAllPrinters( CFMutableArrayRef printers );
+kern_return_t UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result );
+kern_return_t UsbUnloadClassDriver( USBPrinterInfo *printer );
+kern_return_t UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle );
+CFStringRef UsbMakeFullUriAddress( USBPrinterInfo *aPrinter );
+
+int UsbSamePrinter( const USBPrinterAddress *lastTime, const USBPrinterAddress *thisTime );
+
+OSStatus UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout );
+
+
+/*******************************************************************************
+ Contains: Support IEEE-1284 DeviceID as a CFString.
+
+ Copyright 2000-2005 by Apple Computer, Inc., all rights reserved.
+
+ Description:
+ IEEE-1284 Device ID is referenced in USB and PPDT (1394.3). It allows
+ a computer peripheral to convey information about its required software
+ to the host system.
+
+ DeviceID is defined as a stream of ASCII bytes, commencing with one 16-bit
+ binary integer in Little-Endian format which describes how many bytes
+ of data are required by the entire DeviceID.
+
+ The stream of bytes is further characterized as a series of
+ key-value list pairs. In other words each key can be followed by one
+ or more values. Multiple key-value list pairs fill out the DeviceID stream.
+
+ Some keys are required: COMMAND SET (or CMD), MANUFACTURER (or MFG),
+ and MODEL (or MDL).
+
+ One needs to read the first two bytes of DeviceID to allocate storage
+ for the complete DeviceID string. Then a second read operation can
+ retrieve the entire string.
+
+ Often DeviceID is not very large. By allocating a reasonable buffer one
+ can fetch most device's DeviceID string on the first read.
+
+ A more formal definition of DeviceID.
+
+ = +
+
+ =
+ = :[,]*;
+
+ = +
+ = +
+
+ Some keys are defined in the standard. The standard specifies that
+ keys are case sensitive. White space is allowed in the key.
+
+ The standard does not say that values are case-sensitive.
+ Lexmark is known to ship printers with mixed-case value:
+ i.e., 'CLASS:Printer'
+
+ Required Keys:
+ 'COMMAND SET' or CMD
+ MANUFACTURER or MFG
+ MODEL or MDL
+
+ Optional Keys:
+ CLASS
+ Value PRINTER is referenced in the standard.
+
+ Observed Keys:
+ SN,SERN
+ Used by Hewlett-Packard for the serial number.
+
+
+*******************************************************************************/
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Pragmas
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Constants
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+#define kDeviceIDKeyCommand CFSTR("COMMAND SET:")
+#define kDeviceIDKeyCommandAbbrev CFSTR( "CMD:" )
+
+#define kDeviceIDKeyManufacturer CFSTR("MANUFACTURER:")
+#define kDeviceIDKeyManufacturerAbbrev CFSTR( "MFG:" )
+
+#define kDeviceIDKeyModel CFSTR("MODEL:")
+#define kDeviceIDKeyModelAbbrev CFSTR( "MDL:" )
+
+#define kDeviceIDKeySerial CFSTR("SN:")
+#define kDeviceIDKeySerialAbbrev CFSTR("SERN:")
+
+#define kDeviceIDKeyCompatible CFSTR("COMPATIBLITY ID:")
+#define kDeviceIDKeyCompatibleAbbrev CFSTR("CID:")
+
+/* delimiters */
+#define kDeviceIDKeyValuePairDelimiter CFSTR(";")
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Type definitions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Function prototypes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+static CFStringRef CreateEncodedCFString(CFStringRef string);
+static CFRange DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options );
+static void parseOptions(const char *options, char *serial);
+
+CFStringRef
+DeviceIDCreateValueList( const CFStringRef deviceID,
+ const CFStringRef abbrevKey,
+ const CFStringRef key );
+
+static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax);
+static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax);
+
+/* Required to suppress redefinition warnings for these two symbols
+*/
+#if defined(TCP_NODELAY)
+#undef TCP_NODELAY
+#endif
+#if defined(TCP_MAXSEG)
+#undef TCP_MAXSEG
+#endif
+
+#include
+
+
+#define PRINTER_POLLING_INTERVAL 5 /* seconds */
+#define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
+#define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
+
+/* WAITEOF_DELAY is number of seconds we'll wait for responses from the printer */
+/* after we've finished sending all the data */
+#define WAITEOF_DELAY 7
+
+#define USB_MAX_STR_SIZE 1024
+
+
+static volatile int done = 0;
+static int gWaitEOF = false;
+static pthread_cond_t *gReadCompleteConditionPtr = NULL;
+static pthread_mutex_t *gReadMutexPtr = NULL;
+
+
+
+#if DEBUG==2
+
+static char
+hexdigit( char c )
+{
+ return ( c < 0 || c > 15 )? '?': (c < 10)? c + '0': c - 10 + 'A';
+}
+
+static char
+asciidigit( char c )
+{
+ return (c< 20 || c > 0x7E)? '.': c;
+}
+
+void
+dump( char *text, void *s, int len )
+{
+ int i;
+ char *p = (char *) s;
+ char m[1+2*16+1+16+1];
+
+ fprintf( stderr, "%s pointer %x len %d\n", text, (unsigned int) p, len );
+
+ for ( ; len > 0; len -= 16 )
+ {
+ char *q = p;
+ char *out = m;
+ *out++ = '\t';
+ for ( i = 0; i < 16 && i < len; ++i, ++p )
+ {
+ *out++ = hexdigit( (*p >> 4) & 0x0F );
+ *out++ = hexdigit( *p & 0x0F );
+ }
+ for ( ;i < 16; ++i )
+ {
+ *out++ = ' ';
+ *out++ = ' ';
+ }
+ *out++ = '\t';
+ for ( i = 0; i < 16 && i < len; ++i, ++q )
+ *out++ = asciidigit( *q );
+ *out = 0;
+ m[ strlen( m ) ] = '\0';
+ fprintf( stderr, "%s\n", m );
+ }
+}
+
+void
+printcfs( char *text, CFStringRef s )
+{
+ char dest[1024];
+ if ( s != NULL )
+ {
+ if ( CFStringGetCString(s, dest, sizeof(dest), kCFStringEncodingUTF8) )
+ sprintf( dest, "%s <%s>\n", text, dest );
+ else
+ sprintf( dest, "%s [Unknown string]\n", text );
+ } else {
+ sprintf( dest, "%s [NULL]\n", text );
+ }
+ perror( dest );
+}
+
+void
+cmpcfs( char *text, CFStringRef a, CFStringRef b )
+{
+ CFRange found = {0, 0};
+
+ printcfs( text, a );
+ printcfs( " ", b );
+
+ if (a != NULL && b != NULL) {
+ found = CFStringFind( a, b, kCFCompareCaseInsensitive );
+
+ } else if (a == NULL && b == NULL) {
+ found.length = 1; /* Match */
+ found.location = 0;
+ } else {
+ found.length = 0; /* No match. */
+ }
+
+ if ( found.length > 0 )
+ fprintf( stderr, "matched @%d:%d\n", (int) found.location, (int) found.length);
+ else
+ fprintf( stderr, "not matched\n" );
+}
+#endif /*DEBUG==2 */
+
+#ifdef PARSE_PS_ERRORS
+static const char *nextLine (const char *buffer);
+static void parsePSError (char *sockBuffer, int len);
+
+
+static const char *nextLine (const char *buffer)
+{
+ const char *cptr, *lptr = NULL;
+ for (cptr = buffer; *cptr && lptr == NULL; cptr++)
+ if (*cptr == '\n' || *cptr == '\r')
+ lptr = cptr;
+ return lptr;
+}
+
+static void parsePSError (char *sockBuffer, int len)
+{
+ static char gErrorBuffer[1024] = "";
+ static char *gErrorBufferPtr = gErrorBuffer;
+ static char *gErrorBufferEndPtr = gErrorBuffer + sizeof(gErrorBuffer);
+
+ char *pCommentBegin, *pCommentEnd, *pLineEnd;
+ char *logLevel;
+ char logstr[1024];
+ int logstrlen;
+
+ if (gErrorBufferPtr + len > gErrorBufferEndPtr - 1)
+ gErrorBufferPtr = gErrorBuffer;
+ if (len > sizeof(gErrorBuffer) - 1)
+ len = sizeof(gErrorBuffer) - 1;
+
+ memcpy(gErrorBufferPtr, (const void *)sockBuffer, len);
+ gErrorBufferPtr += len;
+ *(gErrorBufferPtr + 1) = '\0';
+
+
+ pLineEnd = (char *)nextLine((const char *)gErrorBuffer);
+ while (pLineEnd != NULL)
+ {
+ *pLineEnd++ = '\0';
+
+ pCommentBegin = strstr(gErrorBuffer,"%%[");
+ pCommentEnd = strstr(gErrorBuffer, "]%%");
+ if (pCommentBegin != gErrorBuffer && pCommentEnd != NULL)
+ {
+ pCommentEnd += 3; /* Skip past "]%%" */
+ *pCommentEnd = '\0'; /* There's always room for the nul */
+
+ if (strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
+ logLevel = "DEBUG";
+ else if (strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
+ logLevel = "DEBUG";
+ else
+ logLevel = "INFO";
+
+ if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pCommentBegin)) >= sizeof(logstr))
+ {
+ /* If the string was trucnated make sure it has a linefeed before the nul */
+ logstrlen = sizeof(logstr) - 1;
+ logstr[logstrlen - 1] = '\n';
+ }
+ write(STDERR_FILENO, logstr, logstrlen);
+ }
+
+ /* move everything over... */
+ strcpy(gErrorBuffer, pLineEnd);
+ gErrorBufferPtr = gErrorBuffer;
+ pLineEnd = (char *)nextLine((const char *)gErrorBuffer);
+ }
+}
+#endif /* PARSE_PS_ERRORS */
+
+void *
+readthread( void *reference )
+{
+ /*
+ ** post a read to the device and write results to stdout
+ ** the final pending read will be Aborted in the main thread
+ */
+ UInt8 readbuffer[512];
+ UInt32 rbytes;
+ kern_return_t readstatus;
+ USBPrinterClassContext **classdriver = (USBPrinterClassContext **) reference;
+
+
+ do
+ {
+ rbytes = sizeof(readbuffer) - 1;
+ readstatus = (*classdriver)->ReadPipe( classdriver, readbuffer, &rbytes );
+ if ( kIOReturnSuccess == readstatus && rbytes > 0 )
+ {
+ write( STDOUT_FILENO, readbuffer, rbytes );
+ /* cntrl-d is echoed by the printer.
+ * NOTES:
+ * Xerox Phaser 6250D doesn't echo the cntrl-d.
+ * Xerox Phaser 6250D doesn't always send the product query.
+ */
+ if (gWaitEOF && readbuffer[rbytes-1] == 0x4)
+ break;
+#ifdef PARSE_PS_ERRORS
+ parsePSError(readbuffer, rbytes);
+#endif
+ }
+ } while ( gWaitEOF || !done ); /* Abort from main thread tests error here */
+
+ /* Let the other thread (main thread) know that we have
+ * completed the read thread...
+ */
+ pthread_mutex_lock(gReadMutexPtr);
+ pthread_cond_signal(gReadCompleteConditionPtr);
+ pthread_mutex_unlock(gReadMutexPtr);
+
+ return NULL;
+}
+
+/*
+* 'print_device()' - Send a file to the specified USB port.
+*/
+
+int print_device(const char *uri, const char *hostname, const char *resource, const char *options, int fd, int copies)
+{
+ UInt32 wbytes, /* Number of bytes written */
+ buffersize = 2048;
+ size_t nbytes; /* Number of bytes read */
+ off_t tbytes; /* Total number of bytes written */
+ char *buffer, /* Output buffer */
+ *bufptr; /* Pointer into buffer */
+
+ pthread_cond_t readCompleteCondition;
+ pthread_mutex_t readMutex;
+ pthread_t thr;
+ int thread_created = 0;
+
+ USBPrinterInfo *targetPrinter = NULL;
+ CFMutableArrayRef usbPrinters;
+ char manufacturer_buf[USB_MAX_STR_SIZE],
+ product_buf[USB_MAX_STR_SIZE],
+ serial_buf[USB_MAX_STR_SIZE];
+ CFStringRef manufacturer;
+ CFStringRef product;
+ CFStringRef serial;
+
+ OSStatus status = noErr;
+
+
+ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+ fprintf(stderr, "INFO: Opening the print file and connection...\n");
+
+ parseOptions(options, serial_buf);
+
+ if (resource[0] == '/')
+ resource++;
+
+ removePercentEscapes(hostname, manufacturer_buf, sizeof(manufacturer_buf));
+ removePercentEscapes(resource, product_buf, sizeof(product_buf));
+
+ manufacturer = CFStringCreateWithCString(NULL, manufacturer_buf, kCFStringEncodingUTF8);
+ product = CFStringCreateWithCString(NULL, product_buf, kCFStringEncodingUTF8);
+ serial = CFStringCreateWithCString(NULL, serial_buf, kCFStringEncodingUTF8);
+
+ USBPrinterInfo *activePrinter = NULL;
+ USBPrinterClassContext **classdriver;
+ int countdown = INITIAL_LOG_INTERVAL;
+ do
+ {
+ /* */
+ /* given a manufacturer and product, bind to a specific printer on the bus */
+ /* */
+ usbPrinters = UsbGetAllPrinters();
+ /* */
+ /* if we have at least one element of the URI, find a printer module that matches */
+ /* */
+ if ( NULL != usbPrinters && (manufacturer || product ) )
+ {
+ int i,
+ numPrinters = CFArrayGetCount(usbPrinters);
+ for ( i = 0; i < numPrinters; ++i )
+ {
+ int match = FALSE;
+ USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbPrinters, i );
+ if ( printer )
+ {
+ match = printer->address.manufacturer && manufacturer? CFEqual(printer->address.manufacturer, manufacturer ): FALSE;
+ if ( match )
+ {
+ match = printer->address.product && product? CFEqual(printer->address.product, product ): FALSE;
+ }
+ if ( match && serial )
+ {
+ /* Note with old queues (pre Panther) the CUPS uri may have no serial number (serial==NULL). */
+ /* In this case, we will ignore serial number (as before), and we'll match to the first */
+ /* printer that agrees with manufacturer and product. */
+ /* If the CUPS uri does include a serial number, we'll enter this clause */
+ /* which requires the printer's serial number to match the CUPS serial number. */
+ /* The net effect is that for printers with a serial number, */
+ /* new queues must match the serial number, while old queues match any printer */
+ /* that satisfies the manufacturer/product match. */
+ /* */
+ match = printer->address.serial? CFEqual(printer->address.serial, serial ): FALSE;
+ }
+ if ( match )
+ {
+ targetPrinter = UsbCopyPrinter( printer );
+ break; /* for, compare partial address to address for each printer on usb bus */
+ }
+ }
+ }
+ }
+ UsbReleaseAllPrinters( usbPrinters );
+ if ( NULL != targetPrinter )
+ status = UsbRegistryOpen( &targetPrinter->address, &activePrinter );
+
+ if ( NULL == activePrinter )
+ {
+ sleep( PRINTER_POLLING_INTERVAL );
+ countdown -= PRINTER_POLLING_INTERVAL;
+ if ( !countdown )
+ {
+ /* periodically, write to the log so someone knows we're waiting */
+ if (NULL == targetPrinter)
+ fprintf( stderr, "WARNING: Printer not responding\n" );
+ else
+ fprintf( stderr, "INFO: Printer busy\n" );
+ countdown = SUBSEQUENT_LOG_INTERVAL; /* subsequent log entries, every 30 minutes */
+ }
+ }
+ } while ( NULL == activePrinter );
+
+ classdriver = activePrinter->classdriver;
+ if ( NULL == classdriver )
+ {
+ perror("ERROR: Unable to open USB Printing Class port");
+ return (status);
+ }
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (fd != 0)
+ {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ buffer = malloc( buffersize );
+ if ( !buffer ) {
+ fprintf( stderr, "ERROR: Couldn't allocate internal buffer\n" );
+ status = -1;
+ }
+ else
+ {
+ fprintf(stderr, "INFO: Sending the print file...\n");
+ if (pthread_cond_init(&readCompleteCondition, NULL) == 0)
+ {
+ gReadCompleteConditionPtr = &readCompleteCondition;
+
+ if (pthread_mutex_init(&readMutex, NULL) == 0)
+ {
+ gReadMutexPtr = &readMutex;
+
+ if (pthread_create(&thr, NULL, readthread, classdriver ) > 0)
+ fprintf(stderr, "WARNING: Couldn't create read channel\n");
+ else
+ thread_created = 1;
+ }
+ }
+ }
+ /*
+ * the main thread sends the print file...
+ */
+ while (noErr == status && copies > 0)
+ {
+ copies --;
+ if (STDIN_FILENO != fd)
+ {
+ fputs("PAGE: 1 1", stderr);
+ lseek( fd, 0, SEEK_SET ); /* rewind */
+ }
+
+ tbytes = 0;
+ while (noErr == status && (nbytes = read(fd, buffer, buffersize)) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ tbytes += nbytes;
+ bufptr = buffer;
+
+ while (nbytes > 0 && noErr == status )
+ {
+ wbytes = nbytes;
+ status = (*classdriver)->WritePipe( classdriver, (UInt8*)bufptr, &wbytes, 0 /*nbytes > wbytes? 0: feof(fp)*/ );
+ if (wbytes < 0 || noErr != status)
+ {
+ OSStatus err;
+ err = (*classdriver)->Abort( classdriver );
+ fprintf(stderr, "ERROR: %ld: Unable to send print file to printer (canceled %ld)\n", status, err );
+ break;
+ }
+
+ nbytes -= wbytes;
+ bufptr += wbytes;
+ }
+
+ if (fd != 0 && noErr == status)
+ fprintf(stderr, "INFO: Sending print file, %qd bytes...\n", (off_t)tbytes);
+ }
+ }
+ done = 1; /* stop scheduling reads */
+
+ if ( thread_created )
+ {
+ /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
+ * we are not signaled in that time then force the thread to exit by setting
+ * the waiteof to be false. Plese note that this relies on us using the timeout
+ * class driver.
+ */
+ struct timespec sleepUntil = { time(NULL) + WAITEOF_DELAY, 0 };
+ pthread_mutex_lock(&readMutex);
+ if (pthread_cond_timedwait(&readCompleteCondition, &readMutex, (const struct timespec *)&sleepUntil) != 0)
+ gWaitEOF = false;
+ pthread_mutex_unlock(&readMutex);
+ pthread_join( thr,NULL); /* wait for the child thread to return */
+ }
+
+ (*classdriver)->Close( classdriver ); /* forces the read to stop incase we are doing a blocking read */
+ UsbUnloadClassDriver( activePrinter );
+ /*
+ * Close the socket connection and input file and return...
+ */
+ free( buffer );
+
+ if (STDIN_FILENO != fd)
+ close(fd);
+
+ if (gReadCompleteConditionPtr != NULL)
+ pthread_cond_destroy(gReadCompleteConditionPtr);
+ if (gReadMutexPtr != NULL)
+ pthread_mutex_destroy(gReadMutexPtr);
+
+ return status == kIOReturnSuccess? 0: status;
+}
+
+static Boolean
+encodecfstr( CFStringRef cfsrc, char *dst, long len )
+{
+ return CFStringGetCString(cfsrc, dst, len, kCFStringEncodingUTF8 );
+}
+
+/*
+* 'list_devices()' - List all USB devices.
+*/
+void list_devices(void)
+{
+ char encodedManufacturer[1024];
+ char encodedProduct[1024];
+ char uri[1024];
+ CFMutableArrayRef usbBusPrinters = UsbGetAllPrinters();
+ CFIndex i, numPrinters = NULL != usbBusPrinters? CFArrayGetCount( usbBusPrinters ): 0;
+
+ puts("direct usb \"Unknown\" \"USB Printer (usb)\"");
+ for ( i = 0; i < numPrinters; ++i )
+ {
+ USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbBusPrinters, i );
+
+ if ( printer )
+ {
+ CFStringRef addressRef = UsbMakeFullUriAddress( printer );
+ if ( addressRef )
+ {
+ if ( CFStringGetCString(addressRef, uri, sizeof(uri), kCFStringEncodingUTF8) ) {
+
+ encodecfstr( printer->address.manufacturer, encodedManufacturer, sizeof(encodedManufacturer) );
+ encodecfstr( printer->address.product, encodedProduct, sizeof(encodedProduct) );
+ printf("direct %s \"%s %s\" \"%s\"\n", uri, encodedManufacturer, encodedProduct, encodedProduct);
+ }
+ }
+ }
+ }
+ UsbReleaseAllPrinters( usbBusPrinters );
+ fflush(NULL);
+}
+
+
+static void parseOptions(const char *options, char *serial)
+{
+ char *serialnumber; /* ?serial= or ?location= */
+ char optionName[255], /* Name of option */
+ value[255], /* Value of option */
+ *ptr; /* Pointer into name or value */
+
+ if (serial)
+ *serial = '\0';
+
+ if (!options)
+ return;
+
+ serialnumber = NULL;
+
+ while (*options != '\0')
+ {
+ /*
+ * Get the name...
+ */
+ for (ptr = optionName; *options && *options != '=' && *options != '+' && *options != '&'; )
+ *ptr++ = *options++;
+
+ *ptr = '\0';
+ value[0] = '\0';
+
+ if (*options == '=')
+ {
+ /*
+ * Get the value...
+ */
+
+ options ++;
+
+ for (ptr = value; *options && *options != '+' && *options != '&';)
+ *ptr++ = *options++;
+
+ *ptr = '\0';
+
+ if (*options == '+' || *options == '&')
+ options ++;
+ }
+ else if (*options == '+' || *options == '&')
+ {
+ options ++;
+ }
+
+ /*
+ * Process the option...
+ */
+ if (strcasecmp(optionName, "waiteof") == 0)
+ {
+ if (strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "yes") == 0 ||
+ strcasecmp(value, "true") == 0)
+ {
+ gWaitEOF = true;
+ }
+ else if (strcasecmp(value, "off") == 0 ||
+ strcasecmp(value, "no") == 0 ||
+ strcasecmp(value, "false") == 0)
+ {
+ gWaitEOF = false;
+ }
+ else
+ {
+ fprintf(stderr, "WARNING: Boolean expected for waiteof option \"%s\"\n", value);
+ }
+ }
+ else if (strcasecmp(optionName, "serial") == 0 ||
+ strcasecmp(optionName, "location") == 0 )
+ {
+ strcpy(serial, value);
+ serialnumber = serial;
+ }
+ }
+
+ return;
+}
+
+
+/*!
+ * @function addPercentEscapes
+ * @abstract Encode a string with percent escapes
+ *
+ * @param src The source C string
+ * @param dst Desination buffer
+ * @param dstMax Size of desination buffer
+ *
+ * @result A non-zero return value for errors
+ */
+static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax)
+{
+ unsigned char c;
+ char *dstEnd = dst + dstMax - 1; /* -1 to leave room for the NUL */
+
+ while (*src)
+ {
+ c = *src++;
+
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || (c == '.' || c == '-' || c == '*' || c == '_'))
+ {
+ if (dst >= dstEnd)
+ return -1;
+
+ *dst++ = c;
+ }
+ else
+ {
+ if (dst >= dstEnd - 2)
+ return -1;
+
+ snprintf(dst, dstEnd - dst, "%%%02x", c);
+ dst += 3;
+ }
+ }
+
+ *dst = '\0';
+ return 0;
+}
+
+
+/*!
+ * @function removePercentEscapes
+ * @abstract Returns a string with any percent escape sequences replaced with their equivalent character
+ *
+ * @param src Source buffer
+ * @param srclen Number of bytes in source buffer
+ * @param dst Desination buffer
+ * @param dstMax Size of desination buffer
+ *
+ * @result A non-zero return value for errors
+ */
+static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax)
+{
+ int c;
+ const unsigned char *dstEnd = dst + dstMax;
+
+ while (*src && dst < dstEnd)
+ {
+ c = *src++;
+
+ if (c == '%')
+ {
+ sscanf(src, "%02x", &c);
+ src += 2;
+ }
+ *dst++ = (char)c;
+ }
+
+ if (dst >= dstEnd)
+ return -1;
+
+ *dst = '\0';
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------*
+
+ DelimitSubstring
+
+ Desc: Search a string from a starting location, looking for a given
+ delimiter. Return the range from the start of the search to the
+ delimiter, or end of string (whichever is shorter).
+
+ In: stringToSearch string which contains a substring that we search
+ delim string which marks the end of the string
+ bounds start and length of substring of stringToSearch
+ options case sensitive, anchored, etc.
+
+ Out: Range up to the delimiter.
+
+*-----------------------------------------------------------------------------*/
+static CFRange
+DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options )
+{
+ CFRange where_delim, /* where the delimiter was found */
+ value;
+ /* */
+ /* trim leading space by changing bounds */
+ /* */
+ while ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, CFSTR(" "), bounds, kCFCompareAnchored, &where_delim ) )
+ {
+ ++bounds.location; /* drop a leading ' ' */
+ --bounds.length;
+ }
+ value = bounds; /* assume match to the end of string, may be NULL */
+ /* */
+ /* find the delimiter in the remaining string */
+ /* */
+ if ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, delim, bounds, options, &where_delim ) )
+ {
+ /* */
+ /* match to the delimiter */
+ /* */
+ value.length = where_delim.location /* delim */ - bounds.location /* start of search */;
+ }
+ DEBUG_CFString( "\tFind target", stringToSearch );
+ DEBUG_CFString( "\tFind pattern", delim );
+ DEBUG_ERR( (int) value.location, "\t\tFound %d\n" );
+ DEBUG_ERR( (int) value.length, " length %d" );
+
+ return value;
+}
+
+
+/*-----------------------------------------------------------------------------*
+
+ DeviceIDCreateValueList
+
+ Desc: Create a new string for the value list of the specified key.
+ The key may be specified as two strings (an abbreviated form
+ and a standard form). NULL can be passed for either form of
+ the key.
+
+ (Although passing NULL for both forms of the key is considered
+ bad form[!] it is handled correctly.)
+
+ In: deviceID the device's IEEE-1284 DeviceID key-value list
+ abbrevKey the key we're interested in (NULL allowed)
+ key
+
+ Out: CFString the value list
+ or NULL key wasn't found in deviceID
+
+*-----------------------------------------------------------------------------*/
+CFStringRef
+DeviceIDCreateValueList( const CFStringRef deviceID, const CFStringRef abbrevKey, const CFStringRef key )
+{
+ CFRange found = CFRangeMake( -1,0); /* note CFStringFind sets length 0 if string not found */
+ CFStringRef valueList = NULL;
+
+ DEBUG_CFString( "---------DeviceIDCreateValueList DeviceID:", deviceID );
+ DEBUG_CFString( "---------DeviceIDCreateValueList key:", key );
+ DEBUG_CFString( "---------DeviceIDCreateValueList abbrevkey:", abbrevKey );
+ if ( NULL != deviceID && NULL != abbrevKey )
+ found = CFStringFind( deviceID, abbrevKey, kCFCompareCaseInsensitive );
+ if ( NULL != deviceID && NULL != key && found.length <= 0 )
+ found = CFStringFind( deviceID, key, kCFCompareCaseInsensitive );
+ if ( found.length > 0 )
+ {
+ /* the key is at found */
+ /* the value follows the key until we reach the semi-colon, or end of string */
+ /* */
+ CFRange search = CFRangeMake( found.location + found.length,
+ CFStringGetLength( deviceID ) - (found.location + found.length) );
+ /* */
+ /* finally extract the string */
+ /* */
+ valueList = CFStringCreateWithSubstring ( kCFAllocatorDefault, deviceID,
+ DelimitSubstring( deviceID, kDeviceIDKeyValuePairDelimiter, search, kCFCompareCaseInsensitive ) );
+ DEBUG_CFString( "---------DeviceIDCreateValueList:", valueList );
+ }
+ return valueList;
+
+}
+
+
+
+/******************************************************************************/
+
+/*-----------------------------------------------------------------------------*
+
+CompareSameString
+
+Desc: Return the CFCompare result for two strings, either or both of which
+ can be NULL.
+
+In:
+ a current value
+ b last value
+
+Out:
+ 0 if the strings match
+ non-zero if the strings don't match
+
+*-----------------------------------------------------------------------------*/
+static int
+CompareSameString( const CFStringRef a, const CFStringRef b )
+{
+ if ( NULL == a && NULL == b )
+ return 0;
+ else if ( NULL != a && NULL != b )
+ return CFStringCompare( a, b, kCFCompareAnchored );
+ else
+ return 1; /* one of a or b is NULL this time, but wasn't last time */
+}
+
+
+/******************************************************************************/
+kern_return_t
+UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle )
+{
+ kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
+ if ( NULL != classDriverBundle )
+ printer->bundle = classDriverBundle; /* vendor-specific class override */
+ else
+#ifdef TIMEOUT
+ classDriverBundle = kUSBGenericTOPrinterClassDriver; /* supply the generic TIMEOUT class driver */
+#else
+ classDriverBundle = kUSBGenericPrinterClassDriver; /* supply the generic class driver */
+#endif
+ DEBUG_CFString( "UsbLoadClassDriver classDriverBundle", classDriverBundle );
+ if ( NULL != classDriverBundle )
+ {
+ USBPrinterClassContext **classdriver = NULL;
+ CFURLRef classDriverURL = CFURLCreateWithFileSystemPath( NULL, classDriverBundle, kCFURLPOSIXPathStyle, TRUE );
+ CFPlugInRef plugin = NULL == classDriverURL? NULL: CFPlugInCreate( NULL, classDriverURL );
+ if ( NULL != plugin)
+ {
+ /* See if this plug-in implements the Test type. */
+ CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn( kUSBPrinterClassTypeID, plugin );
+
+ /* If there are factories for the requested type, attempt to */
+ /* get the IUnknown interface. */
+ DEBUG_ERR( 0, "UsbLoadClassDriver plugin %x\n" );
+ if (NULL != factories && CFArrayGetCount(factories) > 0)
+ {
+ /* Get the factory ID for the first location in the array of IDs. */
+ CFUUIDRef factoryID = CFArrayGetValueAtIndex( factories, 0 );
+ /* Use the factory ID to get an IUnknown interface. */
+ /* Here the code for the PlugIn is loaded. */
+ IUnknownVTbl **iunknown = CFPlugInInstanceCreate( NULL, factoryID, kUSBPrinterClassTypeID );
+ /* If this is an IUnknown interface, query for the Test interface. */
+ DEBUG_ERR( 0, "UsbLoadClassDriver factories %x\n" );
+ if (NULL != iunknown)
+ {
+ DEBUG_ERR( 0, "UsbLoadClassDriver CFPlugInInstanceCreate %x\n" );
+ kr = (*iunknown)->QueryInterface( iunknown, CFUUIDGetUUIDBytes(interfaceID), (LPVOID *) &classdriver );
+
+ (*iunknown)->Release( iunknown );
+ if ( S_OK == kr && NULL != classdriver )
+ {
+ DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface %x\n" );
+ printer->plugin = plugin;
+ kr = (*classdriver)->Initialize( classdriver, printer->classdriver );
+
+ kr = kIOReturnSuccess;
+ printer->classdriver = classdriver;
+ }
+ else
+ {
+ DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface FAILED %x\n" );
+ }
+ }
+ else
+ {
+ DEBUG_ERR( kr, "UsbLoadClassDriver CFPlugInInstanceCreate FAILED %x\n" );
+ }
+ }
+ else
+ {
+ DEBUG_ERR( kr, "UsbLoadClassDriver factories FAILED %x\n" );
+ }
+ }
+ else
+ {
+ DEBUG_ERR( kr, "UsbLoadClassDriver plugin FAILED %x\n" );
+ }
+ if ( kr != kIOReturnSuccess || NULL == plugin || NULL == classdriver )
+ {
+ UsbUnloadClassDriver( printer );
+ }
+ }
+
+ return kr;
+}
+
+
+kern_return_t
+UsbUnloadClassDriver( USBPrinterInfo *printer )
+{
+ DEBUG_ERR( kIOReturnSuccess, "UsbUnloadClassDriver %x\n" );
+ if ( NULL != printer->classdriver )
+ (*printer->classdriver)->Release( printer->classdriver );
+ printer->classdriver = NULL;
+
+ if ( NULL != printer->plugin )
+ CFRelease( printer->plugin );
+ printer->plugin = NULL;
+
+ return kIOReturnSuccess;
+}
+
+
+/*-----------------------------------------------------------------------------*
+
+ UsbAddressDispose
+
+ Desc: deallocates anything used to create a persistent printer address
+
+ In: address the printer address we've created
+
+ Out:
+
+*-----------------------------------------------------------------------------*/
+void
+UsbAddressDispose( USBPrinterAddress *address )
+{
+ if ( address->product != NULL ) CFRelease( address->product );
+ if ( address->manufacturer != NULL ) CFRelease( address->manufacturer );
+ if ( address->serial != NULL ) CFRelease( address->serial );
+ if ( address->command != NULL ) CFRelease( address->command );
+
+ address->product =
+ address->manufacturer =
+ address->serial =
+ address->command = NULL;
+
+}
+
+/*-----------------------------------------------------------------------------*
+
+ UsbGetPrinterAddress
+
+ Desc: Given a printer we're enumerating, discover it's persistent
+ reference.
+
+ A "persistent reference" is one which enables us to identify
+ a printer regardless of where it resides on the USB topology,
+ and enumeration sequence.
+
+ To do this, we actually construct a reference from information
+ buried inside the printer. First we look at the USB device
+ descripton: an ideally defined device will support strings for
+ manufacturer and product id, and serial number. The serial number
+ will be unique for each printer.
+
+ Our prefered identification fetches the IEEE-1284 device id string.
+ This transparently handled IEEE-1284 compatible printers which
+ connected over a USB-parallel cable. Only if we can't get all the
+ information to uniquely identify the printer do we try the strings
+ referenced in the printer's USB device descriptor. (These strings
+ are typically absent in a USB-parallel cable.)
+
+ If a device doesn't support serial numbers we have a problem:
+ we can't distinguish between two identical printers. Unique serial
+ numbers allow us to distinguish between two same-model, same-manufacturer
+ USB printers.
+
+ In:
+ thePrinter iterator required for fetching device descriptor
+ devRefNum required to configure the interface
+
+ Out:
+ address->manufacturer
+ address->product
+ address->serial
+ Any (and all) of these may be NULL if we can't retrieve
+ information for IEEE1284 DeviceID or the USB device
+ descriptor. Caller should be prepared to handle such a case.
+ address->command
+ May be updated.
+
+*-----------------------------------------------------------------------------*/
+OSStatus
+UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout )
+{
+
+ /* */
+ /* start by assuming the device is not IEEE-1284 compliant */
+ /* and that we can't read in the required strings. */
+ /* */
+ OSStatus err;
+ CFStringRef deviceId = NULL;
+ USBPrinterClassContext **printer = NULL == thePrinter? NULL: thePrinter->classdriver;
+
+ address->manufacturer =
+ address->product =
+ address->compatible =
+ address->serial =
+ address->command = NULL;
+
+ DEBUG_DUMP( "UsbGetPrinterAddress thePrinter", thePrinter, sizeof(USBPrinterInfo) );
+
+ err = (*printer)->GetDeviceID( printer, &deviceId, timeout );
+ if ( noErr == err && NULL != deviceId )
+ {
+ /* the strings embedded here are defined in the IEEE1284 spec */
+ /* */
+ /* use the MFG/MANUFACTURER for the manufacturer */
+ /* and the MDL/MODEL for the product */
+ /* there is no serial number defined in IEEE1284 */
+ /* but it's been observed in recent HP printers */
+ /* */
+ address->command = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCommandAbbrev, kDeviceIDKeyCommand );
+
+ address->product = DeviceIDCreateValueList( deviceId, kDeviceIDKeyModelAbbrev, kDeviceIDKeyModel );
+ address->compatible = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCompatibleAbbrev, kDeviceIDKeyCompatible );
+
+ address->manufacturer = DeviceIDCreateValueList( deviceId, kDeviceIDKeyManufacturerAbbrev, kDeviceIDKeyManufacturer );
+
+ address->serial = DeviceIDCreateValueList( deviceId, kDeviceIDKeySerialAbbrev, kDeviceIDKeySerial );
+ CFRelease( deviceId );
+ }
+ DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->product", address->product );
+ DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->compatible", address->compatible );
+ DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->manufacturer", address->manufacturer );
+ DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->serial", address->serial );
+
+ if ( NULL == address->product || NULL == address->manufacturer || NULL == address->serial )
+ {
+ /* */
+ /* if the manufacturer or the product or serial number were not specified in DeviceID */
+ /* try to construct the address using USB English string descriptors */
+ /* */
+ IOUSBDeviceDescriptor desc;
+ USBIODeviceRequest request;
+
+ request.requestType = USBmakebmRequestType( kUSBIn, kUSBStandard, kUSBDevice );
+ request.request = kUSBRqGetDescriptor;
+ request.value = (kUSBDeviceDesc << 8) | 0;
+ request.index = 0; /* not kUSBLanguageEnglish*/
+ request.length = sizeof(desc);
+ request.buffer = &desc;
+ err = (*printer)->DeviceRequest( printer, &request, timeout );
+ DEBUG_ERR( (kern_return_t) err, "UsbGetPrinterAddress: GetDescriptor %x" );
+ if ( kIOReturnSuccess == err )
+ {
+ /* once we've retrieved the device descriptor */
+ /* try to fill in missing pieces of information */
+ /* */
+ /* Don't override any information already retrieved from DeviceID. */
+
+ if ( NULL == address->product)
+ {
+ err = (*printer)->GetString( printer, desc.iProduct, kUSBLanguageEnglish, timeout, &address->product );
+ if ( kIOReturnSuccess != err || address->product == NULL) {
+ address->product = CFSTR("Unknown");
+ }
+ }
+ DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->product\n", address->product );
+
+ if ( NULL == address->manufacturer )
+ {
+ err = (*printer)->GetString( printer, desc.iManufacturer, kUSBLanguageEnglish, timeout, &address->manufacturer );
+ if (kIOReturnSuccess != err || address->manufacturer == NULL) {
+ address->manufacturer = CFSTR("Unknown");
+ }
+ }
+ DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->manufacturer\n", address->manufacturer );
+
+ if ( NULL == address->serial )
+ {
+ /* if the printer doesn't have a serial number, use locationId */
+ if ( 0 == desc.iSerialNumber )
+ {
+ address->serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), (*printer)->location );
+ }
+ else
+ {
+ err = (*printer)->GetString( printer, desc.iSerialNumber, kUSBLanguageEnglish, timeout, &address->serial );
+ /* trailing NULs aren't handled correctly in URI */
+ if ( address->serial )
+ {
+ UniChar nulbyte = { 0 };
+ CFStringRef trim = CFStringCreateWithCharacters(NULL, &nulbyte, 1);
+ CFMutableStringRef newserial = CFStringCreateMutableCopy(NULL, 0, address->serial);
+
+ CFStringTrim( newserial, trim );
+
+ CFRelease(trim);
+ CFRelease( address->serial );
+
+ address->serial = newserial;
+ }
+ }
+ }
+ DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->serial\n", address->serial );
+ }
+ }
+ if ( NULL != address->product)
+ CFRetain(address->product); /* UsbGetString is really a UsbCopyString. */
+ if ( NULL != address->manufacturer )
+ CFRetain( address->manufacturer );
+ if ( NULL != address->serial )
+ CFRetain( address->serial );
+ return err;
+}
+
+
+/*-----------------------------------------------------------------------------*
+
+UsbSamePrinter
+
+ Desc: match two Usb printer address; return TRUE if they are the same.
+
+ In: a the persistent address found last time
+ b the persistent address found this time
+
+ Out: non-zero iff the addresses are the same
+
+*-----------------------------------------------------------------------------*/
+int
+UsbSamePrinter( const USBPrinterAddress *a, const USBPrinterAddress *b )
+{
+ int result = 0;
+ DEBUG_CFCompareString( "UsbSamePrinter serial", a->serial, b->serial );
+ DEBUG_CFCompareString( "UsbSamePrinter product", a->product, b->product );
+ DEBUG_CFCompareString( "UsbSamePrinter manufacturer", a->manufacturer, b->manufacturer );
+
+ result = !CompareSameString( a->serial, b->serial );
+ if ( result ) result = !CompareSameString( a->product, b->product );
+ if ( result ) result = !CompareSameString( a->manufacturer, b->manufacturer );
+
+ return result;
+}
+
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Method: UsbMakeFullUriAddress
+
+ Input Parameters:
+
+ Output Parameters:
+
+ Description:
+ Fill in missing address information
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+CFStringRef
+UsbMakeFullUriAddress( USBPrinterInfo *printer )
+{
+ /* */
+ /* fill in missing address information. */
+ /* */
+ CFMutableStringRef printerUri = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("usb://") );
+ if ( NULL != printerUri )
+ {
+ CFStringRef serial = printer->address.serial;
+
+ CFStringAppend(printerUri, printer->address.manufacturer? CreateEncodedCFString( printer->address.manufacturer ): CFSTR("Unknown") );
+ CFStringAppend(printerUri, CFSTR("/") );
+
+ CFStringAppend(printerUri, printer->address.product? CreateEncodedCFString( printer->address.product ): CFSTR("Unknown") );
+
+ /*Handle the common case where there is no serial number (S450?) */
+ CFStringAppend(printerUri, serial == NULL? CFSTR("?location="): CFSTR("?serial=") );
+ if ( serial == NULL)
+ serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), printer->location );
+
+ CFStringAppend(printerUri, serial? CreateEncodedCFString( serial ): CFSTR("Unknown") );
+ }
+
+ return printerUri;
+}
+
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Method: UsbGetAllPrinters
+
+ Input Parameters:
+
+ Output Parameters:
+ array of all USB printers on the system
+
+ Description:
+ Build a list of USB printers by iterating IOKit USB objects
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+CFMutableArrayRef
+UsbGetAllPrinters( void )
+{
+ kern_return_t kr; /* kernel errors */
+ mach_port_t master_device_port = 0;
+ io_service_t usbInterface = 0;
+ io_iterator_t iter = 0;
+ CFMutableArrayRef printers = CFArrayCreateMutable( NULL, 0, NULL ); /* all printers */
+
+ do
+ {
+
+ kr = IOMasterPort( bootstrap_port, &master_device_port );
+ DEBUG_ERR( kr, "UsbGetAllPrinters IOMasterPort %x\n" );
+ if(kIOReturnSuccess != kr) break;
+
+ {
+ CFDictionaryRef usbMatch = NULL;
+
+ /* iterate over all interfaces. */
+ usbMatch = IOServiceMatching(kIOUSBInterfaceClassName);
+ if ( !usbMatch ) break;
+ DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceMatching %x\n" );
+
+ /* IOServiceGetMatchingServices() consumes the usbMatch reference so we don't need to release it. */
+ kr = IOServiceGetMatchingServices(master_device_port, usbMatch, &iter);
+ usbMatch = NULL;
+
+ DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceGetMatchingServices %x\n" );
+ if(kIOReturnSuccess != kr || iter == NULL) break;
+ }
+
+ while ( NULL != (usbInterface = IOIteratorNext(iter)) )
+ {
+ IOCFPlugInInterface **iodev;
+ USBPrinterInterface intf;
+ HRESULT res;
+ SInt32 score;
+ CFMutableDictionaryRef properties;
+ CFStringRef classDriver = NULL;
+
+ kr = IORegistryEntryCreateCFProperties( usbInterface, &properties, kCFAllocatorDefault, kNilOptions);
+ if ( kIOReturnSuccess == kr && NULL != properties)
+ {
+ classDriver = (CFStringRef) CFDictionaryGetValue( properties, kUSBClassDriverProperty );
+ if ( NULL != classDriver )
+ CFRetain( classDriver );
+ CFRelease( properties );
+ }
+
+ kr = IOCreatePlugInInterfaceForService( usbInterface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &iodev,
+ &score);
+
+ DEBUG_ERR( kr, "UsbGetAllPrinters IOCreatePlugInInterfaceForService %x\n" );
+ if ( kIOReturnSuccess == kr )
+ {
+ UInt8 intfClass = 0;
+ UInt8 intfSubClass = 0;
+
+ res = (*iodev)->QueryInterface( iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
+ DEBUG_ERR( (kern_return_t) res, "UsbGetAllPrinters QueryInterface %x\n" );
+
+ (*iodev)->Release(iodev);
+ if ( noErr != res ) break;
+
+ kr = (*intf)->GetInterfaceClass(intf, &intfClass);
+ DEBUG_ERR(kr, "UsbGetAllPrinters GetInterfaceClass %x\n");
+ if ( kIOReturnSuccess == kr )
+ kr = (*intf)->GetInterfaceSubClass(intf, &intfSubClass);
+ DEBUG_ERR(kr, "UsbGetAllPrinters GetInterfaceSubClass %x\n");
+
+ if ( kIOReturnSuccess == kr &&
+ kUSBPrintingClass == intfClass &&
+ kUSBPrintingSubclass == intfSubClass )
+ {
+
+ USBPrinterInfo printer,
+ *printerInfo;
+ /*
+ For each type of printer specified in the lookup spec array, find
+ all of that type of printer and add the results to the list of found
+ printers.
+ */
+ /* create this printer's persistent address */
+ memset( &printer, 0, sizeof(USBPrinterInfo) );
+ kr = (*intf)->GetLocationID(intf, &printer.location);
+ DEBUG_ERR(kr, "UsbGetAllPrinters GetLocationID %x\n");
+ if ( kIOReturnSuccess == kr )
+ {
+ kr = UsbLoadClassDriver( &printer, kUSBPrinterClassInterfaceID, classDriver );
+ DEBUG_ERR(kr, "UsbGetAllPrinters UsbLoadClassDriver %x\n");
+ if ( kIOReturnSuccess == kr && printer.classdriver )
+ {
+ (*(printer.classdriver))->interface = intf;
+ kr = UsbGetPrinterAddress( &printer, &printer.address, 60000L );
+ {
+ /* always unload the driver */
+ /* but don't mask last error */
+ kern_return_t unload_err = UsbUnloadClassDriver( &printer );
+ if ( kIOReturnSuccess == kr )
+ kr = unload_err;
+ }
+ }
+ }
+
+ printerInfo = UsbCopyPrinter( &printer );
+ if ( NULL != printerInfo )
+ CFArrayAppendValue( printers, (const void *) printerInfo ); /* keep track of it */
+
+ } /* if there's a printer */
+ kr = (*intf)->Release(intf);
+ } /* if IOCreatePlugInInterfaceForService */
+
+ IOObjectRelease(usbInterface);
+ usbInterface = NULL;
+
+ } /* while there's an interface */
+ } while ( 0 );
+
+ if (iter)
+ {
+ IOObjectRelease(iter);
+ iter = 0;
+ }
+
+ if (master_device_port)
+ {
+ mach_port_deallocate(mach_task_self(), master_device_port);
+ master_device_port = 0;
+ }
+ return printers;
+
+} /* UsbGetAllPrinters */
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Method: UsbReleasePrinter
+
+ Input Parameters:
+
+ Output Parameters:
+
+ Description:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+void
+UsbReleasePrinter( USBPrinterInfo *printer )
+{
+ if ( printer )
+ {
+ UsbUnloadClassDriver( printer );
+ if ( NULL != printer->address.manufacturer )
+ CFRelease( printer->address.manufacturer );
+ if ( NULL != printer->address.product )
+ CFRelease( printer->address.product );
+ if ( NULL != printer->address.serial )
+ CFRelease( printer->address.serial );
+ if ( NULL != printer->address.command )
+ CFRelease( printer->address.command );
+ if ( NULL != printer->bundle )
+ CFRelease( printer->bundle );
+ free( printer );
+ }
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Method: UsbReleaseAllPrinters
+
+ Input Parameters:
+
+ Output Parameters:
+
+ Description:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+void
+UsbReleaseAllPrinters( CFMutableArrayRef printers )
+{
+ if ( NULL != printers )
+ {
+ CFIndex i,
+ numPrinters = CFArrayGetCount(printers);
+ for ( i = 0; i < numPrinters; ++i )
+ UsbReleasePrinter( (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i ) );
+ CFRelease( printers );
+ }
+}
+
+USBPrinterInfo *
+UsbCopyPrinter( USBPrinterInfo *aPrinter )
+{
+ /* */
+ /* note this does not copy interface information, just address information */
+ /* */
+ USBPrinterInfo *printerInfo = (USBPrinterInfo *) calloc( 1, sizeof(USBPrinterInfo));
+ if ( NULL != printerInfo && NULL != aPrinter )
+ {
+ printerInfo->location = aPrinter->location;
+ if ( NULL != (printerInfo->address.manufacturer = aPrinter->address.manufacturer) )
+ CFRetain( printerInfo->address.manufacturer );
+ if ( NULL != (printerInfo->address.product = aPrinter->address.product) )
+ CFRetain( printerInfo->address.product );
+ if ( NULL != (printerInfo->address.serial = aPrinter->address.serial) )
+ CFRetain( printerInfo->address.serial );
+ if ( NULL != (printerInfo->address.command = aPrinter->address.command) )
+ CFRetain( printerInfo->address.command );
+ if ( NULL != (printerInfo->bundle = aPrinter->bundle) )
+ CFRetain( printerInfo->bundle );
+ }
+
+ return printerInfo;
+}
+
+/*-----------------------------------------------------------------------------*
+
+ UsbRegistryOpen
+
+ Desc: opens the USB printer which matches the supplied printerAddress
+
+ In: myContext->printerAddress persistent name which identifies the printer
+
+ Out: myContext->usbDeviceRef current IOKit address of this printer
+*-----------------------------------------------------------------------------*/
+kern_return_t
+UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result )
+{
+ kern_return_t kr = -1; /* indeterminate failure */
+ CFMutableArrayRef printers = UsbGetAllPrinters();
+ CFIndex numPrinters = NULL != printers? CFArrayGetCount( printers): 0;
+ CFIndex i;
+
+ *result = NULL; /* nothing matched */
+ for ( i = 0; i < numPrinters; ++i )
+ {
+ USBPrinterInfo *thisPrinter = (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i );
+ if ( NULL != thisPrinter && UsbSamePrinter( usbAddress, &thisPrinter->address ) )
+ {
+ *result = UsbCopyPrinter( thisPrinter ); /* retains reference */
+ if ( NULL != *result )
+ {
+ /* */
+ /* if we can't find a bi-di interface, settle for a known uni-directional interface */
+ /* */
+ USBPrinterClassContext **printer = NULL;
+ /* */
+ /* setup the default class driver */
+ /* If one is specified, allow the vendor driver to override our default implementation */
+ /* */
+ kr = UsbLoadClassDriver( *result, kUSBPrinterClassInterfaceID, NULL );
+ if ( kIOReturnSuccess == kr && (*result)->bundle )
+ kr = UsbLoadClassDriver( *result, kUSBPrinterClassInterfaceID, (*result)->bundle );
+ if ( kIOReturnSuccess == kr && NULL != (*result)->classdriver )
+ {
+ printer = (*result)->classdriver;
+ kr = (*printer)->Open( printer, (*result)->location, kUSBPrintingProtocolBidirectional );
+ if ( kIOReturnSuccess != kr || NULL == (*printer)->interface )
+ kr = (*printer)->Open( printer, (*result)->location, kUSBPrintingProtocolUnidirectional );
+ /* it's possible kIOReturnSuccess == kr && NULL == (*printer)->interface */
+ /* in the event that we can't open either Bidirectional or Unidirectional interface */
+ if ( kIOReturnSuccess == kr )
+ {
+ if ( NULL == (*printer)->interface )
+ {
+ (*printer)->Close( printer );
+ UsbReleasePrinter( *result );
+ *result = NULL;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ UsbReleaseAllPrinters( printers ); /* but, copied printer is retained */
+ DEBUG_ERR( kr, "UsbRegistryOpen return %x\n" );
+
+ return kr;
+}
+
+/*!
+ * @function CreateEncodedCFString
+ *
+ * @abstract Create an encoded version of the string parameter
+ * so that it can be included in a URI.
+ *
+ * @param string A CFStringRef of the string to be encoded.
+ * @result An encoded CFString.
+ *
+ * @discussion This function will change all characters in string into URL acceptable format
+ * by encoding the text using the US-ASCII coded character set. The following
+ * are invalid characters: the octets 00-1F, 7F, and 80-FF hex. Also called out
+ * are the chars "<", ">", """, "#", "{", "}", "|", "\", "^", "~", "[", "]", "`".
+ * The reserved characters for URL syntax are also to be encoded: (so don't pass
+ * in a full URL here!) ";", "/", "?", ":", "@", "=", "%", and "&".
+ */
+static CFStringRef CreateEncodedCFString(CFStringRef string)
+{
+ CFStringRef result = NULL;
+ char *bufferUTF8 = NULL;
+ char *bufferEncoded = NULL;
+
+ if (string != NULL)
+ {
+ CFIndex bufferSizeUTF8 = (3 * CFStringGetLength(string));
+ if ((bufferUTF8 = (char*)malloc(bufferSizeUTF8)) != NULL)
+ {
+ CFStringGetCString(string, bufferUTF8, bufferSizeUTF8, kCFStringEncodingUTF8);
+ {
+ UInt16 bufferSizeEncoded = (3 * strlen(bufferUTF8)) + 1;
+ if ((bufferEncoded = (char*)malloc(bufferSizeEncoded)) != NULL)
+ {
+ addPercentEscapes(bufferUTF8, bufferEncoded, bufferSizeEncoded);
+ result = CFStringCreateWithCString(kCFAllocatorDefault, bufferEncoded, kCFStringEncodingUTF8);
+ }
+ }
+ }
+ }
+
+ if (bufferUTF8) free(bufferUTF8);
+ if (bufferEncoded) free(bufferEncoded);
+
+ return result;
+}
+
+/*
+ * End of "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $".
+ */
diff --git a/backend/usb-unix.c b/backend/usb-unix.c
new file mode 100644
index 000000000..4470e9c65
--- /dev/null
+++ b/backend/usb-unix.c
@@ -0,0 +1,620 @@
+/*
+ * "$Id: usb-unix.c 4881 2005-12-15 22:03:40Z mike $"
+ *
+ * USB port backend for the Common UNIX Printing System (CUPS).
+ *
+ * This file is included from "usb.c" when compiled on UNIX/Linux.
+ *
+ * Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Send a file to the specified USB port.
+ * list_devices() - List all USB devices.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "ieee1284.c"
+#include
+
+
+/*
+ * Local functions...
+ */
+
+int open_device(const char *uri);
+
+
+/*
+ * 'print_device()' - Print a file to a USB device.
+ */
+
+int /* O - Exit status */
+print_device(const char *uri, /* I - Device URI */
+ const char *hostname, /* I - Hostname/manufacturer */
+ const char *resource, /* I - Resource/modelname */
+ const char *options, /* I - Device options/serial number */
+ int fp, /* I - File descriptor to print */
+ int copies) /* I - Copies to print */
+{
+ int fd; /* USB device */
+ int rbytes; /* Number of bytes read */
+ int wbytes; /* Number of bytes written */
+ size_t nbytes, /* Number of bytes read */
+ tbytes; /* Total number of bytes written */
+ char buffer[8192], /* Output buffer */
+ *bufptr, /* Pointer into buffer */
+ backbuf[1024]; /* Backchannel buffer */
+ struct termios opts; /* Parallel port options */
+ fd_set input, /* Input set for select() */
+ output; /* Output set for select() */
+ int paperout; /* Paper out? */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+#ifdef __linux
+ unsigned int status; /* Port status (off-line, out-of-paper, etc.) */
+#endif /* __linux */
+
+
+ /*
+ * Open the USB port device...
+ */
+
+ do
+ {
+ if ((fd = open_device(uri)) == -1)
+ {
+ if (getenv("CLASS") != NULL)
+ {
+ /*
+ * If the CLASS environment variable is set, the job was submitted
+ * to a class and not to a specific queue. In this case, we want
+ * to abort immediately so that the job can be requeued on the next
+ * available printer in the class.
+ */
+
+ fputs("INFO: Unable to open USB device, queuing on next printer in class...\n",
+ stderr);
+
+ /*
+ * Sleep 5 seconds to keep the job from requeuing too rapidly...
+ */
+
+ sleep(5);
+
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ if (errno == EBUSY)
+ {
+ fputs("INFO: USB port busy; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else if (errno == ENXIO || errno == EIO || errno == ENOENT || errno == ENODEV)
+ {
+ fputs("INFO: Printer not connected; will retry in 30 seconds...\n", stderr);
+ sleep(30);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to open USB device \"%s\": %s\n",
+ uri, strerror(errno));
+ return (CUPS_BACKEND_FAILED);
+ }
+ }
+ }
+ while (fd < 0);
+
+ /*
+ * Set any options provided...
+ */
+
+ tcgetattr(fd, &opts);
+
+ opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
+
+ /**** No options supported yet ****/
+
+ tcsetattr(fd, TCSANOW, &opts);
+
+ /*
+ * Check printer status...
+ */
+
+ paperout = 0;
+
+#if defined(__linux) && defined(LP_POUTPA)
+ /*
+ * Show the printer status before we send the file...
+ */
+
+ while (!ioctl(fd, LPGETSTATUS, &status))
+ {
+ fprintf(stderr, "DEBUG: LPGETSTATUS returned a port status of %02X...\n", status);
+
+ if (status & LP_POUTPA)
+ {
+ fputs("WARNING: Media tray empty!\n", stderr);
+ fputs("STATUS: +media-tray-empty-error\n", stderr);
+
+ paperout = 1;
+ }
+
+ if (!(status & LP_PERRORP))
+ fputs("WARNING: Printer fault!\n", stderr);
+ else if (!(status & LP_PSELECD))
+ fputs("WARNING: Printer off-line.\n", stderr);
+ else
+ break;
+
+ sleep(5);
+ }
+#endif /* __linux && LP_POUTPA */
+
+ /*
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (!fp)
+ {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ wbytes = 0;
+
+ while (copies > 0)
+ {
+ copies --;
+
+ if (fp != 0)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ lseek(fp, 0, SEEK_SET);
+ }
+
+ tbytes = 0;
+ while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ tbytes += nbytes;
+ bufptr = buffer;
+
+ while (nbytes > 0)
+ {
+ /*
+ * See if we are ready to read or write...
+ */
+
+ do
+ {
+ FD_ZERO(&input);
+ FD_SET(fd, &input);
+ FD_ZERO(&output);
+ FD_SET(fd, &output);
+ }
+ while (select(fd + 1, &input, &output, NULL, NULL) < 0);
+
+ if (FD_ISSET(fd, &input))
+ {
+ /*
+ * Read backchannel data...
+ */
+
+ if ((rbytes = read(fd, backbuf, sizeof(backbuf))) > 0)
+ {
+ fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
+ rbytes);
+ cupsBackchannelWrite(backbuf, rbytes, 1.0);
+ }
+ }
+
+ if (FD_ISSET(fd, &output))
+ {
+ /*
+ * Write print data...
+ */
+
+ if ((wbytes = write(fd, bufptr, nbytes)) < 0)
+ if (errno == ENOTTY)
+ wbytes = write(fd, bufptr, nbytes);
+
+ if (wbytes < 0)
+ {
+ /*
+ * Check for retryable errors...
+ */
+
+ if (errno == ENOSPC)
+ {
+ paperout = 1;
+ fputs("ERROR: Out of paper!\n", stderr);
+ fputs("STATUS: +media-tray-empty-error\n", stderr);
+ }
+ else if (errno != EAGAIN && errno != EINTR)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Update count and pointer...
+ */
+
+ if (paperout)
+ {
+ fputs("STATUS: -media-tray-empty-error\n", stderr);
+ paperout = 0;
+ }
+
+ nbytes -= wbytes;
+ bufptr += wbytes;
+ }
+ }
+ }
+
+ if (wbytes < 0)
+ break;
+
+ if (fp)
+ fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
+ (unsigned long)tbytes);
+ }
+ }
+
+ /*
+ * Close the USB port and return...
+ */
+
+ close(fd);
+
+ return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
+}
+
+
+/*
+ * 'list_devices()' - List all USB devices.
+ */
+
+void
+list_devices(void)
+{
+#ifdef __linux
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char format[255], /* Format for device filename */
+ device[255], /* Device filename */
+ device_id[1024], /* Device ID string */
+ device_uri[1024], /* Device URI string */
+ make_model[1024]; /* Make and model */
+
+
+ /*
+ * First figure out which USB printer filename to use...
+ */
+
+ if (!access("/dev/usblp0", 0))
+ strcpy(format, "/dev/usblp%d");
+ else if (!access("/dev/usb/usblp0", 0))
+ strcpy(format, "/dev/usb/usblp%d");
+ else
+ strcpy(format, "/dev/usb/lp%d");
+
+ /*
+ * Then open each USB device...
+ */
+
+ for (i = 0; i < 16; i ++)
+ {
+ sprintf(device, format, i);
+
+ if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
+ {
+ if (!get_device_id(fd, device_id, sizeof(device_id),
+ make_model, sizeof(make_model),
+ "usb", device_uri, sizeof(device_uri)))
+ printf("direct %s \"%s\" \"%s USB #%d\" \"%s\"\n", device_uri,
+ make_model, make_model, i + 1, device_id);
+
+ close(fd);
+ }
+ }
+#elif defined(__sgi)
+#elif defined(__sun) && defined(ECPPIOC_GETDEVID)
+ int i; /* Looping var */
+ int fd; /* File descriptor */
+ char device[255], /* Device filename */
+ device_id[1024], /* Device ID string */
+ device_uri[1024], /* Device URI string */
+ make_model[1024]; /* Make and model */
+ struct ecpp_device_id did; /* Device ID buffer */
+
+
+ /*
+ * Open each USB device...
+ */
+
+ for (i = 0; i < 8; i ++)
+ {
+ sprintf(device, "/dev/usb/printer%d", i);
+
+ if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
+ {
+ if (!get_device_id(fd, device_id, sizeof(device_id),
+ make_model, sizeof(make_model),
+ "usb", device_uri, sizeof(device_uri)))
+ printf("direct %s \"%s\" \"%s USB #%d\" \"%s\"\n", device_uri,
+ make_model, make_model, i + 1, device_id);
+
+ close(fd);
+ }
+ }
+#elif defined(__hpux)
+#elif defined(__osf)
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+ int i; /* Looping var */
+ char device[255]; /* Device filename */
+
+
+ for (i = 0; i < 8; i ++)
+ {
+ sprintf(device, "/dev/ulpt%d", i);
+ if (!access(device, 0))
+ printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);
+
+ sprintf(device, "/dev/unlpt%d", i);
+ if (!access(device, 0))
+ printf("direct usb:%s \"Unknown\" \"USB Printer #%d (no reset)\"\n", device, i + 1);
+ }
+#endif
+}
+
+
+/*
+ * 'open_device()' - Open a USB device...
+ */
+
+int /* O - File descriptor or -1 on error */
+open_device(const char *uri) /* I - Device URI */
+{
+ /*
+ * The generic implementation just treats the URI as a device filename...
+ * Specific operating systems may also support using the device serial
+ * number and/or make/model.
+ */
+
+ if (!strncmp(uri, "usb:/dev/", 9))
+#ifdef __linux
+ return (-1); /* Do not allow direct devices anymore */
+ else if (!strncmp(uri, "usb://", 6))
+ {
+ /*
+ * For Linux, try looking up the device serial number or model...
+ */
+
+ int i; /* Looping var */
+ int busy; /* Are any ports busy? */
+ int fd; /* File descriptor */
+ char format[255], /* Format for device filename */
+ device[255], /* Device filename */
+ device_id[1024], /* Device ID string */
+ make_model[1024], /* Make and model */
+ device_uri[1024]; /* Device URI string */
+
+
+ /*
+ * First figure out which USB printer filename to use...
+ */
+
+ if (!access("/dev/usblp0", 0))
+ strcpy(format, "/dev/usblp%d");
+ else if (!access("/dev/usb/usblp0", 0))
+ strcpy(format, "/dev/usb/usblp%d");
+ else
+ strcpy(format, "/dev/usb/lp%d");
+
+ /*
+ * Then find the correct USB device...
+ */
+
+ do
+ {
+ for (busy = 0, i = 0; i < 16; i ++)
+ {
+ sprintf(device, format, i);
+
+ if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
+ {
+ get_device_id(fd, device_id, sizeof(device_id),
+ make_model, sizeof(make_model),
+ "usb", device_uri, sizeof(device_uri));
+ }
+ else
+ {
+ /*
+ * If the open failed because it was busy, flag it so we retry
+ * as needed...
+ */
+
+ if (errno == EBUSY)
+ busy = 1;
+
+ device_uri[0] = '\0';
+ }
+
+ if (!strcmp(uri, device_uri))
+ {
+ /*
+ * Yes, return this file descriptor...
+ */
+
+ fprintf(stderr, "DEBUG: Printer using device file \"%s\"...\n", device);
+
+ return (fd);
+ }
+
+ /*
+ * This wasn't the one...
+ */
+
+ if (fd >= 0)
+ close(fd);
+ }
+
+ /*
+ * If we get here and at least one of the printer ports showed up
+ * as "busy", then sleep for a bit and retry...
+ */
+
+ if (busy)
+ {
+ fputs("INFO: USB printer is busy; will retry in 5 seconds...\n",
+ stderr);
+ sleep(5);
+ }
+ }
+ while (busy);
+
+ /*
+ * Couldn't find the printer, return "no such device or address"...
+ */
+
+ errno = ENODEV;
+
+ return (-1);
+ }
+#elif defined(__sun) && defined(ECPPIOC_GETDEVID)
+ return (-1); /* Do not allow direct devices anymore */
+ else if (!strncmp(uri, "usb://", 6))
+ {
+ /*
+ * For Solaris, try looking up the device serial number or model...
+ */
+
+ int i; /* Looping var */
+ int busy; /* Are any ports busy? */
+ int fd; /* File descriptor */
+ char device[255], /* Device filename */
+ device_id[1024], /* Device ID string */
+ make_model[1024], /* Make and model */
+ device_uri[1024]; /* Device URI string */
+ struct ecpp_device_id did; /* Device ID buffer */
+
+
+ /*
+ * Find the correct USB device...
+ */
+
+ do
+ {
+ for (i = 0, busy = 0; i < 8; i ++)
+ {
+ sprintf(device, "/dev/usb/printer%d", i);
+
+ if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
+ get_device_id(fd, device_id, sizeof(device_id),
+ make_model, sizeof(make_model),
+ "usb", device_uri, sizeof(device_uri));
+ else
+ {
+ /*
+ * If the open failed because it was busy, flag it so we retry
+ * as needed...
+ */
+
+ if (errno == EBUSY)
+ busy = 1;
+
+ device_uri[0] = '\0';
+ }
+
+ if (!strcmp(uri, device_uri))
+ return (fd); /* Yes, return this file descriptor... */
+
+ /*
+ * This wasn't the one...
+ */
+
+ if (fd >= 0)
+ close(fd);
+ }
+
+ /*
+ * If we get here and at least one of the printer ports showed up
+ * as "busy", then sleep for a bit and retry...
+ */
+
+ if (busy)
+ {
+ fputs("INFO: USB printer is busy; will retry in 5 seconds...\n",
+ stderr);
+ sleep(5);
+ }
+ }
+ while (busy);
+
+ /*
+ * Couldn't find the printer, return "no such device or address"...
+ */
+
+ errno = ENODEV;
+
+ return (-1);
+ }
+#else
+ return (open(uri + 4, O_RDWR | O_EXCL));
+#endif /* __linux */
+ else
+ {
+ errno = ENODEV;
+ return (-1);
+ }
+}
+
+
+/*
+ * End of "$Id: usb-unix.c 4881 2005-12-15 22:03:40Z mike $".
+ */
diff --git a/backend/usb.c b/backend/usb.c
new file mode 100644
index 000000000..3ffe2d0d1
--- /dev/null
+++ b/backend/usb.c
@@ -0,0 +1,273 @@
+/*
+ * "$Id: usb.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * USB port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * list_devices() - List all available USB devices to stdout.
+ * print_device() - Print a file to a USB device.
+ * main() - Send a file to the specified USB port.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#ifdef __APPLE__
+ /* A header order dependency requires this be first */
+# include
+#endif /* __APPLE__ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef WIN32
+# include
+#else
+# include
+# include
+# include
+#endif /* WIN32 */
+
+
+/*
+ * Local functions...
+ */
+
+void list_devices(void);
+int print_device(const char *uri, const char *hostname,
+ const char *resource, const char *options,
+ int fp, int copies);
+
+
+/*
+ * Include the vendor-specific USB implementation...
+ */
+
+#ifdef __APPLE__
+# include "usb-darwin.c"
+#elif defined(__linux) || defined(__sun) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# include "usb-unix.c"
+#else
+/*
+ * Use dummy functions that do nothing on unsupported platforms...
+ * These can be used as templates for implementing USB printing on new
+ * platforms...
+ */
+
+/*
+ * 'list_devices()' - List all available USB devices to stdout.
+ */
+
+void
+list_devices(void)
+{
+ /*
+ * Don't have any devices to list... Use output of the form:
+ *
+ * direct usb:/make/model?serial=foo "Make Model" "USB Printer"
+ *
+ * Note that "Hewlett Packard" or any other variation MUST be mapped to
+ * "HP" for compatibility with the PPD and ICC specs.
+ */
+}
+
+
+/*
+ * 'print_device()' - Print a file to a USB device.
+ */
+
+int /* O - Exit status */
+print_device(const char *uri, /* I - Device URI */
+ const char *hostname, /* I - Hostname/manufacturer */
+ const char *resource, /* I - Resource/modelname */
+ const char *options, /* I - Device options/serial number */
+ int fp, /* I - File descriptor to print */
+ int copies) /* I - Copies to print */
+{
+ /*
+ * Can't print, so just reference the arguments to eliminate compiler
+ * warnings and return and exit status of 1. Normally you would use the
+ * arguments to send a file to the printer and return 0 if everything
+ * worked OK and non-zero if there was an error.
+ */
+
+ (void)uri;
+ (void)hostname;
+ (void)resource;
+ (void)options;
+ (void)fp;
+ (void)copies;
+
+ return (CUPS_BACKEND_FAILED);
+}
+#endif /* __APPLE__ */
+
+
+/*
+ * 'main()' - Send a file to the specified USB port.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int fp; /* Print file */
+ int copies; /* Number of copies to print */
+ int status; /* Exit status */
+ int port; /* Port number (not used) */
+ const char *uri; /* Device URI */
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024], /* Resource info (device and options) */
+ *options; /* Pointer to options */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ /*
+ * Make sure status messages are not buffered...
+ */
+
+ setbuf(stderr, NULL);
+
+ /*
+ * Ignore SIGPIPE signals...
+ */
+
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Check command-line...
+ */
+
+ if (argc == 1)
+ {
+ list_devices();
+ return (CUPS_BACKEND_OK);
+ }
+ else if (argc < 6 || argc > 7)
+ {
+ fputs("Usage: usb job-id user title copies options [file]\n", stderr);
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ /*
+ * Extract the device name and options from the URI...
+ */
+
+ if (strncmp(argv[0], "usb:", 4))
+ uri = getenv("DEVICE_URI");
+ else
+ uri = argv[0];
+
+ if (!uri)
+ {
+ fputs("ERROR: No device URI found in argv[0] or in DEVICE_URI environment variable!\n", stderr);
+ return (1);
+ }
+
+ httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
+ hostname, sizeof(hostname), &port,
+ resource, sizeof(resource));
+
+ /*
+ * See if there are any options...
+ */
+
+ if ((options = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the options...
+ */
+
+ *options++ = '\0';
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ {
+ fp = 0;
+ copies = 1;
+ }
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = open(argv[6], O_RDONLY)) < 0)
+ {
+ fprintf(stderr, "ERROR: unable to open print file %s - %s\n",
+ argv[6], strerror(errno));
+ return (CUPS_BACKEND_FAILED);
+ }
+
+ copies = atoi(argv[4]);
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ status = print_device(uri, hostname, resource, options, fp, copies);
+
+ /*
+ * Close the input file and return...
+ */
+
+ if (fp != 0)
+ close(fp);
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id: usb.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/berkeley/Dependencies b/berkeley/Dependencies
new file mode 100644
index 000000000..ceb85278c
--- /dev/null
+++ b/berkeley/Dependencies
@@ -0,0 +1,14 @@
+# DO NOT DELETE
+
+lpc.o: ../cups/cups.h ../cups/ipp.h ../cups/http.h ../cups/md5.h
+lpc.o: ../cups/ppd.h ../cups/file.h ../cups/i18n.h ../cups/language.h
+lpc.o: ../cups/array.h ../cups/debug.h ../cups/string.h ../config.h
+lpq.o: ../cups/string.h ../config.h ../cups/cups.h ../cups/ipp.h
+lpq.o: ../cups/http.h ../cups/md5.h ../cups/ppd.h ../cups/file.h
+lpq.o: ../cups/i18n.h ../cups/language.h ../cups/array.h ../cups/debug.h
+lpr.o: ../cups/string.h ../config.h ../cups/cups.h ../cups/ipp.h
+lpr.o: ../cups/http.h ../cups/md5.h ../cups/ppd.h ../cups/file.h
+lpr.o: ../cups/i18n.h ../cups/language.h ../cups/array.h
+lprm.o: ../cups/cups.h ../cups/ipp.h ../cups/http.h ../cups/md5.h
+lprm.o: ../cups/ppd.h ../cups/file.h ../cups/i18n.h ../cups/language.h
+lprm.o: ../cups/array.h ../cups/string.h ../config.h
diff --git a/berkeley/Makefile b/berkeley/Makefile
new file mode 100644
index 000000000..454f6d657
--- /dev/null
+++ b/berkeley/Makefile
@@ -0,0 +1,112 @@
+#
+# "$Id: Makefile 4494 2005-02-18 02:18:11Z mike $"
+#
+# Berkeley commands makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2005 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636 USA
+#
+# Voice: (301) 373-9600
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+TARGETS = lpc lpq lpr lprm
+OBJS = lpc.o lpq.o lpr.o lprm.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS)
+
+
+#
+# Update dependencies (without system header dependencies...)
+#
+
+depend:
+ makedepend -Y -I.. -fDependencies $(OBJS:.o=.c) >/dev/null 2>&1
+
+
+#
+# Install all targets...
+#
+
+install: all
+ $(INSTALL_DIR) $(BINDIR)
+ $(INSTALL_BIN) lpq $(BINDIR)
+ $(INSTALL_BIN) lpr $(BINDIR)
+ $(INSTALL_BIN) lprm $(BINDIR)
+ $(INSTALL_DIR) $(SBINDIR)
+ $(INSTALL_BIN) lpc $(SBINDIR)
+
+
+#
+# lpc
+#
+
+lpc: lpc.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpc lpc.o $(LIBS)
+
+
+#
+# lpq
+#
+
+lpq: lpq.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpq lpq.o $(LIBS)
+
+
+#
+# lpr
+#
+
+lpr: lpr.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpr lpr.o $(LIBS)
+
+
+#
+# lprm
+#
+
+lprm: lprm.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lprm lprm.o $(LIBS)
+
+
+#
+# Dependencies...
+#
+
+include Dependencies
+
+
+#
+# End of "$Id: Makefile 4494 2005-02-18 02:18:11Z mike $".
+#
diff --git a/berkeley/lpc.c b/berkeley/lpc.c
new file mode 100644
index 000000000..4b80a618d
--- /dev/null
+++ b/berkeley/lpc.c
@@ -0,0 +1,524 @@
+/*
+ * "$Id: lpc.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * "lpc" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and commands.
+ * compare_strings() - Compare two command-line strings.
+ * do_command() - Do an lpc command...
+ * show_help() - Show help messages.
+ * show_status() - Show printers.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_strings(const char *, const char *, int);
+static void do_command(http_t *, const char *, const char *);
+static void show_help(const char *);
+static void show_status(http_t *, const char *);
+
+
+/*
+ * 'main()' - Parse options and commands.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ http_t *http; /* Connection to server */
+ char line[1024], /* Input line from user */
+ *params; /* Pointer to parameters */
+
+
+ /*
+ * Connect to the scheduler...
+ */
+
+ http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
+
+ if (argc > 1)
+ {
+ /*
+ * Process a single command on the command-line...
+ */
+
+ do_command(http, argv[1], argv[2]);
+ }
+ else
+ {
+ /*
+ * Do the command prompt thing...
+ */
+
+ printf("lpc> ");
+ while (fgets(line, sizeof(line), stdin) != NULL)
+ {
+ /*
+ * Strip trailing whitespace...
+ */
+
+ for (params = line + strlen(line) - 1; params >= line;)
+ if (!isspace(*params & 255))
+ break;
+ else
+ *params-- = '\0';
+
+ /*
+ * Strip leading whitespace...
+ */
+
+ for (params = line; isspace(*params & 255); params ++);
+
+ if (params > line)
+ _cups_strcpy(line, params);
+
+ if (!line[0])
+ {
+ /*
+ * Nothing left, just show a prompt...
+ */
+
+ printf("lpc> ");
+ continue;
+ }
+
+ /*
+ * Find any options in the string...
+ */
+
+ for (params = line; *params != '\0'; params ++)
+ if (isspace(*params & 255))
+ break;
+
+ /*
+ * Remove whitespace between the command and parameters...
+ */
+
+ while (isspace(*params & 255))
+ *params++ = '\0';
+
+ /*
+ * The "quit" and "exit" commands exit; otherwise, process as needed...
+ */
+
+ if (!compare_strings(line, "quit", 1) ||
+ !compare_strings(line, "exit", 2))
+ break;
+
+ if (*params == '\0')
+ do_command(http, line, NULL);
+ else
+ do_command(http, line, params);
+
+ /*
+ * Put another prompt out to the user...
+ */
+
+ printf("lpc> ");
+ }
+ }
+
+ /*
+ * Close the connection to the server and return...
+ */
+
+ httpClose(http);
+
+ return (0);
+}
+
+
+/*
+ * 'compare_strings()' - Compare two command-line strings.
+ */
+
+static int /* O - -1 or 1 = no match, 0 = match */
+compare_strings(const char *s, /* I - Command-line string */
+ const char *t, /* I - Option string */
+ int tmin) /* I - Minimum number of unique chars in option */
+{
+ int slen; /* Length of command-line string */
+
+
+ slen = strlen(s);
+ if (slen < tmin)
+ return (-1);
+ else
+ return (strncmp(s, t, slen));
+}
+
+
+/*
+ * 'do_command()' - Do an lpc command...
+ */
+
+static void
+do_command(http_t *http, /* I - HTTP connection to server */
+ const char *command, /* I - Command string */
+ const char *params) /* I - Parameters for command */
+{
+ if (!compare_strings(command, "status", 4))
+ show_status(http, params);
+ else if (!compare_strings(command, "help", 1) || !strcmp(command, "?"))
+ show_help(params);
+ else
+ _cupsLangPrintf(stdout, cupsLangDefault(),
+ _("%s is not implemented by the CUPS version of lpc.\n"),
+ command);
+}
+
+
+/*
+ * 'show_help()' - Show help messages.
+ */
+
+static void
+show_help(const char *command) /* I - Command to describe or NULL */
+{
+ if (!command)
+ {
+ _cupsLangPrintf(stdout, cupsLangDefault(),
+ _("Commands may be abbreviated. Commands are:\n"
+ "\n"
+ "exit help quit status ?\n"));
+ }
+ else if (!compare_strings(command, "help", 1) || !strcmp(command, "?"))
+ _cupsLangPrintf(stdout, cupsLangDefault(),
+ _("help\t\tget help on commands\n"));
+ else if (!compare_strings(command, "status", 4))
+ _cupsLangPrintf(stdout, cupsLangDefault(),
+ _("status\t\tshow status of daemon and queue\n"));
+ else
+ _cupsLangPrintf(stdout, cupsLangDefault(),
+ _("?Invalid help command unknown\n"));
+}
+
+
+/*
+ * 'show_status()' - Show printers.
+ */
+
+static void
+show_status(http_t *http, /* I - HTTP connection to server */
+ const char *dests) /* I - Destinations */
+{
+ ipp_t *request, /* IPP Request */
+ *response, /* IPP Response */
+ *jobs; /* IPP Get Jobs response */
+ ipp_attribute_t *attr, /* Current attribute */
+ *jattr; /* Current job attribute */
+ cups_lang_t *language; /* Default language */
+ char *printer, /* Printer name */
+ *device, /* Device URI */
+ *delimiter; /* Char search result */
+ ipp_pstate_t pstate; /* Printer state */
+ int accepting; /* Is printer accepting jobs? */
+ int jobcount; /* Count of current jobs */
+ const char *dptr, /* Pointer into destination list */
+ *ptr; /* Pointer into printer name */
+ int match; /* Non-zero if this job matches */
+ char printer_uri[HTTP_MAX_URI];
+ /* Printer URI */
+ static const char *requested[] = /* Requested attributes */
+ {
+ "printer-name",
+ "device-uri",
+ "printer-state",
+ "printer-is-accepting-jobs"
+ };
+
+
+ DEBUG_printf(("show_status(http=%p, dests=\"%s\")\n", http, dests));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", sizeof(requested) / sizeof(requested[0]),
+ NULL, requested);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ DEBUG_puts("show_status: request succeeded...");
+
+ /*
+ * Loop through the printers returned in the list and display
+ * their status...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ printer = NULL;
+ device = "file:/dev/null";
+ pstate = IPP_PRINTER_IDLE;
+ jobcount = 0;
+ accepting = 1;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (!strcmp(attr->name, "printer-name") &&
+ attr->value_tag == IPP_TAG_NAME)
+ printer = attr->values[0].string.text;
+
+ if (!strcmp(attr->name, "device-uri") &&
+ attr->value_tag == IPP_TAG_URI)
+ device = attr->values[0].string.text;
+
+ if (!strcmp(attr->name, "printer-state") &&
+ attr->value_tag == IPP_TAG_ENUM)
+ pstate = (ipp_pstate_t)attr->values[0].integer;
+
+ if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
+ attr->value_tag == IPP_TAG_BOOLEAN)
+ accepting = attr->values[0].boolean;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (printer == NULL)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * A single 'all' printer name is special, meaning all printers.
+ */
+
+ if (dests != NULL && !strcmp(dests, "all"))
+ dests = NULL;
+
+ /*
+ * See if this is a printer we're interested in...
+ */
+
+ match = dests == NULL;
+
+ if (dests != NULL)
+ {
+ for (dptr = dests; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr & 255) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = printer;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr & 255)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr & 255) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr & 255) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ /*
+ * Display the printer entry if needed...
+ */
+
+ if (match)
+ {
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * limit
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL,
+ cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language->language);
+
+ httpAssembleURIf(printer_uri, sizeof(printer_uri), "ipp", NULL,
+ "localhost", 631, "/printers/%s", printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", NULL, "job-id");
+
+ if ((jobs = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ /*
+ * Grab the number of jobs for the printer.
+ */
+
+ for (jattr = jobs->attrs; jattr != NULL; jattr = jattr->next)
+ if (jattr->name && !strcmp(jattr->name, "job-id"))
+ jobcount ++;
+
+ ippDelete(jobs);
+ }
+
+ /*
+ * Display it...
+ */
+
+ printf("%s:\n", printer);
+ if (!strncmp(device, "file:", 5))
+ _cupsLangPrintf(stdout, language,
+ _("\tprinter is on device \'%s\' speed -1\n"),
+ device + 5);
+ else
+ {
+ /*
+ * Just show the scheme...
+ */
+
+ if ((delimiter = strchr(device, ':')) != NULL )
+ {
+ *delimiter = '\0';
+ _cupsLangPrintf(stdout, language,
+ _("\tprinter is on device \'%s\' speed -1\n"),
+ device);
+ }
+ }
+
+ if (accepting)
+ _cupsLangPuts(stdout, language, _("\tqueuing is enabled\n"));
+ else
+ _cupsLangPuts(stdout, language, _("\tqueuing is disabled\n"));
+
+ if (pstate != IPP_PRINTER_STOPPED)
+ _cupsLangPuts(stdout, language, _("\tprinting is enabled\n"));
+ else
+ _cupsLangPuts(stdout, language, _("\tprinting is disabled\n"));
+
+ if (jobcount == 0)
+ _cupsLangPuts(stdout, language, _("\tno entries\n"));
+ else
+ _cupsLangPrintf(stdout, language, _("\t%d entries\n"), jobcount);
+
+ _cupsLangPuts(stdout, language, _("\tdaemon present\n"));
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * End of "$Id: lpc.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/berkeley/lpq.c b/berkeley/lpq.c
new file mode 100644
index 000000000..a5e009171
--- /dev/null
+++ b/berkeley/lpq.c
@@ -0,0 +1,640 @@
+/*
+ * "$Id: lpq.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * "lpq" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and commands.
+ * show_jobs() - Show jobs.
+ * show_printer() - Show printer status.
+ * usage() - Show program usage.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/*
+ * Local functions...
+ */
+
+static int show_jobs(http_t *, const char *, const char *, const int,
+ const int);
+static void show_printer(http_t *, const char *);
+static void usage(void);
+
+
+/*
+ * 'main()' - Parse options and commands.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ http_t *http; /* Connection to server */
+ const char *dest, /* Desired printer */
+ *user, /* Desired user */
+ *val; /* Environment variable name */
+ char *instance; /* Printer instance */
+ int id, /* Desired job ID */
+ all, /* All printers */
+ interval, /* Reporting interval */
+ longstatus; /* Show file details */
+ int num_dests; /* Number of destinations */
+ cups_dest_t *dests; /* Destinations */
+ cups_lang_t *language; /* Language */
+#ifdef HAVE_SSL
+ http_encryption_t encryption; /* Encryption? */
+#endif /* HAVE_SSL */
+
+
+ language = cupsLangDefault();
+
+ /*
+ * Connect to the scheduler...
+ */
+
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
+ cupsEncryption())) == NULL)
+ {
+ _cupsLangPuts(stderr, language,
+ _("lpq: Unable to contact server!\n"));
+ return (1);
+ }
+
+ /*
+ * Check for command-line options...
+ */
+
+ dest = NULL;
+ user = NULL;
+ id = 0;
+ interval = 0;
+ longstatus = 0;
+ all = 0;
+ num_dests = cupsGetDests(&dests);
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '+')
+ interval = atoi(argv[i] + 1);
+ else if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'E' : /* Encrypt */
+#ifdef HAVE_SSL
+ encryption = HTTP_ENCRYPT_REQUIRED;
+
+ if (http)
+ httpEncryption(http, encryption);
+#else
+ _cupsLangPrintf(stderr, language,
+ _("%s: Sorry, no encryption support compiled in!\n"),
+ argv[0]);
+#endif /* HAVE_SSL */
+ break;
+
+ case 'P' : /* Printer */
+ if (argv[i][2])
+ dest = argv[i] + 2;
+ else
+ {
+ i ++;
+
+ if (i >= argc)
+ {
+ httpClose(http);
+ cupsFreeDests(num_dests, dests);
+
+ usage();
+ }
+
+ dest = argv[i];
+ }
+
+ if ((instance = strchr(dest, '/')) != NULL)
+ *instance++ = '\0';
+
+ if (cupsGetDest(dest, instance, num_dests, dests) == NULL)
+ {
+ if (instance)
+ _cupsLangPrintf(stderr, language,
+ _("lpq: Unknown destination \"%s/%s\"!\n"),
+ dest, instance);
+ else
+ _cupsLangPrintf(stderr, language,
+ _("lpq: Unknown destination \"%s\"!\n"), dest);
+
+ return (1);
+ }
+ break;
+
+ case 'a' : /* All printers */
+ all = 1;
+ break;
+
+ case 'l' : /* Long status */
+ longstatus = 1;
+ break;
+
+ default :
+ httpClose(http);
+ cupsFreeDests(num_dests, dests);
+
+ usage();
+ break;
+ }
+ }
+ else if (isdigit(argv[i][0] & 255))
+ id = atoi(argv[i]);
+ else
+ user = argv[i];
+
+ if (dest == NULL && !all)
+ {
+ for (i = 0; i < num_dests; i ++)
+ if (dests[i].is_default)
+ dest = dests[i].name;
+
+ if (dest == NULL)
+ {
+ val = NULL;
+
+ if ((dest = getenv("LPDEST")) == NULL)
+ {
+ if ((dest = getenv("PRINTER")) != NULL)
+ {
+ if (!strcmp(dest, "lp"))
+ dest = NULL;
+ else
+ val = "PRINTER";
+ }
+ }
+ else
+ val = "LPDEST";
+
+ if (dest && !cupsGetDest(dest, NULL, num_dests, dests))
+ _cupsLangPrintf(stderr, language,
+ _("lp: error - %s environment variable names "
+ "non-existent destination \"%s\"!\n"),
+ val, dest);
+ else
+ _cupsLangPuts(stderr, language,
+ _("lpq: error - no default destination available.\n"));
+ httpClose(http);
+ cupsFreeDests(num_dests, dests);
+ return (1);
+ }
+ }
+
+ /*
+ * Show the status in a loop...
+ */
+
+ for (;;)
+ {
+ if (dest)
+ show_printer(http, dest);
+
+ i = show_jobs(http, dest, user, id, longstatus);
+
+ if (i && interval)
+ {
+ fflush(stdout);
+ sleep(interval);
+ }
+ else
+ break;
+ }
+
+ /*
+ * Close the connection to the server and return...
+ */
+
+ cupsFreeDests(num_dests, dests);
+ httpClose(http);
+
+ return (0);
+}
+
+
+/*
+ * 'show_jobs()' - Show jobs.
+ */
+
+static int /* O - Number of jobs in queue */
+show_jobs(http_t *http, /* I - HTTP connection to server */
+ const char *dest, /* I - Destination */
+ const char *user, /* I - User */
+ const int id, /* I - Job ID */
+ const int longstatus)/* I - 1 if long report desired */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *jobdest, /* Pointer into job-printer-uri */
+ *jobuser, /* Pointer to job-originating-user-name */
+ *jobname; /* Pointer to job-name */
+ ipp_jstate_t jobstate; /* job-state */
+ int jobid, /* job-id */
+ jobsize, /* job-k-octets */
+#ifdef __osf__
+ jobpriority, /* job-priority */
+#endif /* __osf__ */
+ jobcount, /* Number of jobs */
+ jobcopies, /* Number of copies */
+ rank; /* Rank of job */
+ char resource[1024]; /* Resource string */
+ char rankstr[255]; /* Rank string */
+ char namestr[1024]; /* Job name string */
+ static const char *ranks[10] =/* Ranking strings */
+ {
+ "th",
+ "st",
+ "nd",
+ "rd",
+ "th",
+ "th",
+ "th",
+ "th",
+ "th",
+ "th"
+ };
+
+
+ DEBUG_printf(("show_jobs(%08x, %08x, %08x, %d, %d)\n", http, dest, user, id,
+ longstatus));
+
+ if (http == NULL)
+ return (0);
+
+ /*
+ * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
+ * the following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri or printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ attr = ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ attr = ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ if (dest == NULL)
+ {
+ if (id)
+ sprintf(resource, "ipp://localhost/jobs/%d", id);
+ else
+ strcpy(resource, "ipp://localhost/jobs");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
+ NULL, resource);
+ }
+ else
+ {
+ httpAssembleURIf(resource, sizeof(resource), "ipp", NULL, "localhost", 0,
+ "/printers/%s", dest);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, resource);
+ }
+
+ if (user)
+ {
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, user);
+ ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
+ }
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ jobcount = 0;
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ _cupsLangPrintf(stderr, language, _("lpq: get-jobs failed: %s\n"),
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return (0);
+ }
+
+ rank = 1;
+
+ /*
+ * Loop through the job list and display them...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ jobid = 0;
+ jobsize = 0;
+#ifdef __osf__
+ jobpriority = 50;
+#endif /* __osf__ */
+ jobstate = IPP_JOB_PENDING;
+ jobname = "untitled";
+ jobuser = NULL;
+ jobdest = NULL;
+ jobcopies = 1;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobid = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobsize = attr->values[0].integer;
+
+#ifdef __osf__
+ if (strcmp(attr->name, "job-priority") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobpriority = attr->values[0].integer;
+#endif /* __osf__ */
+
+ if (strcmp(attr->name, "job-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ jobstate = (ipp_jstate_t)attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-printer-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
+ jobdest ++;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ jobuser = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ jobname = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "copies") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobcopies = attr->values[0].integer;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (jobdest == NULL || jobid == 0)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ if (!longstatus && jobcount == 0)
+#ifdef __osf__
+ _cupsLangPuts(stdout, language,
+ _("Rank Owner Pri Job Files"
+ " Total Size\n"));
+#else
+ _cupsLangPuts(stdout, language,
+ _("Rank Owner Job File(s)"
+ " Total Size\n"));
+#endif /* __osf__ */
+
+ jobcount ++;
+
+ /*
+ * Display the job...
+ */
+
+ if (jobstate == IPP_JOB_PROCESSING)
+ strcpy(rankstr, "active");
+ else
+ {
+ /*
+ * Make the rank show the "correct" suffix for each number
+ * (11-13 are the only special cases, for English anyways...)
+ */
+
+ if ((rank % 100) >= 11 && (rank % 100) <= 13)
+ snprintf(rankstr, sizeof(rankstr), "%dth", rank);
+ else
+ snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
+
+ rank ++;
+ }
+
+ if (longstatus)
+ {
+ _cupsLangPuts(stdout, language, "");
+
+ if (jobcopies > 1)
+ snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
+ jobname);
+ else
+ strlcpy(namestr, jobname, sizeof(namestr));
+
+ _cupsLangPrintf(stdout, language, _("%s: %-33.33s [job %d localhost]\n"),
+ jobuser, rankstr, jobid);
+ _cupsLangPrintf(stdout, language, _(" %-39.39s %.0f bytes\n"),
+ namestr, 1024.0 * jobsize);
+ }
+ else
+#ifdef __osf__
+ _cupsLangPrintf(stdout, language,
+ _("%-6s %-10.10s %-4d %-10d %-27.27s %.0f bytes\n"),
+ rankstr, jobuser, jobpriority, jobid, jobname,
+ 1024.0 * jobsize);
+#else
+ _cupsLangPrintf(stdout, language,
+ _("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n"),
+ rankstr, jobuser, jobid, jobname, 1024.0 * jobsize);
+#endif /* __osf */
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ {
+ _cupsLangPrintf(stderr, language, _("lpq: get-jobs failed: %s\n"),
+ ippErrorString(cupsLastError()));
+ return (0);
+ }
+
+ if (jobcount == 0)
+ _cupsLangPuts(stdout, language, _("no entries\n"));
+
+ return (jobcount);
+}
+
+
+/*
+ * 'show_printer()' - Show printer status.
+ */
+
+static void
+show_printer(http_t *http, /* I - HTTP connection to server */
+ const char *dest) /* I - Destination */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ ipp_pstate_t state; /* Printer state */
+ char uri[HTTP_MAX_URI];
+ /* Printer URI */
+
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/printers/%s", dest);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lpq: get-printer-attributes failed: %s\n"),
+ ippErrorString(response->request.status.status_code));
+ ippDelete(response);
+ return;
+ }
+
+ if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+ state = (ipp_pstate_t)attr->values[0].integer;
+ else
+ state = IPP_PRINTER_STOPPED;
+
+ switch (state)
+ {
+ case IPP_PRINTER_IDLE :
+ _cupsLangPrintf(stdout, language, _("%s is ready\n"), dest);
+ break;
+ case IPP_PRINTER_PROCESSING :
+ _cupsLangPrintf(stdout, language, _("%s is ready and printing\n"),
+ dest);
+ break;
+ case IPP_PRINTER_STOPPED :
+ _cupsLangPrintf(stdout, language, _("%s is not ready\n"), dest);
+ break;
+ }
+
+ ippDelete(response);
+ }
+ else
+ _cupsLangPrintf(stderr, language,
+ _("lpq: get-printer-attributes failed: %s\n"),
+ ippErrorString(cupsLastError()));
+}
+
+
+/*
+ * 'usage()' - Show program usage.
+ */
+
+static void
+usage(void)
+{
+ _cupsLangPuts(stderr, cupsLangDefault(),
+ _("Usage: lpq [-P dest] [-l] [+interval]\n"));
+ exit(1);
+}
+
+
+/*
+ * End of "$Id: lpq.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/berkeley/lpr.c b/berkeley/lpr.c
new file mode 100644
index 000000000..58c9202f4
--- /dev/null
+++ b/berkeley/lpr.c
@@ -0,0 +1,495 @@
+/*
+ * "$Id: lpr.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * "lpr" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and send files for printing.
+ * sighandler() - Signal catcher for when we print from stdin...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#ifndef WIN32
+# include
+# include
+
+
+/*
+ * Local functions.
+ */
+
+void sighandler(int);
+#endif /* !WIN32 */
+
+
+/*
+ * Globals...
+ */
+
+char tempfile[1024]; /* Temporary file for printing from stdin */
+
+
+/*
+ * 'main()' - Parse options and send files for printing.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i, j; /* Looping var */
+ int job_id; /* Job ID */
+ char ch; /* Option character */
+ char *printer, /* Destination printer or class */
+ *instance; /* Instance */
+ const char *title, /* Job title */
+ *val; /* Environment variable name */
+ int num_copies; /* Number of copies per file */
+ int num_files; /* Number of files to print */
+ const char *files[1000]; /* Files to print */
+ int num_dests; /* Number of destinations */
+ cups_dest_t *dests, /* Destinations */
+ *dest; /* Selected destination */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ int deletefile; /* Delete file after print? */
+ char buffer[8192]; /* Copy buffer */
+ int temp; /* Temporary file descriptor */
+ cups_lang_t *language; /* Language information */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+ struct sigaction action; /* Signal action */
+ struct sigaction oldaction; /* Old signal action */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+
+
+ deletefile = 0;
+ printer = NULL;
+ num_dests = 0;
+ dests = NULL;
+ num_options = 0;
+ options = NULL;
+ num_files = 0;
+ title = NULL;
+ language = cupsLangDefault();
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (ch = argv[i][1])
+ {
+ case 'E' : /* Encrypt */
+#ifdef HAVE_SSL
+ cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
+#else
+ _cupsLangPrintf(stderr, language,
+ _("%s: Sorry, no encryption support compiled in!\n"),
+ argv[0]);
+#endif /* HAVE_SSL */
+ break;
+
+ case '1' : /* TROFF font set 1 */
+ case '2' : /* TROFF font set 2 */
+ case '3' : /* TROFF font set 3 */
+ case '4' : /* TROFF font set 4 */
+ case 'i' : /* indent */
+ case 'w' : /* width */
+ if (argv[i][2] == '\0')
+ {
+ i ++;
+
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - expected value after -%c "
+ "option!\n"), ch);
+ return (1);
+ }
+ }
+
+ case 'c' : /* CIFPLOT */
+ case 'd' : /* DVI */
+ case 'f' : /* FORTRAN */
+ case 'g' : /* plot */
+ case 'n' : /* Ditroff */
+ case 't' : /* Troff */
+ case 'v' : /* Raster image */
+ _cupsLangPrintf(stderr, language,
+ _("lpr: warning - \'%c\' format modifier not "
+ "supported - output may not be correct!\n"), ch);
+ break;
+
+ case 'o' : /* Option */
+ if (argv[i][2] != '\0')
+ num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPuts(stderr, language,
+ _("lpr: error - expected option=value after "
+ "-o option!\n"));
+ return (1);
+ }
+
+ num_options = cupsParseOptions(argv[i], num_options, &options);
+ }
+ break;
+
+ case 'l' : /* Literal/raw */
+ num_options = cupsAddOption("raw", "", num_options, &options);
+ break;
+
+ case 'p' : /* Prettyprint */
+ num_options = cupsAddOption("prettyprint", "", num_options, &options);
+ break;
+
+ case 'h' : /* Suppress burst page */
+ num_options = cupsAddOption("job-sheets", "none", num_options, &options);
+ break;
+
+ case 's' : /* Don't use symlinks */
+ break;
+
+ case 'm' : /* Mail on completion */
+ _cupsLangPuts(stderr, language,
+ _("lpr: warning - email notification is not "
+ "currently supported!\n"));
+ break;
+
+ case 'q' : /* Queue file but don't print */
+ num_options = cupsAddOption("job-hold-until", "indefinite",
+ num_options, &options);
+ break;
+
+ case 'r' : /* Remove file after printing */
+ deletefile = 1;
+ break;
+
+ case 'P' : /* Destination printer or class */
+ if (argv[i][2] != '\0')
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPuts(stderr, language,
+ _("lpr: error - expected destination after -P "
+ "option!\n"));
+ return (1);
+ }
+
+ printer = argv[i];
+ }
+
+ if ((instance = strrchr(printer, '/')) != NULL)
+ *instance++ = '\0';
+
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if ((dest = cupsGetDest(printer, instance, num_dests, dests)) != NULL)
+ {
+ for (j = 0; j < dest->num_options; j ++)
+ if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
+ num_options = cupsAddOption(dest->options[j].name,
+ dest->options[j].value,
+ num_options, &options);
+ }
+ break;
+
+ case '#' : /* Number of copies */
+ if (argv[i][2] != '\0')
+ num_copies = atoi(argv[i] + 2);
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPuts(stderr, language,
+ _("lpr: error - expected copy count after -# "
+ "option!\n"));
+ return (1);
+ }
+
+ num_copies = atoi(argv[i]);
+ }
+
+ sprintf(buffer, "%d", num_copies);
+ num_options = cupsAddOption("copies", buffer, num_options, &options);
+ break;
+
+ case 'C' : /* Class */
+ case 'J' : /* Job name */
+ case 'T' : /* Title */
+ if (argv[i][2] != '\0')
+ title = argv[i] + 2;
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - expected name after -%c "
+ "option!\n"), ch);
+ return (1);
+ }
+
+ title = argv[i];
+ }
+ break;
+
+ case 'U' : /* User */
+ if (argv[i][2] != '\0')
+ cupsSetUser(argv[i] + 2);
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ _cupsLangPuts(stderr, language,
+ _("lpr: error - expected username after -U "
+ "option!\n"));
+ return (1);
+ }
+
+ cupsSetUser(argv[i]);
+ }
+ break;
+
+ default :
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - unknown option \'%c\'!\n"),
+ argv[i][1]);
+ return (1);
+ }
+ else if (num_files < 1000)
+ {
+ /*
+ * Print a file...
+ */
+
+ if (access(argv[i], R_OK) != 0)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - unable to access \"%s\" - %s\n"),
+ argv[i], strerror(errno));
+ return (1);
+ }
+
+ files[num_files] = argv[i];
+ num_files ++;
+
+ if (title == NULL)
+ {
+ if ((title = strrchr(argv[i], '/')) != NULL)
+ title ++;
+ else
+ title = argv[i];
+ }
+ }
+ else
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - too many files - \"%s\"\n"), argv[i]);
+ /*
+ * See if we have any files to print; if not, print from stdin...
+ */
+
+ if (printer == NULL)
+ {
+ if (num_dests == 0)
+ num_dests = cupsGetDests(&dests);
+
+ if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
+ {
+ printer = dest->name;
+
+ for (j = 0; j < dest->num_options; j ++)
+ if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
+ num_options = cupsAddOption(dest->options[j].name,
+ dest->options[j].value,
+ num_options, &options);
+ }
+ }
+
+ if (printer == NULL)
+ {
+ val = NULL;
+
+ if ((printer = getenv("LPDEST")) == NULL)
+ {
+ if ((printer = getenv("PRINTER")) != NULL)
+ {
+ if (!strcmp(printer, "lp"))
+ printer = NULL;
+ else
+ val = "PRINTER";
+ }
+ }
+ else
+ val = "LPDEST";
+
+ if (printer && !cupsGetDest(printer, NULL, num_dests, dests))
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - %s environment variable names "
+ "non-existent destination \"%s\"!\n"),
+ val, printer);
+ else if (cupsLastError() == IPP_NOT_FOUND)
+ _cupsLangPuts(stderr, language,
+ _("lpr: error - no default destination available.\n"));
+ else
+ _cupsLangPuts(stderr, language,
+ _("lpr: error - scheduler not responding!\n"));
+
+ return (1);
+ }
+
+ if (num_files > 0)
+ {
+ job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options);
+
+ if (deletefile && job_id > 0)
+ {
+ /*
+ * Delete print files after printing...
+ */
+
+ for (i = 0; i < num_files; i ++)
+ unlink(files[i]);
+ }
+ }
+ else
+ {
+ num_files = 1;
+
+#ifndef WIN32
+# if defined(HAVE_SIGSET)
+ sigset(SIGHUP, sighandler);
+ if (sigset(SIGINT, sighandler) == SIG_IGN)
+ sigset(SIGINT, SIG_IGN);
+ sigset(SIGTERM, sighandler);
+# elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sighandler;
+
+ sigaction(SIGHUP, &action, NULL);
+ sigaction(SIGINT, NULL, &oldaction);
+ if (oldaction.sa_handler != SIG_IGN)
+ sigaction(SIGINT, &action, NULL);
+ sigaction(SIGTERM, &action, NULL);
+# else
+ signal(SIGHUP, sighandler);
+ if (signal(SIGINT, sighandler) == SIG_IGN)
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTERM, sighandler);
+# endif
+#endif /* !WIN32 */
+
+ if ((temp = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - unable to create temporary file "
+ "\"%s\" - %s\n"),
+ tempfile, strerror(errno));
+ return (1);
+ }
+
+ while ((i = read(0, buffer, sizeof(buffer))) > 0)
+ if (write(temp, buffer, i) < 0)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - unable to write to temporary file "
+ "\"%s\" - %s\n"),
+ tempfile, strerror(errno));
+ close(temp);
+ unlink(tempfile);
+ return (1);
+ }
+
+ i = lseek(temp, 0, SEEK_CUR);
+ close(temp);
+
+ if (i == 0)
+ {
+ _cupsLangPuts(stderr, language,
+ _("lpr: error - stdin is empty, so no job has been sent.\n"));
+ unlink(tempfile);
+ return (1);
+ }
+
+ if (title)
+ job_id = cupsPrintFile(printer, tempfile, title, num_options, options);
+ else
+ job_id = cupsPrintFile(printer, tempfile, "(stdin)", num_options, options);
+
+ unlink(tempfile);
+ }
+
+ if (job_id < 1)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lpr: error - unable to print file: %s\n"),
+ ippErrorString(cupsLastError()));
+ return (1);
+ }
+
+ return (0);
+}
+
+
+#ifndef WIN32
+/*
+ * 'sighandler()' - Signal catcher for when we print from stdin...
+ */
+
+void
+sighandler(int s) /* I - Signal number */
+{
+ /*
+ * Remove the temporary file we're using to print from stdin...
+ */
+
+ unlink(tempfile);
+
+ /*
+ * Exit...
+ */
+
+ exit(s);
+}
+#endif /* !WIN32 */
+
+
+/*
+ * End of "$Id: lpr.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/berkeley/lprm.c b/berkeley/lprm.c
new file mode 100644
index 000000000..f6fe9fc6b
--- /dev/null
+++ b/berkeley/lprm.c
@@ -0,0 +1,284 @@
+/*
+ * "$Id: lprm.c 4906 2006-01-10 20:53:28Z mike $"
+ *
+ * "lprm" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and cancel jobs.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+
+/*
+ * 'main()' - Parse options and cancel jobs.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ http_t *http; /* HTTP connection to server */
+ int i; /* Looping var */
+ int job_id; /* Job ID */
+ const char *dest; /* Destination printer */
+ char *instance; /* Pointer to instance name */
+ char uri[1024]; /* Printer or job URI */
+ ipp_t *request; /* IPP request */
+ ipp_t *response; /* IPP response */
+ ipp_op_t op; /* Operation */
+ cups_lang_t *language; /* Language */
+ int num_dests; /* Number of destinations */
+ cups_dest_t *dests; /* Destinations */
+ http_encryption_t encryption; /* Encryption? */
+
+
+ /*
+ * Setup to cancel individual print jobs...
+ */
+
+ op = IPP_CANCEL_JOB;
+ job_id = 0;
+ dest = NULL;
+ response = NULL;
+ http = NULL;
+ encryption = cupsEncryption();
+ language = cupsLangDefault();
+ num_dests = cupsGetDests(&dests);
+
+ for (i = 0; i < num_dests; i ++)
+ if (dests[i].is_default)
+ dest = dests[i].name;
+
+ /*
+ * Open a connection to the server...
+ */
+
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), encryption)) == NULL)
+ {
+ _cupsLangPuts(stderr, language, _("lprm: Unable to contact server!\n"));
+ cupsFreeDests(num_dests, dests);
+ return (1);
+ }
+
+ /*
+ * Process command-line arguments...
+ */
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-' && argv[i][1] != '\0')
+ switch (argv[i][1])
+ {
+ case 'E' : /* Encrypt */
+#ifdef HAVE_SSL
+ encryption = HTTP_ENCRYPT_REQUIRED;
+
+ httpEncryption(http, encryption);
+#else
+ _cupsLangPrintf(stderr, language,
+ _("%s: Sorry, no encryption support compiled in!\n"),
+ argv[0]);
+#endif /* HAVE_SSL */
+ break;
+
+ case 'P' : /* Cancel jobs on a printer */
+ if (argv[i][2])
+ dest = argv[i] + 2;
+ else
+ {
+ i ++;
+ dest = argv[i];
+ }
+
+ if ((instance = strchr(dest, '/')) != NULL)
+ *instance = '\0';
+
+ if (cupsGetDest(dest, NULL, num_dests, dests) == NULL)
+ {
+ _cupsLangPrintf(stderr, language,
+ _("lprm: Unknown destination \"%s\"!\n"), dest);
+ cupsFreeDests(num_dests, dests);
+ httpClose(http);
+ return(1);
+ }
+ break;
+
+ default :
+ _cupsLangPrintf(stderr, language,
+ _("lprm: Unknown option \'%c\'!\n"), argv[i][1]);
+ cupsFreeDests(num_dests, dests);
+ httpClose(http);
+ return (1);
+ }
+ else
+ {
+ /*
+ * Cancel a job or printer...
+ */
+
+ if (isdigit(argv[i][0] & 255) &&
+ cupsGetDest(argv[i], NULL, num_dests, dests) == NULL)
+ {
+ dest = NULL;
+ op = IPP_CANCEL_JOB;
+ job_id = atoi(argv[i]);
+ }
+ else if (strcmp(argv[i], "-") == 0)
+ {
+ /*
+ * Cancel all jobs
+ */
+
+ op = IPP_PURGE_JOBS;
+ }
+ else
+ {
+ dest = argv[i];
+ job_id = 0;
+ }
+
+ /*
+ * Build an IPP request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri + job-id *or* job-uri
+ * [requesting-user-name]
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = op;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ if (dest)
+ {
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/printers/%s", dest);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
+ job_id);
+ }
+ else
+ {
+ sprintf(uri, "ipp://localhost/jobs/%d", job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
+ uri);
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser());
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if (op == IPP_PURGE_JOBS)
+ response = cupsDoRequest(http, request, "/admin/");
+ else
+ response = cupsDoRequest(http, request, "/jobs/");
+
+ if (response != NULL)
+ {
+ switch (response->request.status.status_code)
+ {
+ case IPP_NOT_FOUND :
+ _cupsLangPuts(stderr, language,
+ _("lprm: Job or printer not found!\n"));
+ break;
+ case IPP_NOT_AUTHORIZED :
+ _cupsLangPuts(stderr, language,
+ _("lprm: Not authorized to lprm job(s)!\n"));
+ break;
+ case IPP_FORBIDDEN :
+ _cupsLangPrintf(stderr, language,
+ _("lprm: You don't own job ID %d!\n"), job_id);
+ break;
+ default :
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ _cupsLangPuts(stderr, language,
+ _("lprm: Unable to lprm job(s)!\n"));
+ break;
+ }
+
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ ippDelete(response);
+ cupsFreeDests(num_dests, dests);
+ httpClose(http);
+ return (1);
+ }
+
+ ippDelete(response);
+ }
+ else
+ {
+ _cupsLangPuts(stderr, language,
+ _("lprm: Unable to cancel job(s)!\n"));
+ cupsFreeDests(num_dests, dests);
+ httpClose(http);
+ return (1);
+ }
+ }
+
+ /*
+ * If nothing has been cancelled yet, cancel the current job on the specified
+ * (or default) printer...
+ */
+
+ if (response == NULL)
+ if (!cupsCancelJob(dest, 0))
+ {
+ _cupsLangPuts(stderr, language,
+ _("lprm: Unable to cancel job(s)!\n"));
+ cupsFreeDests(num_dests, dests);
+ httpClose(http);
+ return (1);
+ }
+
+ cupsFreeDests(num_dests, dests);
+ httpClose(http);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: lprm.c 4906 2006-01-10 20:53:28Z mike $".
+ */
diff --git a/cgi-bin/Dependencies b/cgi-bin/Dependencies
new file mode 100644
index 000000000..fbcdc3d50
--- /dev/null
+++ b/cgi-bin/Dependencies
@@ -0,0 +1,50 @@
+# DO NOT DELETE
+
+help-index.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+help-index.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+help-index.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h
+help-index.o: ../cups/array.h ../cups/string.h ../config.h ../cups/dir.h
+html.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+html.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+html.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+html.o: ../cups/string.h ../config.h
+ipp-var.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+ipp-var.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+ipp-var.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+ipp-var.o: ../cups/string.h ../config.h
+search.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+search.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+search.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+search.o: ../cups/string.h ../config.h
+template.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+template.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+template.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+template.o: ../cups/string.h ../config.h
+var.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+var.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+var.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+var.o: ../cups/string.h ../config.h
+admin.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+admin.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+admin.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+admin.o: ../cups/string.h ../config.h ../cups/file.h
+classes.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+classes.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+classes.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+classes.o: ../cups/string.h ../config.h
+help.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+help.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+help.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+help.o: ../cups/string.h ../config.h
+jobs.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+jobs.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+jobs.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+jobs.o: ../cups/string.h ../config.h
+printers.o: cgi-private.h cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h
+printers.o: ../cups/md5.h ../cups/ppd.h ../cups/file.h help-index.h
+printers.o: ../cups/debug.h ../cups/i18n.h ../cups/language.h ../cups/array.h
+printers.o: ../cups/string.h ../config.h
+testcgi.o: cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h ../cups/md5.h
+testcgi.o: ../cups/ppd.h ../cups/file.h help-index.h
+testhi.o: cgi.h ../cups/cups.h ../cups/ipp.h ../cups/http.h ../cups/md5.h
+testhi.o: ../cups/ppd.h ../cups/file.h help-index.h
diff --git a/cgi-bin/Makefile b/cgi-bin/Makefile
new file mode 100644
index 000000000..fd218a306
--- /dev/null
+++ b/cgi-bin/Makefile
@@ -0,0 +1,151 @@
+#
+# "$Id: Makefile 4869 2005-12-06 02:43:40Z mike $"
+#
+# CGI makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-2005 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636 USA
+#
+# Voice: (301) 373-9600
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+CGIS = admin.cgi classes.cgi help.cgi jobs.cgi printers.cgi
+TARGETS = libcgi.a $(CGIS) testcgi testhi
+LIBOBJS = help-index.o html.o ipp-var.o search.o template.o var.o
+OBJS = $(LIBOBJS) admin.o classes.o help.o \
+ jobs.o printers.o testcgi.o testhi.o
+
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+
+#
+# Clean all object files...
+#
+
+clean:
+ $(RM) $(OBJS) $(TARGETS)
+
+
+#
+# Update dependencies (without system header dependencies...)
+#
+
+depend:
+ makedepend -Y -I.. -fDependencies $(OBJS:.o=.c) >/dev/null 2>&1
+
+
+#
+# Install all targets...
+#
+
+install: all
+ $(INSTALL_DIR) $(SERVERBIN)/cgi-bin
+ for file in $(CGIS); do \
+ $(INSTALL_BIN) $$file $(SERVERBIN)/cgi-bin; \
+ done
+
+
+#
+# libcgi.a
+#
+
+libcgi.a: $(LIBOBJS)
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+
+#
+# admin.cgi
+#
+
+admin.cgi: admin.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ admin.o libcgi.a $(LIBS)
+
+
+#
+# classes.cgi
+#
+
+classes.cgi: classes.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ classes.o libcgi.a $(LIBS)
+
+
+#
+# help.cgi
+#
+
+help.cgi: help.o ../Makedefs libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ help.o libcgi.a $(LIBS)
+
+
+#
+# jobs.cgi
+#
+
+jobs.cgi: jobs.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ jobs.o libcgi.a $(LIBS)
+
+
+#
+# printers.cgi
+#
+
+printers.cgi: printers.o ../Makedefs ../cups/$(LIBCUPS) libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ printers.o libcgi.a $(LIBS)
+
+
+#
+# testcgi
+#
+
+testcgi: testcgi.o ../Makedefs libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testcgi.o libcgi.a $(LIBS)
+
+
+#
+# testhi
+#
+
+testhi: testhi.o ../Makedefs libcgi.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testhi.o libcgi.a $(LIBS)
+
+
+#
+# Dependencies...
+#
+
+include Dependencies
+
+
+#
+# End of "$Id: Makefile 4869 2005-12-06 02:43:40Z mike $".
+#
diff --git a/cgi-bin/admin.c b/cgi-bin/admin.c
new file mode 100644
index 000000000..37be8beaa
--- /dev/null
+++ b/cgi-bin/admin.c
@@ -0,0 +1,3572 @@
+/*
+ * "$Id: admin.c 4921 2006-01-12 21:26:26Z mike $"
+ *
+ * Administration CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for CGI.
+ * compare_printer_devices() - Compare two printer devices.
+ * do_am_class() - Add or modify a class.
+ * do_am_printer() - Add or modify a printer.
+ * do_config_printer() - Configure the default options for a printer.
+ * do_config_server() - Configure server settings.
+ * do_delete_class() - Delete a class...
+ * do_delete_printer() - Delete a printer...
+ * do_menu() - Show the main menu...
+ * do_printer_op() - Do a printer operation.
+ * do_set_allowed_users() - Set the allowed/denied users for a queue.
+ * do_set_sharing() - Set printer-is-shared value...
+ * match_string() - Return the number of matching characters.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cgi-private.h"
+#include
+#include
+
+
+/*
+ * Local functions...
+ */
+
+static void do_am_class(http_t *http, cups_lang_t *language, int modify);
+static void do_am_printer(http_t *http, cups_lang_t *language, int modify);
+static void do_config_printer(http_t *http, cups_lang_t *language);
+static void do_config_server(http_t *http, cups_lang_t *language);
+static void do_delete_class(http_t *http, cups_lang_t *language);
+static void do_delete_printer(http_t *http, cups_lang_t *language);
+static void do_menu(http_t *http, cups_lang_t *language);
+static void do_printer_op(http_t *http, cups_lang_t *language,
+ ipp_op_t op, const char *title);
+static void do_set_allowed_users(http_t *http, cups_lang_t *language);
+static void do_set_sharing(http_t *http, cups_lang_t *language);
+static int match_string(const char *a, const char *b);
+
+
+/*
+ * 'main()' - Main entry for CGI.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ cups_lang_t *language; /* Language information */
+ http_t *http; /* Connection to the server */
+ const char *op; /* Operation name */
+
+
+ /*
+ * Get the request language...
+ */
+
+ language = cupsLangDefault();
+
+ /*
+ * Connect to the HTTP server...
+ */
+
+ http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
+
+ /*
+ * Set the web interface section...
+ */
+
+ cgiSetVariable("SECTION", "admin");
+
+ /*
+ * See if we have form data...
+ */
+
+ if (!cgiInitialize())
+ {
+ /*
+ * Nope, send the administration menu...
+ */
+
+ do_menu(http, language);
+ }
+ else if ((op = cgiGetVariable("OP")) != NULL)
+ {
+ /*
+ * Do the operation...
+ */
+
+ if (!strcmp(op, "redirect"))
+ {
+ const char *url; /* Redirection URL... */
+
+
+ if ((url = cgiGetVariable("URL")) != NULL)
+ printf("Location: %s\n\n", url);
+ else
+ puts("Location: /admin\n");
+ }
+ else if (!strcmp(op, "start-printer"))
+ do_printer_op(http, language, IPP_RESUME_PRINTER, "Start Printer");
+ else if (!strcmp(op, "stop-printer"))
+ do_printer_op(http, language, IPP_PAUSE_PRINTER, "Stop Printer");
+ else if (!strcmp(op, "start-class"))
+ do_printer_op(http, language, IPP_RESUME_PRINTER, "Start Class");
+ else if (!strcmp(op, "stop-class"))
+ do_printer_op(http, language, IPP_PAUSE_PRINTER, "Stop Class");
+ else if (!strcmp(op, "accept-jobs"))
+ do_printer_op(http, language, CUPS_ACCEPT_JOBS, "Accept Jobs");
+ else if (!strcmp(op, "reject-jobs"))
+ do_printer_op(http, language, CUPS_REJECT_JOBS, "Reject Jobs");
+ else if (!strcmp(op, "purge-jobs"))
+ do_printer_op(http, language, IPP_PURGE_JOBS, "Purge Jobs");
+ else if (!strcmp(op, "set-allowed-users"))
+ do_set_allowed_users(http, language);
+ else if (!strcmp(op, "set-as-default"))
+ do_printer_op(http, language, CUPS_SET_DEFAULT, "Set As Default");
+ else if (!strcmp(op, "set-sharing"))
+ do_set_sharing(http, language);
+ else if (!strcmp(op, "add-class"))
+ do_am_class(http, language, 0);
+ else if (!strcmp(op, "add-printer"))
+ do_am_printer(http, language, 0);
+ else if (!strcmp(op, "modify-class"))
+ do_am_class(http, language, 1);
+ else if (!strcmp(op, "modify-printer"))
+ do_am_printer(http, language, 1);
+ else if (!strcmp(op, "delete-class"))
+ do_delete_class(http, language);
+ else if (!strcmp(op, "delete-printer"))
+ do_delete_printer(http, language);
+ else if (!strcmp(op, "set-printer-options"))
+ do_config_printer(http, language);
+ else if (!strcmp(op, "config-server"))
+ do_config_server(http, language);
+ else
+ {
+ /*
+ * Bad operation code... Display an error...
+ */
+
+ cgiStartHTML("Error");
+ cgiCopyTemplateLang("admin-op.tmpl");
+ cgiEndHTML();
+ }
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ }
+ else
+ {
+ /*
+ * Form data but no operation code... Display an error...
+ */
+
+ cgiStartHTML("Error");
+ cgiCopyTemplateLang("admin-op.tmpl");
+ cgiEndHTML();
+ }
+
+ /*
+ * Free the request language...
+ */
+
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'compare_printer_devices()' - Compare two printer devices.
+ */
+
+static int /* O - Result of comparison */
+compare_printer_devices(const void *a, /* I - First device */
+ const void *b) /* I - Second device */
+{
+ return (strcmp(*((char **)a), *((char **)b)));
+}
+
+
+/*
+ * 'do_am_class()' - Add or modify a class.
+ */
+
+static void
+do_am_class(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language, /* I - Client's language */
+ int modify) /* I - Modify the printer? */
+{
+ int i, j; /* Looping vars */
+ int element; /* Element number */
+ int num_printers; /* Number of printers */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* member-uris attribute */
+ ipp_status_t status; /* Request status */
+ char uri[HTTP_MAX_URI]; /* Device or printer URI */
+ const char *name, /* Pointer to class name */
+ *ptr; /* Pointer to CGI variable */
+ const char *title; /* Title of page */
+ static const char * const pattrs[] = /* Requested printer attributes */
+ {
+ "member-names",
+ "printer-info",
+ "printer-location"
+ };
+
+
+ title = modify ? "Modify Class" : "Add Class";
+ name = cgiGetVariable("PRINTER_NAME");
+
+ if (cgiGetVariable("PRINTER_LOCATION") == NULL)
+ {
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, "ipp://localhost/printers");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ /*
+ * Create MEMBER_URIS and MEMBER_NAMES arrays...
+ */
+
+ for (element = 0, attr = response->attrs;
+ attr != NULL;
+ attr = attr->next)
+ if (attr->name && !strcmp(attr->name, "printer-uri-supported"))
+ {
+ if ((ptr = strrchr(attr->values[0].string.text, '/')) != NULL &&
+ (!name || strcasecmp(name, ptr + 1)))
+ {
+ /*
+ * Don't show the current class...
+ */
+
+ cgiSetArray("MEMBER_URIS", element, attr->values[0].string.text);
+ element ++;
+ }
+ }
+
+ for (element = 0, attr = response->attrs;
+ attr != NULL;
+ attr = attr->next)
+ if (attr->name && !strcmp(attr->name, "printer-name"))
+ {
+ if (!name || strcasecmp(name, attr->values[0].string.text))
+ {
+ /*
+ * Don't show the current class...
+ */
+
+ cgiSetArray("MEMBER_NAMES", element, attr->values[0].string.text);
+ element ++;
+ }
+ }
+
+ num_printers = cgiGetSize("MEMBER_URIS");
+
+ ippDelete(response);
+ }
+ else
+ num_printers = 0;
+
+ if (modify)
+ {
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/classes/%s", name);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes",
+ (int)(sizeof(pattrs) / sizeof(pattrs[0])),
+ NULL, pattrs);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ if ((attr = ippFindAttribute(response, "member-names", IPP_TAG_NAME)) != NULL)
+ {
+ /*
+ * Mark any current members in the class...
+ */
+
+ for (j = 0; j < num_printers; j ++)
+ cgiSetArray("MEMBER_SELECTED", j, "");
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (j = 0; j < num_printers; j ++)
+ {
+ if (!strcasecmp(attr->values[i].string.text,
+ cgiGetArray("MEMBER_NAMES", j)))
+ {
+ cgiSetArray("MEMBER_SELECTED", j, "SELECTED");
+ break;
+ }
+ }
+ }
+ }
+
+ if ((attr = ippFindAttribute(response, "printer-info",
+ IPP_TAG_TEXT)) != NULL)
+ cgiSetVariable("PRINTER_INFO", attr->values[0].string.text);
+
+ if ((attr = ippFindAttribute(response, "printer-location",
+ IPP_TAG_TEXT)) != NULL)
+ cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text);
+
+ ippDelete(response);
+ }
+
+ /*
+ * Update the location and description of an existing printer...
+ */
+
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("modify-class.tmpl");
+ }
+ else
+ {
+ /*
+ * Get the name, location, and description for a new printer...
+ */
+
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("add-class.tmpl");
+ }
+
+ cgiEndHTML();
+
+ return;
+ }
+
+ for (ptr = name; *ptr; ptr ++)
+ if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
+ break;
+
+ if (*ptr || ptr == name || strlen(name) > 127)
+ {
+ cgiSetVariable("ERROR", "The class name may only contain up to 127 printable "
+ "characters and may not contain spaces, slashes (/), "
+ "or the pound sign (#).");
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ return;
+ }
+
+ /*
+ * Build a CUPS_ADD_CLASS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * printer-location
+ * printer-info
+ * printer-is-accepting-jobs
+ * printer-state
+ * member-uris
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_CLASS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/classes/%s", cgiGetVariable("PRINTER_NAME"));
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
+ NULL, cgiGetVariable("PRINTER_LOCATION"));
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
+ NULL, cgiGetVariable("PRINTER_INFO"));
+
+ ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
+
+ ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+ IPP_PRINTER_IDLE);
+
+ if ((num_printers = cgiGetSize("MEMBER_URIS")) > 0)
+ {
+ attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris",
+ num_printers, NULL, NULL);
+ for (i = 0; i < num_printers; i ++)
+ attr->values[i].string.text = strdup(cgiGetArray("MEMBER_URIS", i));
+ }
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ status = response->request.status.status_code;
+ ippDelete(response);
+ }
+ else
+ status = cupsLastError();
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiStartHTML(title);
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang("error.tmpl");
+ }
+ else
+ {
+ /*
+ * Redirect successful updates back to the class page...
+ */
+
+ char refresh[1024]; /* Refresh URL */
+
+ cgiFormEncode(uri, name, sizeof(uri));
+ snprintf(refresh, sizeof(refresh), "5;/admin?OP=redirect&URL=/classes/%s",
+ uri);
+ cgiSetVariable("refresh_page", refresh);
+
+ cgiStartHTML(title);
+
+ if (modify)
+ cgiCopyTemplateLang("class-modified.tmpl");
+ else
+ cgiCopyTemplateLang("class-added.tmpl");
+ }
+
+ cgiEndHTML();
+}
+
+
+/*
+ * 'do_am_printer()' - Add or modify a printer.
+ */
+
+static void
+do_am_printer(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language, /* I - Client's language */
+ int modify) /* I - Modify the printer? */
+{
+ int i; /* Looping var */
+ int element; /* Element number */
+ ipp_attribute_t *attr, /* Current attribute */
+ *last; /* Last attribute */
+ ipp_t *request, /* IPP request */
+ *response, /* IPP response */
+ *oldinfo; /* Old printer information */
+ ipp_status_t status; /* Request status */
+ const cgi_file_t *file; /* Uploaded file, if any */
+ const char *var; /* CGI variable */
+ char uri[HTTP_MAX_URI], /* Device or printer URI */
+ *uriptr; /* Pointer into URI */
+ int maxrate; /* Maximum baud rate */
+ char baudrate[255]; /* Baud rate string */
+ const char *name, /* Pointer to class name */
+ *ptr; /* Pointer to CGI variable */
+ const char *title; /* Title of page */
+ static int baudrates[] = /* Baud rates */
+ {
+ 1200,
+ 2400,
+ 4800,
+ 9600,
+ 19200,
+ 38400,
+ 57600,
+ 115200,
+ 230400,
+ 460800
+ };
+
+
+ title = modify ? "Modify Printer" : "Add Printer";
+
+ if (modify)
+ {
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/printers/%s", cgiGetVariable("PRINTER_NAME"));
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ oldinfo = cupsDoRequest(http, request, "/");
+ }
+ else
+ oldinfo = NULL;
+
+ if ((name = cgiGetVariable("PRINTER_NAME")) == NULL ||
+ cgiGetVariable("PRINTER_LOCATION") == NULL)
+ {
+ cgiStartHTML(title);
+
+ if (modify)
+ {
+ /*
+ * Update the location and description of an existing printer...
+ */
+
+ if (oldinfo)
+ cgiSetIPPVars(oldinfo, NULL, NULL, NULL, 0);
+
+ cgiCopyTemplateLang("modify-printer.tmpl");
+ }
+ else
+ {
+ /*
+ * Get the name, location, and description for a new printer...
+ */
+
+ cgiCopyTemplateLang("add-printer.tmpl");
+ }
+
+ cgiEndHTML();
+
+ if (oldinfo)
+ ippDelete(oldinfo);
+
+ return;
+ }
+
+ for (ptr = name; *ptr; ptr ++)
+ if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
+ break;
+
+ if (*ptr || ptr == name || strlen(name) > 127)
+ {
+ cgiSetVariable("ERROR", "The printer name may only contain up to 127 printable "
+ "characters and may not contain spaces, slashes (/), "
+ "or the pound sign (#).");
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ return;
+ }
+
+ file = cgiGetFile();
+
+ if (file)
+ {
+ fprintf(stderr, "DEBUG: file->tempfile=%s\n", file->tempfile);
+ fprintf(stderr, "DEBUG: file->name=%s\n", file->name);
+ fprintf(stderr, "DEBUG: file->filename=%s\n", file->filename);
+ fprintf(stderr, "DEBUG: file->mimetype=%s\n", file->mimetype);
+ }
+
+ if ((var = cgiGetVariable("DEVICE_URI")) == NULL)
+ {
+ /*
+ * Build a CUPS_GET_DEVICES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_DEVICES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, "ipp://localhost/printers/");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ cgiSetIPPVars(response, NULL, NULL, NULL, 0);
+ ippDelete(response);
+ }
+
+ /*
+ * Let the user choose...
+ */
+
+ if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
+ {
+ strlcpy(uri, attr->values[0].string.text, sizeof(uri));
+ if ((uriptr = strchr(uri, ':')) != NULL && strncmp(uriptr, "://", 3) == 0)
+ *uriptr = '\0';
+
+ cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text);
+ cgiSetVariable("CURRENT_DEVICE_SCHEME", uri);
+ }
+
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("choose-device.tmpl");
+ cgiEndHTML();
+ }
+ else if (strchr(var, '/') == NULL)
+ {
+ if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
+ {
+ /*
+ * Set the current device URI for the form to the old one...
+ */
+
+ if (strncmp(attr->values[0].string.text, var, strlen(var)) == 0)
+ cgiSetVariable("DEVICE_URI", attr->values[0].string.text);
+ }
+
+ /*
+ * User needs to set the full URI...
+ */
+
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("choose-uri.tmpl");
+ cgiEndHTML();
+ }
+ else if (!strncmp(var, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
+ {
+ /*
+ * Need baud rate, parity, etc.
+ */
+
+ if ((var = strchr(var, '?')) != NULL &&
+ strncmp(var, "?baud=", 6) == 0)
+ maxrate = atoi(var + 6);
+ else
+ maxrate = 19200;
+
+ for (i = 0; i < 10; i ++)
+ if (baudrates[i] > maxrate)
+ break;
+ else
+ {
+ sprintf(baudrate, "%d", baudrates[i]);
+ cgiSetArray("BAUDRATES", i, baudrate);
+ }
+
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("choose-serial.tmpl");
+ cgiEndHTML();
+ }
+ else if (!file && (var = cgiGetVariable("PPD_NAME")) == NULL)
+ {
+ if (modify)
+ {
+ /*
+ * Get the PPD file...
+ */
+
+ int fd; /* PPD file */
+ char filename[1024]; /* PPD filename */
+ ppd_file_t *ppd; /* PPD information */
+ char buffer[1024]; /* Buffer */
+ int bytes; /* Number of bytes */
+ http_status_t get_status; /* Status of GET */
+
+
+ snprintf(uri, sizeof(uri), "/printers/%s.ppd", name);
+
+ if (httpGet(http, uri))
+ httpGet(http, uri);
+
+ while ((get_status = httpUpdate(http)) == HTTP_CONTINUE);
+
+ if (get_status != HTTP_OK)
+ {
+ fprintf(stderr, "ERROR: Unable to get PPD file %s: %d - %s\n",
+ uri, get_status, httpStatus(get_status));
+ }
+ else if ((fd = cupsTempFd(filename, sizeof(filename))) >= 0)
+ {
+ while ((bytes = httpRead(http, buffer, sizeof(buffer))) > 0)
+ write(fd, buffer, bytes);
+
+ close(fd);
+
+ if ((ppd = ppdOpenFile(filename)) != NULL)
+ {
+ if (ppd->manufacturer)
+ cgiSetVariable("CURRENT_MAKE", ppd->manufacturer);
+
+ if (ppd->nickname)
+ cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd->nickname);
+
+ ppdClose(ppd);
+ unlink(filename);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to open PPD file %s: %s\n",
+ filename, ppdErrorString(ppdLastError(&bytes)));
+ }
+ }
+ else
+ {
+ httpFlush(http);
+
+ fprintf(stderr,
+ "ERROR: Unable to create temporary file for PPD file: %s\n",
+ strerror(errno));
+ }
+ }
+ else if ((uriptr = strrchr(cgiGetVariable("DEVICE_URI"), '|')) != NULL)
+ {
+ /*
+ * Extract make and make/model from device URI string...
+ */
+
+ char make[1024], /* Make string */
+ *makeptr; /* Pointer into make */
+
+
+ *uriptr++ = '\0';
+
+ strlcpy(make, uriptr, sizeof(make));
+
+ if ((makeptr = strchr(make, ' ')) != NULL)
+ *makeptr = '\0';
+ else if ((makeptr = strchr(make, '-')) != NULL)
+ *makeptr = '\0';
+ else if (!strncasecmp(make, "laserjet", 8) ||
+ !strncasecmp(make, "deskjet", 7) ||
+ !strncasecmp(make, "designjet", 9))
+ strcpy(make, "HP");
+ else if (!strncasecmp(make, "phaser", 6))
+ strcpy(make, "Xerox");
+ else if (!strncasecmp(make, "stylus", 6))
+ strcpy(make, "Epson");
+ else
+ strcpy(make, "Generic");
+
+ cgiSetVariable("CURRENT_MAKE", make);
+ cgiSetVariable("PPD_MAKE", make);
+ cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr);
+ }
+
+ /*
+ * Build a CUPS_GET_PPDS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PPDS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, "ipp://localhost/printers/");
+
+
+ if ((var = cgiGetVariable("PPD_MAKE")) != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
+ "ppd-make", NULL, var);
+ else
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", NULL, "ppd-make");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ /*
+ * Got the list of PPDs, see if the user has selected a make...
+ */
+
+ cgiSetIPPVars(response, NULL, NULL, NULL, 0);
+
+ if (var == NULL)
+ {
+ /*
+ * Let the user choose a make...
+ */
+
+ for (element = 0, attr = response->attrs, last = NULL;
+ attr != NULL;
+ attr = attr->next)
+ if (attr->name && strcmp(attr->name, "ppd-make") == 0)
+ if (last == NULL ||
+ strcasecmp(last->values[0].string.text,
+ attr->values[0].string.text) != 0)
+ {
+ cgiSetArray("PPD_MAKE", element, attr->values[0].string.text);
+ element ++;
+ last = attr;
+ }
+
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("choose-make.tmpl");
+ cgiEndHTML();
+ }
+ else
+ {
+ /*
+ * Let the user choose a model...
+ */
+
+ const char *make_model; /* Current make/model string */
+
+
+ if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL)
+ {
+ /*
+ * Scan for "close" matches...
+ */
+
+ int match, /* Current match */
+ best_match, /* Best match so far */
+ count; /* Number of drivers */
+ const char *best, /* Best matching string */
+ *current; /* Current string */
+
+
+ count = cgiGetSize("PPD_MAKE_AND_MODEL");
+
+ for (i = 0, best_match = 0, best = NULL; i < count; i ++)
+ {
+ current = cgiGetArray("PPD_MAKE_AND_MODEL", i);
+ match = match_string(make_model, current);
+
+ if (match > best_match)
+ {
+ best_match = match;
+ best = current;
+ }
+ }
+
+ if (best_match > strlen(var))
+ {
+ /*
+ * Found a match longer than the make...
+ */
+
+ cgiSetVariable("CURRENT_MAKE_AND_MODEL", best);
+ }
+ }
+
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("choose-model.tmpl");
+ cgiEndHTML();
+ }
+
+
+ ippDelete(response);
+ }
+ else
+ {
+ char message[1024];
+
+
+ snprintf(message, sizeof(message), "Unable to get list of printer drivers: %s",
+ ippErrorString(cupsLastError()));
+ cgiSetVariable("ERROR", message);
+ cgiStartHTML(title);
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ }
+ }
+ else
+ {
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * printer-location
+ * printer-info
+ * ppd-name
+ * device-uri
+ * printer-is-accepting-jobs
+ * printer-state
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/printers/%s", cgiGetVariable("PRINTER_NAME"));
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
+ NULL, cgiGetVariable("PRINTER_LOCATION"));
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
+ NULL, cgiGetVariable("PRINTER_INFO"));
+
+ if (!file)
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "ppd-name",
+ NULL, cgiGetVariable("PPD_NAME"));
+
+ strlcpy(uri, cgiGetVariable("DEVICE_URI"), sizeof(uri));
+
+ /*
+ * Strip make and model from URI...
+ */
+
+ if ((uriptr = strrchr(uri, '|')) != NULL)
+ *uriptr = '\0';
+
+ if (strncmp(uri, "serial:", 7) == 0)
+ {
+ /*
+ * Update serial port URI to include baud rate, etc.
+ */
+
+ if ((uriptr = strchr(uri, '?')) == NULL)
+ uriptr = uri + strlen(uri);
+
+ snprintf(uriptr, sizeof(uri) - (uriptr - uri),
+ "?baud=%s+bits=%s+parity=%s+flow=%s",
+ cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
+ cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
+ }
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri",
+ NULL, uri);
+
+ ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
+
+ ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+ IPP_PRINTER_IDLE);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if (file)
+ response = cupsDoFileRequest(http, request, "/admin/", file->tempfile);
+ else
+ response = cupsDoRequest(http, request, "/admin/");
+
+ if (response)
+ {
+ status = response->request.status.status_code;
+ ippDelete(response);
+ }
+ else
+ status = cupsLastError();
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiStartHTML(title);
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang("error.tmpl");
+ }
+ else
+ {
+ /*
+ * Redirect successful updates back to the printer or set-options pages...
+ */
+
+ char refresh[1024]; /* Refresh URL */
+
+
+ cgiFormEncode(uri, name, sizeof(uri));
+
+ if (modify)
+ snprintf(refresh, sizeof(refresh),
+ "5;/admin?OP=redirect&URL=/printers/%s", uri);
+ else
+ snprintf(refresh, sizeof(refresh),
+ "5;/admin?OP=set-printer-options&PRINTER_NAME=%s", uri);
+
+ cgiSetVariable("refresh_page", refresh);
+
+ cgiStartHTML(title);
+
+ if (modify)
+ cgiCopyTemplateLang("printer-modified.tmpl");
+ else
+ cgiCopyTemplateLang("printer-added.tmpl");
+ }
+
+ cgiEndHTML();
+ }
+
+ if (oldinfo)
+ ippDelete(oldinfo);
+}
+
+
+/*
+ * 'do_config_printer()' - Configure the default options for a printer.
+ */
+
+static void
+do_config_printer(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language)/* I - Client's language */
+{
+ int i, j, k, m; /* Looping vars */
+ int have_options; /* Have options? */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* IPP attribute */
+ char uri[HTTP_MAX_URI]; /* Job URI */
+ const char *var; /* Variable value */
+ const char *printer; /* Printer printer name */
+ ipp_status_t status; /* Operation status... */
+ const char *filename; /* PPD filename */
+ char tempfile[1024]; /* Temporary filename */
+ cups_file_t *in, /* Input file */
+ *out; /* Output file */
+ char line[1024]; /* Line from PPD file */
+ char keyword[1024], /* Keyword from Default line */
+ *keyptr; /* Pointer into keyword... */
+ ppd_file_t *ppd; /* PPD file */
+ ppd_group_t *group; /* Option group */
+ ppd_option_t *option; /* Option */
+ ppd_attr_t *protocol; /* cupsProtocol attribute */
+
+
+ /*
+ * Get the printer name...
+ */
+
+ if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/printers/%s", printer);
+ else
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND));
+ cgiStartHTML("Set Printer Options");
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ return;
+ }
+
+ /*
+ * Get the PPD file...
+ */
+
+ if ((filename = cupsGetPPD(printer)) == NULL)
+ {
+ if (cupsLastError() == IPP_NOT_FOUND)
+ {
+ /*
+ * No PPD file for this printer, so we can't configure it!
+ */
+
+ cgiSetVariable("ERROR", ippErrorString(IPP_NOT_POSSIBLE));
+ cgiStartHTML("Set Printer Options");
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ }
+ else
+ {
+ /*
+ * Unable to access the PPD file for some reason...
+ */
+
+ cgiSetVariable("ERROR", ippErrorString(cupsLastError()));
+ cgiStartHTML("Set Printer Options");
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ }
+ return;
+ }
+
+ if ((ppd = ppdOpenFile(filename)) == NULL)
+ {
+ cgiSetVariable("ERROR", ippErrorString(IPP_DEVICE_ERROR));
+ cgiStartHTML("Set Printer Options");
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+ return;
+ }
+
+ if (cgiGetVariable("job_sheets_start") != NULL ||
+ cgiGetVariable("job_sheets_end") != NULL)
+ have_options = 1;
+ else
+ have_options = 0;
+
+ ppdMarkDefaults(ppd);
+
+ DEBUG_printf(("ppd->num_groups = %d\n"
+ "
\n", ppd->num_groups));
+
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ {
+ DEBUG_printf(("- %s
\n", group->text));
+
+ for (j = group->num_options, option = group->options; j > 0; j --, option ++)
+ if ((var = cgiGetVariable(option->keyword)) != NULL)
+ {
+ DEBUG_printf(("- %s = \"%s\"
\n", option->keyword, var));
+ have_options = 1;
+ ppdMarkOption(ppd, option->keyword, var);
+ }
+#ifdef DEBUG
+ else
+ printf("- %s not defined!
\n", option->keyword);
+#endif /* DEBUG */
+
+ DEBUG_puts("
");
+ }
+
+ DEBUG_printf(("
\n"
+ "ppdConflicts(ppd) = %d\n", ppdConflicts(ppd)));
+
+ if (!have_options || ppdConflicts(ppd))
+ {
+ /*
+ * Show the options to the user...
+ */
+
+ cgiStartHTML("Set Printer Options");
+ cgiCopyTemplateLang("set-printer-options-header.tmpl");
+
+ if (ppdConflicts(ppd))
+ {
+ for (i = ppd->num_groups, k = 0, group = ppd->groups; i > 0; i --, group ++)
+ for (j = group->num_options, option = group->options; j > 0; j --, option ++)
+ if (option->conflicted)
+ {
+ cgiSetArray("ckeyword", k, option->keyword);
+ cgiSetArray("ckeytext", k, option->text);
+ k ++;
+ }
+
+ cgiCopyTemplateLang("option-conflict.tmpl");
+ }
+
+ for (i = ppd->num_groups, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ {
+ if (!strcmp(group->name, "InstallableOptions"))
+ cgiSetVariable("GROUP",
+ _cupsLangString(language, _("Options Installed")));
+ else
+ cgiSetVariable("GROUP", group->text);
+
+ cgiCopyTemplateLang("option-header.tmpl");
+
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ {
+ if (!strcmp(option->keyword, "PageRegion"))
+ continue;
+
+ cgiSetVariable("KEYWORD", option->keyword);
+ cgiSetVariable("KEYTEXT", option->text);
+
+ if (option->conflicted)
+ cgiSetVariable("CONFLICTED", "1");
+ else
+ cgiSetVariable("CONFLICTED", "0");
+
+ cgiSetSize("CHOICES", 0);
+ cgiSetSize("TEXT", 0);
+ for (k = 0, m = 0; k < option->num_choices; k ++)
+ {
+ /*
+ * Hide custom option values...
+ */
+
+ if (!strcmp(option->choices[k].choice, "Custom"))
+ continue;
+
+ cgiSetArray("CHOICES", m, option->choices[k].choice);
+ cgiSetArray("TEXT", m, option->choices[k].text);
+
+ m ++;
+
+ if (option->choices[k].marked)
+ cgiSetVariable("DEFCHOICE", option->choices[k].choice);
+ }
+
+ switch (option->ui)
+ {
+ case PPD_UI_BOOLEAN :
+ cgiCopyTemplateLang("option-boolean.tmpl");
+ break;
+ case PPD_UI_PICKONE :
+ cgiCopyTemplateLang("option-pickone.tmpl");
+ break;
+ case PPD_UI_PICKMANY :
+ cgiCopyTemplateLang("option-pickmany.tmpl");
+ break;
+ }
+ }
+
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/printers/%s", printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) != NULL)
+ {
+ if ((attr = ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO)) != NULL)
+ {
+ /*
+ * Add the job sheets options...
+ */
+
+ cgiSetVariable("GROUP", "Banners");
+ cgiCopyTemplateLang("option-header.tmpl");
+
+ cgiSetSize("CHOICES", attr->num_values);
+ cgiSetSize("TEXT", attr->num_values);
+ for (k = 0; k < attr->num_values; k ++)
+ {
+ cgiSetArray("CHOICES", k, attr->values[k].string.text);
+ cgiSetArray("TEXT", k, attr->values[k].string.text);
+ }
+
+ attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
+
+ cgiSetVariable("KEYWORD", "job_sheets_start");
+ cgiSetVariable("KEYTEXT", "Starting Banner");
+ cgiSetVariable("DEFCHOICE", attr == NULL ?
+ "" : attr->values[0].string.text);
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+
+ cgiSetVariable("KEYWORD", "job_sheets_end");
+ cgiSetVariable("KEYTEXT", "Ending Banner");
+ cgiSetVariable("DEFCHOICE", attr == NULL && attr->num_values > 1 ?
+ "" : attr->values[1].string.text);
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+
+ if (ippFindAttribute(response, "printer-error-policy-supported",
+ IPP_TAG_ZERO) ||
+ ippFindAttribute(response, "printer-op-policy-supported",
+ IPP_TAG_ZERO))
+ {
+ /*
+ * Add the error and operation policy options...
+ */
+
+ cgiSetVariable("GROUP", "Policies");
+ cgiCopyTemplateLang("option-header.tmpl");
+
+ /*
+ * Error policy...
+ */
+
+ attr = ippFindAttribute(response, "printer-error-policy-supported",
+ IPP_TAG_ZERO);
+
+ if (attr)
+ {
+ cgiSetSize("CHOICES", attr->num_values);
+ cgiSetSize("TEXT", attr->num_values);
+ for (k = 0; k < attr->num_values; k ++)
+ {
+ cgiSetArray("CHOICES", k, attr->values[k].string.text);
+ cgiSetArray("TEXT", k, attr->values[k].string.text);
+ }
+
+ attr = ippFindAttribute(response, "printer-error-policy",
+ IPP_TAG_ZERO);
+
+ cgiSetVariable("KEYWORD", "printer_error_policy");
+ cgiSetVariable("KEYTEXT", "Error Policy");
+ cgiSetVariable("DEFCHOICE", attr == NULL ?
+ "" : attr->values[0].string.text);
+ }
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+
+ /*
+ * Operation policy...
+ */
+
+ attr = ippFindAttribute(response, "printer-op-policy-supported",
+ IPP_TAG_ZERO);
+
+ if (attr)
+ {
+ cgiSetSize("CHOICES", attr->num_values);
+ cgiSetSize("TEXT", attr->num_values);
+ for (k = 0; k < attr->num_values; k ++)
+ {
+ cgiSetArray("CHOICES", k, attr->values[k].string.text);
+ cgiSetArray("TEXT", k, attr->values[k].string.text);
+ }
+
+ attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
+
+ cgiSetVariable("KEYWORD", "printer_op_policy");
+ cgiSetVariable("KEYTEXT", "Operation Policy");
+ cgiSetVariable("DEFCHOICE", attr == NULL ?
+ "" : attr->values[0].string.text);
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+ }
+
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+
+ ippDelete(response);
+ }
+
+ /*
+ * Binary protocol support...
+ */
+
+ if (ppd->protocols && strstr(ppd->protocols, "BCP"))
+ {
+ protocol = ppdFindAttr(ppd, "cupsProtocol", NULL);
+
+ cgiSetVariable("GROUP", "PS Binary Protocol");
+ cgiCopyTemplateLang("option-header.tmpl");
+
+ cgiSetSize("CHOICES", 2);
+ cgiSetSize("TEXT", 2);
+ cgiSetArray("CHOICES", 0, "None");
+ cgiSetArray("TEXT", 0, "None");
+
+ if (strstr(ppd->protocols, "TBCP"))
+ {
+ cgiSetArray("CHOICES", 1, "TBCP");
+ cgiSetArray("TEXT", 1, "TBCP");
+ }
+ else
+ {
+ cgiSetArray("CHOICES", 1, "BCP");
+ cgiSetArray("TEXT", 1, "BCP");
+ }
+
+ cgiSetVariable("KEYWORD", "protocol");
+ cgiSetVariable("KEYTEXT", "PS Binary Protocol");
+ cgiSetVariable("DEFCHOICE", protocol ? protocol->value : "None");
+
+ cgiCopyTemplateLang("option-pickone.tmpl");
+
+ cgiCopyTemplateLang("option-trailer.tmpl");
+ }
+
+ cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
+ cgiEndHTML();
+ }
+ else
+ {
+ /*
+ * Set default options...
+ */
+
+ out = cupsTempFile2(tempfile, sizeof(tempfile));
+ in = cupsFileOpen(filename, "r");
+
+ if (!in || !out)
+ {
+ cgiSetVariable("ERROR", strerror(errno));
+ cgiStartHTML("Set Printer Options");
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+
+ if (in)
+ cupsFileClose(in);
+
+ if (out)
+ {
+ cupsFileClose(out);
+ unlink(tempfile);
+ }
+
+ unlink(filename);
+ return;
+ }
+
+ while (cupsFileGets(in, line, sizeof(line)))
+ {
+ if (!strncmp(line, "*cupsProtocol:", 14) && cgiGetVariable("protocol"))
+ continue;
+ else if (strncmp(line, "*Default", 8))
+ cupsFilePrintf(out, "%s\n", line);
+ else
+ {
+ /*
+ * Get default option name...
+ */
+
+ strlcpy(keyword, line + 8, sizeof(keyword));
+
+ for (keyptr = keyword; *keyptr; keyptr ++)
+ if (*keyptr == ':' || isspace(*keyptr & 255))
+ break;
+
+ *keyptr = '\0';
+
+ if (!strcmp(keyword, "PageRegion"))
+ var = cgiGetVariable("PageSize");
+ else
+ var = cgiGetVariable(keyword);
+
+ if (var != NULL)
+ cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
+ else
+ cupsFilePrintf(out, "%s\n", line);
+ }
+ }
+
+ if ((var = cgiGetVariable("protocol")) != NULL)
+ cupsFilePrintf(out, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
+
+ cupsFileClose(in);
+ cupsFileClose(out);
+
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * job-sheets-default
+ * [ppd file]
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, "localhost", 0,
+ "/printers/%s", cgiGetVariable("PRINTER_NAME"));
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ "job-sheets-default", 2, NULL, NULL);
+ attr->values[0].string.text = strdup(cgiGetVariable("job_sheets_start"));
+ attr->values[1].string.text = strdup(cgiGetVariable("job_sheets_end"));
+
+ if ((var = cgiGetVariable("printer_error_policy")) != NULL)
+ attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ "printer-error-policy", NULL, var);
+
+ if ((var = cgiGetVariable("printer_op_policy")) != NULL)
+ attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ "printer-op-policy", NULL, var);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoFileRequest(http, request, "/admin/", tempfile)) != NULL)
+ {
+ status = response->request.status.status_code;
+ ippDelete(response);
+ }
+ else
+ status = cupsLastError();
+
+ if (status > IPP_OK_CONFLICT)
+ {
+ cgiStartHTML("Set Printer Options");
+ cgiSetVariable("ERROR", ippErrorString(status));
+ cgiCopyTemplateLang("error.tmpl");
+ }
+ else
+ {
+ /*
+ * Redirect successful updates back to the printer page...
+ */
+
+ char refresh[1024]; /* Refresh URL */
+
+ cgiFormEncode(uri, printer, sizeof(uri));
+ snprintf(refresh, sizeof(refresh), "5;/admin?OP=redirect&URL=/printers/%s",
+ uri);
+ cgiSetVariable("refresh_page", refresh);
+
+ cgiStartHTML("Set Printer Options");
+
+ cgiCopyTemplateLang("printer-configured.tmpl");
+ }
+
+ cgiEndHTML();
+
+ unlink(tempfile);
+ }
+
+ unlink(filename);
+}
+
+
+/*
+ * 'do_config_server()' - Configure server settings.
+ */
+
+static void
+do_config_server(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language) /* I - Client's language */
+{
+ if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF"))
+ {
+ /*
+ * Save basic setting changes...
+ */
+
+ http_status_t status; /* PUT status */
+ cups_file_t *cupsd; /* cupsd.conf file */
+ char tempfile[1024]; /* Temporary new cupsd.conf */
+ int tempfd; /* Temporary file descriptor */
+ cups_file_t *temp; /* Temporary file */
+ char line[1024], /* Line from cupsd.conf file */
+ *value; /* Value on line */
+ const char *server_root; /* Location of config files */
+ int linenum, /* Line number in file */
+ in_policy, /* In a policy section? */
+ in_cancel_job, /* In a cancel-job section? */
+ in_admin_location, /* In the /admin location? */
+ in_conf_location, /* In the /admin/conf location? */
+ in_root_location; /* In the / location? */
+ int remote_printers, /* Show remote printers */
+ share_printers, /* Share local printers */
+ remote_admin, /* Remote administration allowed? */
+ user_cancel_any, /* Cancel-job policy set? */
+ debug_logging; /* LogLevel debug set? */
+ int wrote_port_listen, /* Wrote the port/listen lines? */
+ wrote_browsing, /* Wrote the browsing lines? */
+ wrote_policy, /* Wrote the policy? */
+ wrote_loglevel, /* Wrote the LogLevel line? */
+ wrote_admin_location, /* Wrote the /admin location? */
+ wrote_conf_location, /* Wrote the /admin/conf location? */
+ wrote_root_location; /* Wrote the / location? */
+ int indent; /* Indentation */
+
+
+ /*
+ * Get form variables...
+ */
+
+ remote_printers = cgiGetVariable("REMOTE_PRINTERS") != NULL;
+ share_printers = cgiGetVariable("SHARE_PRINTERS") != NULL;
+ remote_admin = cgiGetVariable("REMOTE_ADMIN") != NULL;
+ user_cancel_any = cgiGetVariable("USER_CANCEL_ANY") != NULL;
+ debug_logging = cgiGetVariable("DEBUG_LOGGING") != NULL;
+
+ /*
+ * Locate the cupsd.conf file...
+ */
+
+ if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
+ server_root = CUPS_SERVERROOT;
+
+ snprintf(line, sizeof(line), "%s/cupsd.conf", server_root);
+
+ /*
+ * Open the cupsd.conf file...
+ */
+
+ if ((cupsd = cupsFileOpen(line, "r")) == NULL)
+ {
+ /*
+ * Unable to open - log an error...
+ */
+
+ cgiStartHTML("Change Settings");
+ cgiSetVariable("ERROR", strerror(errno));
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+
+ perror(line);
+ return;
+ }
+
+ /*
+ * Create a temporary file for the new cupsd.conf file...
+ */
+
+ if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
+ {
+ cgiStartHTML("Change Settings");
+ cgiSetVariable("ERROR", strerror(errno));
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+
+ perror(tempfile);
+ cupsFileClose(cupsd);
+ return;
+ }
+
+ if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL)
+ {
+ cgiStartHTML("Change Settings");
+ cgiSetVariable("ERROR", strerror(errno));
+ cgiCopyTemplateLang("error.tmpl");
+ cgiEndHTML();
+
+ perror(tempfile);
+ close(tempfd);
+ unlink(tempfile);
+ cupsFileClose(cupsd);
+ return;
+ }
+
+ /*
+ * Copy the old file to the new, making changes along the way...
+ */
+
+ in_admin_location = 0;
+ in_cancel_job = 0;
+ in_conf_location = 0;
+ in_policy = 0;
+ in_root_location = 0;
+ linenum = 0;
+ wrote_admin_location = 0;
+ wrote_browsing = 0;
+ wrote_conf_location = 0;
+ wrote_loglevel = 0;
+ wrote_policy = 0;
+ wrote_port_listen = 0;
+ wrote_root_location = 0;
+ indent = 0;
+
+ while (cupsFileGetConf(cupsd, line, sizeof(line), &value, &linenum))
+ {
+ if (!strcasecmp(line, "Port") || !strcasecmp(line, "Listen"))
+ {
+ if (!wrote_port_listen)
+ {
+ wrote_port_listen = 1;
+
+ if (share_printers || remote_admin)
+ {
+ cupsFilePuts(temp, "# Allow remote access\n");
+ cupsFilePrintf(temp, "Listen *:%d\n", ippPort());
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Only listen for connections from the local machine.\n");
+ cupsFilePrintf(temp, "Listen localhost:%d\n", ippPort());
+ }
+
+#ifdef CUPS_DEFAULT_DOMAINSOCKET
+ cupsFilePuts(temp, "Listen " CUPS_DEFAULT_DOMAINSOCKET "\n");
+#endif /* CUPS_DEFAULT_DOMAINSOCKET */
+ }
+ }
+ else if (!strcasecmp(line, "Browsing") ||
+ !strcasecmp(line, "BrowseAddress") ||
+ !strcasecmp(line, "BrowseAllow") ||
+ !strcasecmp(line, "BrowseDeny") ||
+ !strcasecmp(line, "BrowseOrder"))
+ {
+ if (!wrote_browsing)
+ {
+ wrote_browsing = 1;
+
+ if (remote_printers || share_printers)
+ {
+ if (remote_printers && share_printers)
+ cupsFilePuts(temp, "# Enable printer sharing and shared printers.\n");
+ else if (remote_printers)
+ cupsFilePuts(temp, "# Show shared printers on the local network.\n");
+ else
+ cupsFilePuts(temp, "# Share local printers on the local network.\n");
+
+ cupsFilePuts(temp, "Browsing On\n");
+ cupsFilePuts(temp, "BrowseOrder allow,deny\n");
+
+ if (remote_printers)
+ cupsFilePuts(temp, "BrowseAllow @LOCAL\n");
+
+ if (share_printers)
+ cupsFilePuts(temp, "BrowseAddress @LOCAL\n");
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Disable printer sharing and shared printers.\n");
+ cupsFilePuts(temp, "Browsing Off\n");
+ }
+ }
+ }
+ else if (!strcasecmp(line, "LogLevel"))
+ {
+ wrote_loglevel = 1;
+
+ if (debug_logging)
+ {
+ cupsFilePuts(temp, "# Show troubleshooting information in error_log.\n");
+ cupsFilePuts(temp, "LogLevel debug\n");
+ }
+ else
+ {
+ cupsFilePuts(temp, "# Show general information in error_log.\n");
+ cupsFilePuts(temp, "LogLevel info\n");
+ }
+ }
+ else if (!strcasecmp(line, "\n", line, value);
+ indent += 2;
+ }
+ else if (!strcasecmp(line, ""))
+ {
+ indent -= 2;
+ if (!wrote_policy)
+ {
+ wrote_policy = 1;
+
+ if (!user_cancel_any)
+ cupsFilePuts(temp, " # Only the owner or an administrator can cancel a job...\n"
+ " \n"
+ " Order deny,allow\n"
+ " Allow @SYSTEM\n"
+ " Allow @OWNER\n"
+ " \n");
+ }
+
+ in_policy = 0;
+
+ cupsFilePuts(temp, "\n");
+ }
+ else if (!strcasecmp(line, "\n", line, value);
+ }
+ else if (!strcasecmp(line, ""))
+ {
+ indent -= 2;
+ if (in_admin_location)
+ {
+ wrote_admin_location = 1;
+
+ if (remote_admin)
+ cupsFilePuts(temp, " # Allow remote administration...\n");
+ else
+ cupsFilePuts(temp, " # Restrict access to the admin pages...\n");
+
+ cupsFilePuts(temp, " Order allow,deny\n");
+
+ if (remote_admin)
+ cupsFilePuts(temp, " Allow @LOCAL\n");
+ else
+ cupsFilePuts(temp, " Allow localhost\n");
+ }
+ else if (in_conf_location)
+ {
+ wrote_conf_location = 1;
+
+ if (remote_admin)
+ cupsFilePuts(temp, " # Allow remote access to the configuration files...\n");
+ else
+ cupsFilePuts(temp, " # Restrict access to the configuration files...\n");
+
+ cupsFilePuts(temp, " Order allow,deny\n");
+
+ if (remote_admin)
+ cupsFilePuts(temp, " Allow @LOCAL\n");
+ else
+ cupsFilePuts(temp, " Allow localhost\n");
+ }
+ else if (in_root_location)
+ {
+ wrote_root_location = 1;
+
+ if (remote_admin && share_printers)
+ cupsFilePuts(temp, " # Allow shared printing and remote administration...\n");
+ else if (remote_admin)
+ cupsFilePuts(temp, " # Allow remote administration...\n");
+ else if (share_printers)
+ cupsFilePuts(temp, " # Allow shared printing...\n");
+ else
+ cupsFilePuts(temp, " # Restrict access to the server...\n");
+
+ cupsFilePuts(temp, " Order allow,deny\n");
+
+ if (remote_admin || share_printers)
+ cupsFilePuts(temp, " Allow @LOCAL\n");
+ else
+ cupsFilePuts(temp, " Allow localhost\n");
+ }
+
+ in_admin_location = 0;
+ in_conf_location = 0;
+ in_root_location = 0;
+
+ cupsFilePuts(temp, "
\n");
+ }
+ else if (!strcasecmp(line, "\n");
+ }
+ }
+ else if (!strcasecmp(line, "") && in_cancel_job)
+ {
+ indent -= 2;
+
+ if (in_cancel_job == 1)
+ cupsFilePuts(temp, "