lprng-doc-3.8.Arc2/0000755000175000017500000000000010746255777011064 500000000000000lprng-doc-3.8.Arc2/license.txt0000644000175000017500000006117010523404333013145 00000000000000 *LPRng, IFHP, and LPRngTool LICENSE* GNU GPL and Artistic License (Version 5, 28 Aug 2003) Copyright Patrick Powell, Astart Technologies All rights reserved. You may use "LPRng" or "IFHP" under either the terms of the GNU GPL License or the Artistc License. These licenses are included below. The licenses were obtained from the http://www.opensource.org web site on 28 Aug 2003. These Licenses apply to the computer software packages known as "LPRng", "IFHP", and associated files. The "Package" or "Program" below refers to the programs, files, and associated software which are distributed as the package. The "LPRng" Software Package is a copyrighted work whose copyright is held by Patrick Powell. The "IFHP" Software Package is a copyrighted work whose copyright is held by Patrick Powell. The "LPRngTool" Software Package is a copyrighted work whose copyright is held by Patrick Powell. 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. NOTHING OTHER THAN THIS LICENSE GRANTS YOU PERMISSION TO MODIFY OR DISTRIBUTE THE PROGRAM OR ITS DERIVATIVE WORKS. THESE ACTIONS ARE PROHIBITED BY LAW. IF YOU DO NOT ACCEPT THESE TERMS AND CONDITIONS, DO NOT MODIFY OR DISTRIBUTE THE PROGRAM. ----------------------------------------------------------------------- Addendum Fri Jun 21 17:06:33 PDT 2002 If you wish to distribute the LPRng source code or binaries of any of the programs in the LPRng packages under terms of the GNU license, then when any package or portion of the LPRng is configured to use any facility or utility of the OpenSSL distribution, the additional following clause will be applied, as recommended in the OpenSSL 0.9.6c release FAQ: "This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed." ----------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, 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. -------------------------------------------------------------------------------------------- From http://www.opensource.org - The Artistic License Version as of 28 Aug, 2003 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. lprng-doc-3.8.Arc2/y2k.txt0000644000175000017500000000256210523400374012231 00000000000000 Year 2000 Compliance Patrick Powell Astart Technologies San Diego, CA 92123 Original Aug 30, 1998 Updated July 1, 1999 Statement: The LPRng software has no known Year 2000 data dependencies. Where necessary, dates are generated using values relative to the UNIX Epoch, and will be consistent to 2034 at a conservative estimate. However, since LPRng uses the RFC1179 standard for job and data file transfer, and other systems provide information in a form which may not be consistent, the LPRng author takes no reponsibility for other defective implementations and the effects of using these defective implementations with LPRng. Testing has been done on the following systems: 1. BSDI UNIX, Version 3.0/3.1 (May 1998) 2. FreeBSD 2.5 (July 1998) 3. Red Hat Linux 5.1 (July 1998) 4. Solaris 2.6 (Intel and Sparc Versions) (May 1998) There were no observed problems with date formats using the LPRng software in toto. The software was NOT tested with the native LPD or LP (system V) print spoolers. Note: logging and other operations generate textual timestamps in either a short (hh:mm) or extended (yyyy-mm-dd-hh:mm:ss) format. The choice of short of extended formats is controlled by a simple configuration option. Patrick Powell Sun Aug 30 17:02:02 PDT 1998 lprng-doc-3.8.Arc2/LPRng-Reference.sgml0000644000175000017500000276407210554776205014557 00000000000000 ]> LPRng Reference Manual 24 Sep 2004 (For LPRng-3.8.28) Patrick A Powell
papowell@lprng.com AStArt Technologies 6741 Convoy Court, San Diego, CA 92111 Phone 858-874-6543 Fax 858-751-2435
1996-2001 Patrick Powell $Id: LPRng-Reference.sgml,v 1.24 2004/09/24 20:20:03 papowell Exp $ THIS DOCUMENTATION AND THE DESCRIBED SOFTWARE IS PROVIDED BY THE AUTHORS "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 AUTHORS 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 DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The LPRng; Printing Software consists of the &LPRng; print spooler, the &ifhp; print filter, and the &LPRngTool; graphical user interface. The &LPRng; print spooler is an enhanced, extended, and portable implementation of the Berkeley The SVR4 lp and lpstat functionality is provided by a set of emulator programs, and &LPRng; can be easily integrated with the Samba SMB support package. For users that require secure and/or authenticated printing support, &LPRng; supports SSL (using The &ifhp; print filter converts print jobs into formats compatible with PostScript, PCL, text, and other printers and provides diagnostic and error information as well as accounting information. The ;LPRngTool& Graphical User Interface provides a simple to use configuration and monitoring tool. It allows users to monitor printers and generate printcap entries in a simple manner, as well as providing extensive help and diagnostics.
Preface Introduction The &LPRng; Print Spooler provides the essential printing services for UNIX and UNIX-like operating systems. It can be configured to work in small, large, or enterprise level environments. The &ifhp; Print Filter is used with &LPRng; to convert print jobs into a format compatible with a particular printer and the while it may briefly describe the &ifhp; operation, Finally, the &LPRngTool; Graphical User Interface provides an easy to use configuration and monitoring tool for the &LPRng; print spooler. This document is the basic reference for the &LPRng; print spooler software; the &ifhp; documentation and &LPRngTool; should be consulted for details about their operation. Acknowledgements I would like to thank all of the &LPRng; users who so relentlessly tried the incredible number of permutations and combinations of printers and software, and whose requests for just one more feature led to the development of the software. Shell Prompts The following table shows the default system prompt and superuser prompt. The examples will use this prompt to indicate which user you should be running the example as. User Prompt Normal user % root # Typographic Conventions The following table describes the typographic conventions used in this book. Meaning Examples The name of commands, files, and directories. On screen computer output. Edit your .login file.Use ls -a to list all files.You have mail. What you type, when contrasted with on-screen computer output. % su Password: Manual page references. Use su 1 to change user names. User and group names Only root can do this. Emphasis You must do this. Command line variables; replace with the real name or variable. To delete a file, type rm filename Environment variables $HOME is your home directory. Notes, warnings, and examples Within the text appear notes, warnings, and examples. Notes are represented like this, and contain information that you should take note of, as it may affect what you do. Warnings are represented like this, and contain information warning you about possible damage if you do not follow the instructions. This damage may be physical, to your hardware or to you, or it may be non-physical, such as the inadvertent deletion of important files. Examples are represented like this, and typically contain examples you should walk through, or show you what the results of a particular action should be. Introduction Printing is one of the essential services provided by computer systems. Users want reliable and easy to use methods of printing that require a minimum amount of effort to used and understand. On single user systems with a directly attached printer they perceive that the printing process is simply a matter of storing or spooling a file, and then transferring it to the printer in a timely manner. In the classical multi-user systems, each user expects to share a common printer with one or more users; the print spooling system provides arbitration and sharing of the printer among the various users. In a network based multi-user system, there may be one or more printers shared by multiple users on many different systems. The print spoolers will need to cooperate to provide print services to the users in a simple an predictable manner. What is &LPRng;? The &LPRng; print spooler software was developed to be robust, reliable, secure, scalable, and portable. It has been used since 1988 in extremely demanding academic printing environments such as University of Minnesota, MIT, and Rutgers, commercial companies such as Dow Jones and Abbot Pharmaceuticals, as well as being distributed with Linux, FreeBSD, and other systems. Each of these environments has a unique set of problems, demanding various configuration and administrative capabilities. For example, the simple single user system with a single or limited number of printers requires easy configuration and simple diagnostic procedures, while the network based printing system requires highly robust error logging, authentication, and failover support. &LPRng; provides a highly flexible configuration system that allows it to perform optimally in all of these environments. The &LPRng; software has three components: the &lpd; print spooler and the user client applications &LPRng; mimics many of the features of the vintage or legacy Berkeley (University of California - Berkeley) Line Printer (LPR) package found on Berkeley derivatives of the Unix operating system. &LPRng; will print a document with little or no knowledge of the content or special processing required to print the document on a stand-alone machine or in a distributed printing environment. New (as compared to Berkeley LPR) features include: lightweight lpr, lpc and lprm programs, dynamic redirection of print queues, automatic job holding, highly verbose diagnostics, load balancing queues; enhanced security (SUID not required in most environments), and easy configuration. &LPRng; started life at the University of Waterloo in 1986 as PLP (Public Line Printer), a replacement for the original BSD jmason@iona.ie) who started the &LPRng; mailing list. In 1992 while at San Diego State University Prof. Powell redesigned and reimplemented the PLP code and named the result &LPRng;. The goals of the &LPRng; project were to build a server system that was as close to user abuse proof as possible, that would provide services limited only by the inherent capacities of the support system, RFC1179 compliant, and with extensive debugging capabilities to allow quick and easy diagnostics of problems. In 1999 the code base for &LPRng; was again reorganized in order to provide a common method for running on non-UNIX platforms such as Microsoft Windows NT, Apple Rhapsody, and embedded systems. As a side effect of this work, many security problems that could develop were identified and steps taken to ensure that they were not present in &LPRng;. For example, &LPRng; clients such as lpr, lprm, lpc, and lpq can run as ordinary users programs, the lpd server can run as a non-root user once a network port has been opened, and all text formatting operations done by &LPRng; use a very restricted and highly secure version of the Additional Resources The main &LPRng; documentation is the LPRng Reference Manual, which is available in several formats. Information about &LPRng; and the latest release can be found on the &LPRng; web page http://www.lprng.com/LPRng.html The &ifhp; documentation is the IFHP-HOWTO, which is available in the &ifhp; distribution. Information about &ifhp; and the latest release can be found on the &LPRng; web page http://www.lprng.com/LPRng.html There is also a mailing list at lprng@lprng.com. To post to the list you must subscribe by sending send an email to lprng-request@lprng.com, with the message subject or body containing the word `subscribe' or `help'. Several presentations of &LPRng; and print spooling software have been made at the Large Installation System Administrator (LISA) conferences. The presentation at the LISA 98 conference is in the PowerPoint file LISA98.ppt in the &LPRng; distribution documentation. Frequently Asked Questions There are a list of Frequently Asked Questions that appear regularly on the &LPRng; mailing list. See The Most Frequently Asked Questions. License, Copyright, and Disclaimer The &LPRng; Print Spooler and the ifhp Print Filter software are distributed under the GNU Public License (GPL) and the Artistic License. Users can choose to redistribute or use the software under a license that is appropriate for their purpose. Other licenses and distribution agreements are available by contacting AStArt Technologies for information. THE MATERIAL IN THESE SOFTWARE PACKAGES AND DOCUMENTS IS PROVIDED WITHOUT FEE AND AS-IS WITH NO WARRANTY REGARDING FITNESS OF USE FOR ANY PURPOSE. THE AUTHOR AND ALL CONTRIBUTORS ARE NOT LIABLE FOR ANY DAMAGES, DIRECT OR INDIRECT, RESULTING FROM THE USE OF THE SOFTWARE OR ANY INFORMATION PROVIDED IN THIS DOCUMENT. Commercial Support AStArt Technologies provides commercial support and enhancements for the &LPRng; and other network software. AStArt provides network and system consulting services for UNIX and NT systems, as well as real time and network software. Web Site Web Page: http://www.lprng.com FTP Sites Main FTP Site: ftp://ftp.lprng.com/pub/LPRng (US) Mirrors: ftp://ftp.u-aizu.ac.jp/pub/net/lpr/LPRng (JA) ftp://ftp.cs.columbia.edu/pub/archives/pkg/LPRng (US) ftp://ftp.cise.ufl.edu/pub/mirrors/LPRng (US) ftp://ftp.cs.umn.edu/pub/LPRng (US) ftp://uiarchive.uiuc.edu/pub/ftp/ftp.lprng.com/pub/LPRng (US) ftp://ftp.sage-au.org.au/pub/printing/spooler/lprng/ (AU) ftp://mirror.aarnet.edu.au/pub/LPRng/ (AU/NZ) http://mirror.aarnet.edu.au/pub/LPRng/ (AU/NZ) ftp://sunsite.ualberta.ca/pub/Mirror/LPRng (CA) ftp://ftp.informatik.uni-hamburg.de/pub/os/unix/utils/LPRng (DE) ftp://ftp.uni-paderborn.de/pub/unix/printer/LPRng (DE) ftp://ftp.mono.org/pub/LPRng (UK) ftp://ftp.iona.com/pub/plp/LPRng (IE) ftp://uabgate.uab.ericsson.se/pub/unix/LPRng (SE) Mailing List To join the &LPRng; mailing list, please send mail to lprng-request@lprng.com with the word 'subscribe' in the BODY. The &LPRng; mailing list is archived on http://www.findmail.com/list/lprng PGP Public Key The &LPRng; distributions have an MD5 checksum calculated, which is then signed with a PGP public key. Here is the key for validating the checksums: Type Bits/KeyID Date User ID pub 1024/00D95C9D 1997/01/31 Patrick A. Powell \ <papowell@lprng.com> -----BEGIN PGP PUBLIC KEY BLOCK----- Version: 2.6.3i mQCNAzLygTQAAAEEANBW5fPYjN3wSAnP9xWOUc3CvsMUxjip0cN2sY5qrdoJyIhn qbAspBopR+tGQfyp5T7C21yfWRRnfXmoJ3FVtgToAsJUYmzoSFY08eDx+rmSqCLe rdJjX8aG8jVXpGipEo9U4QsUK+OKzx3/y/OaK4cizoWqKvy1l4lEzDsA2VydAAUT tCdQYXRyaWNrIEEuIFBvd2VsbCA8cGFwb3dlbGxAYXN0YXJ0LmNvbT6JAJUDBRA0 XonoiUTMOwDZXJ0BAQ2cBAC7zU9Fn3sC3x0USJ+3vjhg/qA+Gjb5Fi1dJd4solc4 vJvtf0UL/1/rGipbR+A0XHpHzJUMP9ZfJzKZjaK/d0ZBNlS3i+JnypypeQiAqo9t FV0OyUCwDfWybgAORuAa2V6UJnAhvj/7TpxMmCApolaIb4yFyKunHa8aBxN+17Ro rrQlUGF0cmljayBBLiBQb3dlbGwgPHBhcG93ZWxsQHNkc3UuZWR1PokAlQMFEDLy gTSJRMw7ANlcnQEBYBYD/0zTeoiDNnI+NjaIei6+6z6oakqO70qFVx0FG3aP3kRH WlDhdtFaAuaMRh+RItHfFfcHhw5K7jiJdgKiTgGfj5Vt3OdHYkeeh/sddqgf9YnS tpj0u5NfrotPTUw39n6YTgS5/aW0PQfO9dx7jVUcGeod1TGXTe9mIhDMwDJI4J14 =3Zbp -----END PGP PUBLIC KEY BLOCK----- References and Standards The following references and standards have been used in the development of the &LPRng; software. RFCs During the early development of the Internet developers did not want to go through the laborious process of developing formal standards and applying to a standards body such as the EIA, IEEE, or ISO. Instead, they called the standards documents they developed Requests for Comments. These soon became de facto standards, and with the formal acceptance of the TCP/IP protocol as a network standard, de jure as well. You can get copies of the RFCs from literally hundreds of network sites, including http://www.isi.edu, http://www.faqs.org/rfcs, NIS.NSF.NET, RFC.JVNC.NET, or FTP.ISI.EDU. The RFC1179 - Line Printer Daemon Protocol describes the protocol used to transfer jobs from client program to print server. See RFC1179 for more a discussion of this protocol and more details about the RFC. PostScript PostScript is one of the de facto standards for print jobs. The Adobe Corporation (http://www.adobe.com) provides an excellent set of references for the PostScript language. They have made many of these available for downloading from their web sites or have published them in book form. The PostScript Language Reference Manual contains a great deal of technical information about the PostScript Language, and is the language reference manual. The PostScript Language Tutorial and Cookbook is a very nice and easy to read introduction to PostScript programming, and has some very useful utilities. Combined with GhostScript and the gv display program you can very easily learn to write your own small PostScript programs, and more importantly, can learn to understand the contents of PostScript files. The PostScript Language Program Design is the companion to the PostScript Language Tutorial and Cookbook, and has more complex examples of PostScript programs. More importantly, it also introduces, although without explanation, the PostScript Document Structuring Conventions described in Appendix G of the The PostScript Language Reference Manual. This alone makes it useful. HP PCL 5 The Hewlett-Packard (HP) PCL Printer Language is the second de-facto standard for print jobs. Currently, Hewlett-Packard makes documentation for PCL available through their Developer Program. You will need to register and then search their site for the PCL 5 Printer Language Reference Manual. HP PJL The Hewlett-Packard (HP) Printer Job Language is used to control various features of HP printers. The Printer Job Language Reference Manual is also available from Hewlett-Packard (http://www.hp.com) through their Developer Program. PDF The Portable Document Format (pdf) was developed by Adobe to be a more useful method of distributing documentation for view by online systems and software. The Portable Document Format Reference Manual is available from Adobe (http://www.adobe.com). While pdf is not used directly as a print job language, it is one of the more common formats for files that need to be printed. It can be converted to PostScript by most pdf viewers such as GhostScript and Adobe Acrobat. Installation The basic components of the &LPRng; system are the executables and the database files. This section deals with generating and installing the executable files. Getting Source Code and Support Programs Obtain the latest or stable version of the &LPRng; source code from a &LPRng; FTP Site. Obtain the latest or stable version of the ifhp filter source code from a &LPRng; FTP Site. This filter is used to support PostScript, PCL, and text printers. Obtain the following GNU programs from one of the many GNU Software Mirror Sites and install them. See the directions in the GNU Zip distribution for details. GNU gzip Compression Utility Used to generate the compressed &LPRng; distribution. GNU tar Archive Utility GNU tar supports gzip compression and decompression and is used to generate the &LPRng; distribution. GNU make &LPRng; requires GNU make for configuration and installation. GNU gcc Compiler or ANSI C Compiler &LPRng; requires and ANSI C compiler. If you do not have an ANSI C compiler then please use the GNU gcc compiler. Solaris Sparc and X86 Binaries for GCC and Make can be obtained from http://sunfreeware.com/. While the following are not essential to &LPRng; they are used by the ifhp filter. file - File Identification Utility The Open Source ftp://ftp.astron.com/pub/file/ . or ftp://ftp.lprng.com/pub/LPRng/UNIXTOOLS/file/ . This is a greatly improved version of the original UNIX file utility and may be used by the ifhp filter to do file recognition. gs - GhostScript GhostScript can be obtained from http://www.cs.wisc.edu/~ghost/ or http://www.ghostscript.com. GhostScript is a PostScript interpreter that allows you to translate PostScript to various printer compatible formats such as PCL, as well as displaying the code on a terminal. You might also want to get the PDF extensions that allows GhostScript to read and print PDF files. gv - GhostView Of course you will want to get the gv program that uses GhostScript to display PostScript on an X terminal. It can be obtained from http://wwwthep.physik.uni-mainz.de/~plass/gv/ <envar>PATH</envar> Environment Variable and Utilities Make sure that directory where you you have installed the GNU tools is one of the first entries in the shell search PATH environment variable. For example, if you have installed the utilities in the (default) directory /usr/local/bin, this should be one of the first entries in the PATH. For example: PATH=/usr/local/bin:/bin:/sbin:/usr/bin If you are compiling the distribution on a Solaris system you will need to use the utilities in the /usr/ccs/bin directory. This directory must be in the PATH after the directory where the GNU utilities have been installed. For example: PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/ccs/bin Check to see that the GNU make utility and not the default OS make is being used by default. Use make -v to determine what version you are using: h4: {1} % make -v GNU Make version 3.78, by Richard Stallman and Roland McGrath. Copyright (C) 1988-1999 Network Mounted File System and Spool Directories It is strongly recommended that the print spool directories and essential printer configuration file files should The &LPRng; This warning extends to other network file systems as well. In addition, the configuration file such as /etc/printcap must be available or printing will not function. Care should be taken to ensure that these files are stable and available at all times. Daemon User and Daemon Group The lpd server is started at system initialization time and initially runs as ROOT (Effective UID 0). It performs all file and other operations with daemon Effective daemon Effective GID, and does a lpr operate with the effective user IDs of the user which started them. Most UNIX systems already have user daemon and group daemon, or a similar ones. If suitable user and group IDs are not present then the appropriate system administration tools should be used to create them. The configuration --with-userid=UID and --with-groupid=GID can be used to specify the user and group IDs. The user ID must not have login capability. Configuration The &LPRng; package consists of the following executables and configuration files: lpd - the lpr, lpq, lprm, lpc, and lpstat client programs for printing, status queries, job removal, server configuration, and System V lpstat emulation respectively. printcap print queue database file which is used by all the server and client programs lpd.conf &LPRng; configuration options which is used by all the server and client programs lpd.perms permission information which is used by the lpd server to control user actions. &LPRng; uses the configure script generated by the GNU Makefiles. These are used by GNU make to compile and install the &LPRng; software. The following Makefile variables and values are set by configure to specify the location of the &LPRng; software: Configure VariableDefault Value Expanded Default Value Override ${prefix}/usr/local--prefix=PATH ${exec_prefix}${prefix}/usr/local--execprefix= PATH ${bindir}${exec_prefix}/bin/usr/local/bin--bindir= PATH ${sbindir}${exec_prefix}/sbin/usr/local/sbin--sbindir= PATH ${libexecdir}${exec_prefix} /libexec/usr/local/libexec--libexecdir= PATH ${sysconfdir}${prefix}/etc/usr/local/etc--sysconfdir= PATH ${mandir}${prefix}/man/usr/local/man--mandir= PATH These are used to install the following files: Configure VariableFiles ${bindir}lpr, lprm, lpq, lpstat ${sbindir}lpc, checkpc, lpd ${libexecdir}/filterslpf, pclbanner, psbanner, lpbanner ${sysconfdir}lpd.conf, lpd.perms, printcap ${mandir}/man[1-9]man pages You can set explicit values for the paths by using the override --name=PATH. For example: ./configure --prefix=/usr --sysconfdir=/etc \ --mandir=/usr/share/man VariableValueFiles ${bindir}/usr/bin/usr/bin/{lpr,lprm,lpq,lpstat} ${sbindir}/usr/sbin/usr/sbin/{lpc,checkpc,lpd} ${libexecdir}/filters/usr/libexec/filters/usr/libexec/filters{lpf, pclbanner, psbanner, lpbanner} ${sysconfdir}/etc/etc/{lpd.conf,lpd.perms,printcap} ${mandir}/man[1-9]/usr/share/man/usr/share/man/man[1-9]/{man pages} In addition to these standard configure options the following options provided. --disable-setuid Install the executables without setuid ROOT permissions. Non-setuid clients and programs are inherently more secure than SETUID programs, and system administrators would be well advised to install them without SETUID root permissions. Please see Security Considerations for more details about this option. --enable-priv_ports Require connections to the lpd server to come from a privileged port (range 1-1023). By default &LPRng; will allow connections from any port. Please see Security Considerations for more details about this option. --disable-force_localhost The default &LPRng; configuration assumes that all printing will be done via a lpd print spooler running on the local host system. However, many larger sites prefer that all users do their printing via a few central servers, and do not run lpd servers on user systems. The --disable-force_localhost configuration sets the default value of the force_localhost value to false, by default allowing the &LPRng; clients to connect directly to lpd servers on remote hosts. --disable-require_configfiles By default, the lpr, lpq, lprm, and lpc clients require the lpd.conf and printcap files to be present on the localhost. The --disable-require_configfiles literal removes this requirement. --enable-kerberos Include support for Kerberos 5 authenticated transfers. --enable-mit_kerberos4 Include support for MIT Kerberos 4 authenticated transfers. --disable-kerberos_checks Disable checks for kerberos support libraries, etc. --with-lpddir=DIR lpd executable directory (default ${sbindir}). For historical configuration compatibility. --with-filterdir=DIR Filter directory (default ${libexecdir}/filters). For historical configuration compatibility. --with-lpd_conf_path=PATH Path of lpd.conf file. For historical configuration compatibility. --with-lpd_perms_path=PATH Path of lpd.perms file. For historical configuration compatibility. --with-printcap_path=PATH Path of printcap file. For historical configuration compatibility. --with-ld_library_path=PATHLIST Set the LD_LIBRARY_PATH environment variable of filters to this value. --with-filter_path=PATHLIST Set the PATH environment variable of filters to this value. --with-userid=NAME Run &LPRng; as this user, default daemon --with-groupid=NAME Run &LPRng; as this group, default daemon --with-lockfile=PATH The lockfile path. This will be expanded to PATH.server or PATH.port allowing multiple &LPRng; servers to run on a single host. --with-filterdir=PATH Location of the filters installed by &LPRng;. --with-done_jobs=N retain status of last N done jobs. --with-done_jobs_max_age=N remove status of done jobs older than N seconds. --with-chooser_routine=NAME name of chooser routine provided by user --with-order_routine=NAME name of order routine provided by user --with-user_objs=NAME object file with routines provided by user --with-user_include=NAME include file with templates for routines provided by user --disable-strip Do not strip the executables before installing. For debugging and diagnostic purposes. --with-unixsocketpath=PATHNAME the pathname of the UNIX socket (off or blank to disable) --disable-ssl disable ssl support --with-openssl=DIR root location for OpenSSL --with-openssl-inc OpenSSL include files --with-openssl-lib OpenSSL library files --with-ssl_ca_file=FILE ssl Certificate Authority CERT file (default ${sysconfdir}/lpd/ssl.ca/ca.crt) --with-ssl_ca_key=KEY ssl Certificate Authority private key file (default ${sysconfdir}/lpd/ssl.ca/ca.key) --with-ssl_certs_dir=DIR ssl Certificate Authority certs working directory (default ${sysconfdir}/lpd/ssl.certs/) --with-ssl_server_cert=FILE ssl server certificate file (default ${sysconfdir}/lpd/ssl.server/server.crt) --with-ssl_server_password_file=FILE ssl server private key in password file (default ${sysconfdir}/lpd/ssl.server/server.pwd) It is recommended that you use one of the following configurations: If you already have a print spooling system installed and want to install &LPRng; for testing purposes or as an alternative to the existing system and keep your existing print spooling system, use: ./configure use defaults for file locations and permissions: /usr/local/{bin,sbin,libexec/filters,man} requires lpd to run on the local host executables installed setuid ROOT If you have manual pages in /usr/share/man, your existing print spooling system has executables in /usr/bin and /usr/sbin, and you want to replace your existing print spooling system, use: ./configure --prefix=/usr --sysconfdir=/etc \ --mandir=/usr/share/man executables and files in /usr/{bin,sbin,libexec/filters} /usr/share/man/man[0-9] requires lpd to run on the local host everything installed setuid ROOT If you have manual pages in /usr/share/man and allow jobs (by default) to be sent directly to the server host (lightweight operation), use: ./configure --prefix=/usr --sysconfdir=/etc \ --mandir=/usr/share/man --disable-force_localhost executables and files in /usr/{bin,sbin,libexec/filters} /usr/share/man/man[0-9] does not require lpd to run on the local host everything installed setuid ROOT System and User Printcap, lpd.conf, and lpd.perms files The system printcap file contains the definitions of the print queues used by &LPRng;, and is located in the directory specified by the configuration ${sysconfdir} value. For a complete description of the printcap file see Printcap Database. If your system does not have an existing printcap file then a dummy file similar to the following is installed by default: # dummy printcap file lp:cm=Dummy Printcap Entry: :lp=/dev/null :sd=/var/spool/lpd/%P In addition to the system ${HOME}/.printcap which contains printcap entries as well. The the user The lpd.conf is located in the ${sysconfdir} directory and provides configuration settings for both the &LPRng; client and server programs. For a complete description of the lpd.conf file see Configuration File, Defaults and Overrides. During installation the ${sysconfdir}/lpd.conf.template is created with the default &LPRng; information and if there is not an existing ${sysconfdir}/lpd.conf file is copied to it. The lpd.perms is located in the ${sysconfdir} directory and is only by lpd to determine user permissions for printing activities. For a complete description of the lpd.perms file see Permissions and Authentication for details. During installation the lpd.perms.template file is installed in the ${sysconfdir}/lpd.perms.template and if there is not an existing lpd.perms file is copied to it. By default, the &LPRng; client programs lpr, lpq, lprm, and lprc will require a lpd.conf and printcap file to be installed on the local host. You can relax this requirement by setting using the --disable-require_configfile configuration option. You can also create empty files to satisfy the program requirements. Checking System Installation with <application>checkpc</application> The checkpc program is used to make sure that the spool directories and files used by &LPRng; have the correct permissions and are in place. By default, checkpc will check permissions and report if there are any problems. You should run this as root. For example: h4: {2} # checkpc Warning - No configuration file '/etc/lpd.conf' Warning - No lpd only printcap file found in '/etc/lpd_printcap' Warning - ** cannot open '/var/run/lpd.printer' - 'Permission denied' Warning - bad directory - /var/spool/lpd/lp Warning - Printer 'lp' spool dir '/var/spool/lpd/lp' needs fixing In the above example, checkpc has discovered that the lpd.conf file is missing. This is a serious problem and indicates that the software may be incorrectly installed. The The /var/run/lpd.printer is serious, as the The The checkpc -f (fix) option causes checkpc to take action to rectify errors. The checkpc -f -V (verbose) option causes the fixup activity to be displayed a well: h4: {3} # checkpc -f -V Checking for configuration files '/etc/lpd.conf' found '/usr/local/etc/lpd.conf', mod 0100644 Checking for printcap files '/etc/printcap' found '/usr/local/etc/printcap', mod 0100644 Checking for lpd only printcap files '/etc/lpd_printcap' DaemonUID 1, DaemonGID 1 Using Config file '/etc/lpd.conf' LPD lockfile '/var/run/lpd.printer' Checking directory: '/var/run' directory '/var' directory '/var/run' checking '/var/run/lpd.printer' file Checking printer 'lp' Checking directory: '/var/spool/lp' directory '/var' directory '/var/spool' directory '/var/spool/lp' file 'control.lp', zero length file unchanged in 1 hours file 'status.lp', zero length file unchanged in 1 hours file 'status', zero length file unchanged in 1 hours file 'log', zero length file unchanged in 1 hours file 'acct', zero length file unchanged in 1 hours checking 'control.lp' file checking 'status.lp' file checking 'status' file cleaning 'status' file, 0K bytes: no truncation checking 'log' file cleaning 'log' file, 0K bytes: no truncation checking 'acct' file cleaning 'acct' file, 0K bytes: no truncation checkpc will create the spool directories and any missing files, and fix the permissions of existing files. Compilation and Install Once you have decided on the configuration you want and understand what files need to be installed, then you are ready to do the install. It is extremely simple to extract the files, configure, compile and install the software: h4: {4} % gunzip -c LPRng-<version>.tgz | tar xvf - h4: {5} % cd LPRng-<version> h4: {6} % ./configure [ ... configuration options ] h4: {7} % make clean all h4: {8} % su # you must do the following commands as root h4: {9} # make install h4: {10} # # if checkpc did not run, do the next command h4: {11} # # make sure checkpc and lpq are in your paths h4: {12} # # if using CSH, use 'rehash' as well h4: {13} # checkpc -f h4: {14} # # check to see if the server is running h4: {15} # lpq During installation you may get an error message indicating that the Updating Print Spooler Software and Startup Scripts to remove the existing print spooling software and then try to reinstall &LPRng;. You should also to check the System Specific Notes section for any system specific installation requirements. While the &LPRng; installation scripts try hard to set up the Installation Problems Read the notes for your OS in section System-dependent notes for specific installation help (if any). The following is a list of commonly encountered problems and their solution. If these do not solve your problem, then send mail to the lprng@lprng.com mailing list. You will have to subscribe to the list in order to post to the list. Make complains about a malformed make or Makefile file, illegal syntax in the file, or illegal entries in the file. You are most likely not running GNU Make. You must use GNU make or you should be a Unix Wizard able to master the mysteries of converting GNU Makefiles to your local system make. It is easier to simply install GNU make. The C Compiler complains about missing files or has a large number of errors. Use gcc instead of your vendor's C compiler. configure --with-cc=gcc If there are messages about missing system files, then you most likely have an incomplete set of system include files, or the include do not properly reference other required include files, or the include files are located in an unusual location. If you are using gcc then make sure that the gcc was carried out correctly on your system. The easiest way to assure this is to recompile and reinstall the gcc compiler. If you have checked your compiler installation and are still missing libraries or files then the include files may be in /usr/local/include and libraries may be in /usr/local/include and these directories may not searched or used by the compiler by default. This can be fixed by using the --with-cppopts= and --with-ldopts= configure options. configure \ --with-cppopts="-I/usr/local/include -I/usr/include/kerberosIV" \ --with-ldopts="-L/usr/local/lib -L/usr/lib/kerberosIV" The software compiles but will not run on the system. Make sure that you have followed your system specific rules for compiling and installing setuid ROOT programs on your system. You may need to statically link your executables. The software was compiled on one system and copied to another system, but will not run on the other system. Try compiling the software on the target system. If it compiles and runs, then you most likely have an issue with libraries or Operating System Versions. After you have installed the LPRng software and rebooted your system, do the following commands: h4: {16} # lpq Printer: lp@astart Queue: no printable jobs in queue If you do not get status displayed, or you get some other error message, then the following are a series of tests can use to check that &LPRng; is installed correctly. First we will run lpd in the foreground and are used to make sure that our system configuration is correct. You will need root permissions to do the following steps. Stop the running currently running lpd process. Next, run lpd in foreground mode: h4: {17} # ps -aux | grep lpd daemon 240 0.0 0.0 1292 0 ?? IWs - 0:00.00 lpd: lpd Waiting h4: {18} # kill 240 h4: {19} # checkpc -f h4: {20} # /usr/local/bin/lpd -F -D1 Fatal error - Another print spooler is using TCP printer port If you get the above error message, then you have either not terminated the running lpd server, there is another process using TCP/IP port 515, or you are not starting the lpd server as ROOT. See the System Specific Notes for details on how to resolve these issues. Correct the problem and then restart the server. You should see the output indicated below: h4: {21} # /usr/local/bin/lpd -F -D1 1999-04-05-14:35:14.023 astart27 [2667] Waiting lpd: LOOP START 1999-04-05-14:35:14.024 astart27 [2667] Waiting Get_max_servers: \ getrlimit returns 256 1999-04-05-14:35:14.024 astart27 [2667] Waiting Get_max_servers: \ returning 128 1999-04-05-14:35:14.025 astart27 [2667] Waiting lpd: \ max_servers 128, active 0 1999-04-05-14:35:14.025 astart27 [2667] Waiting lpd: \ starting select timeout 'yes', 600 sec Now from another window do the following commands: h4: {22} # lpq Printer: lp@astart Queue: no printable jobs in queue At this point your &LPRng; software has been installed and tested. See the Updating Print Spooler Software and Startup Scripts for details on how to automatically start Updating Print Spooler Software and Startup Scripts If you are replacing your existing print spooling spooling system, you must shut down and remove the existing print spooler software before installing the &LPRng; software. This process is fairly system dependent, and requires a small amount of system expertise. To assist in this process the &LPRng; installation has a set of preinstall, postinstall, preremove, and postremove scripts in the distribution that may be suitable for your local system use. If these fail to work during the system installation, you will need to carry out the steps described here by hand. SunOS, Linux, and BSD Derived Systems The lpd print server and the /usr/bin, /usr/sbin, /usr/libexec, and /usr/ucb directories. The rc startup script or by a startup script file in the /etc/rc.d/init.d or /etc/init.d directory. You can first locate the startup commands as follows. Use the /etc directory for the file that contains the startup command. h4: {23} # cd /etc h4: {24} # find . -type f -exec grep -l lpd {} \; -print ./rc.local Examine each of the files found find the one that starts the lpd print spooler. You can simply comment out the command or change it to start the &LPRng; h4: {25} # more /etc/rc.local if [ -f /etc/printcap -a -f /usr/libexec/lpd ] ; then /usr/libexec/lpd ; fi --- change this to if [ -f /etc/printcap -a -f /usr/sbin/lpd ] ; then /usr/sbin/lpd ; fi If you have an existing printcap file, then you should either copy this to the location used by &LPRng; or make a symbolic link to it. Next we kill the currently running lpd process. h4: {26} # ps -auxw |grep lpd papowell 23932 0.0 0.3 224 184 p3 S+ 10:40AM 0:00.01 grep lpd daemon 17763 0.0 0.2 448 120 ?? IWs 29Mar99 0:01.35 (lpd) h4: {27} % kill 135 h4: {28} % kill 135 135: No such process Next, you should remove or rename the existing print system executables. The following example shows how to use the find utility to track down candidates. h4: {29} # find /usr -type f -name lp\* -print >/tmp/candidates h4: {30} # find /sbin -type f -name lp\* -print >>/tmp/candidates h4: {31} # cat /tmp/candidates /usr/bin/lpunlock /usr/bin/lpqall.faces /usr/bin/lpq <---- old /usr/bin/lpr <---- old /usr/bin/lprm <---- old /usr/bin/lptest /usr/doc/samba-1.9.18p10/examples/printer-accounting/lp-acct /usr/man/man1/lpq.1 /usr/man/man1/lpr.1 /usr/man/man1/lprm.1 /usr/man/man1/lptest.1 /usr/man/man4/lp.4 /usr/man/man8/lpc.8 /usr/man/man8/lpd.8 /usr/sbin/lpc <--- old /usr/sbin/lpd <--- old /usr/sbin/lpf <--- old h4: {32} # mv /usr/bin/lpq /usr/bin/lpq.old h4: {33} # mv /usr/bin/lpr /usr/bin/lpr.old h4: {34} # mv /usr/bin/lprm /usr/bin/lprm.old h4: {35} # mv /usr/sbin/lpc /usr/sbin/lpc.old h4: {36} # mv /usr/sbin/lpd /usr/sbin/lpd.old h4: {37} # mv /usr/sbin/lpf /usr/sbin/lpf.old After all this, you should now run checkpc -f (as root) to make sure that the &LPRng; configuration is present and correctly set up, and then start lpd by hand. You should try to use lpq to see if the spool queues are present and set up correctly and the system is functional. h4: {38} # checkpc -f h4: {39} # lpd h4: {40} # lpq Printer: lw4@h2 'Hp : LaserWriter' Queue: no printable jobs in queue Status: job 'root@h2+884' removed at 11:27:25.864 Filter_status: done at 11:27:25.766 h4: {41} # lpr /etc/motd h4: {42} # lpq Printer: lw4@h2 'Hp : LaserWriter' Queue: no printable jobs in queue Status: job 'root@h2+888' removed at 11:27:25.864 Filter_status: done at 11:33:17.020 Finally, you should reboot your machine and make sure that the lpd print server starts correctly. Solaris, HP-UX, and other SysVR4 Derived Systems The original SysVR4 (System V, Release 4) software did not have any support for RFC1179 network printing (Berkeley The lpsched process (/usr/lib/lp/lpsched/) process performs many of the functions of the &LPRng; and BSD lpd server. This process is responsible for overseeing job printing and starting processes for handling the print queues on the local host. This process must be shut down and the running print spooling servers terminated before &LPRng; can be correctly installed. While there is no simple and reliable method of shutting down a running lpsched process and the associated network services, it is simple to prevent the process from being started. The preinstall.solaris script is a file in the &LPRng; distribution that contains most of the commands needed to remove the Solaris System V printing software. These are explained in detail in the sections below. The procedures outlined below can be used on other SystemVR4 systems. #!/bin/sh # This is an effort to automate the setup # needed to install the &LPRng; software on the # Solaris OS. This is effectively a one way path. # You are warned. PATH=/etc:/usr/etc:/usr/bin:/bin:/sbin:/usr/sbin:$PATH # remove the init.d entry and links for i in /etc/rc*.d/*lp ; do b=`basename $i`; d=`dirname $i`; mv $i $d/UNUSED.$b.UNUSED done # rename files renameit () { for i in $* ; do if [ -f $i -a '!' -f $i.old ] ; then echo "renaming $i $i.old"; mv $i $i.old fi done } renameit /usr/bin/lp /usr/bin/lpstat /usr/sbin/lpadmin \ /usr/sbin/lpfilter /usr/sbin/lpforms /usr/sbin/lpmove \ /usr/sbin/lpshut /usr/sbin/lpsystem /usr/sbin/lpusers \ /usr/ucb/lpc /usr/ucb/lpq /usr/ucb/lpr /usr/ucb/lprm \ /usr/ucb/lptest /usr/lib/lp/lpsched /usr/lib/lp/lpNet # remove the cron entry if [ -f /var/spool/cron/crontabs/lp ] ; then mv /var/spool/cron/crontabs/lp \ /var/spool/cron/UNUSED.crontabs.lp fi # comment out inetd.conf entry if egrep '^printer' /etc/inetd.conf >/dev/null 2>& ; then mv /etc/inetd.conf /etc/inetd.conf.bak sed -e 's/^printer/# printer/' </etc/inetd.conf.bak \ >/etc/inetd.conf fi # remove the nlsadmin entry nlsadmin -r lpd tcp nlsadmin -r lp tcp echo REBOOT SYSTEM and then install LPRng First, you will need to remove the /etc/rc startup files in the /etc/rc*.d directories that start the lpsched process; see the init program man page for details. You can find these files by using: h4: {43} # cd / h4: {44} # find . -type f -exec grep -l lpsched {} \; -print >/tmp/files h4: {45} # cat /tmp/files /etc/rc0.d/K20lp /etc/rc2.d/K20lp /etc/rc2.d/S80lp /etc/init.d/lp h4: {46} # ls -l ` cat /tmp/files ` lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc0.d/K20lp -> ../../init.d/lp lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc2.d/K20lp -> ../../init.d/lp lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc2.d/S80lp -> ../../init.d/lp -rwxr--r-- 5 root sys 460 Sep 1 1998 /etc/rcS.d/K39lp You can remove these files, or simply comment out all of the executable commands in the /etc/init.d/lp file. Next, find all of the printing related commands and rename them. For example: h4: {47} # find /usr -type f -name lp\* -print >/etc/printingfiles h4: {48} # cat /tmp/printingfiles /usr/bin/lp /usr/bin/lpstat /usr/lib/lp/bin/lp.cat /usr/lib/lp/bin/lp.set /usr/lib/lp/bin/lp.tell /usr/lib/lp/lpNet /usr/lib/lp/lpsched /usr/lib/lp/lpdata /usr/sbin/lpadmin /usr/sbin/lpfilter /usr/sbin/lpforms /usr/sbin/lpmove /usr/sbin/lpshut /usr/sbin/lpsystem /usr/sbin/lpusers /usr/ucb/lpc /usr/ucb/lpq /usr/ucb/lpr /usr/ucb/lprm /usr/ucb/lptest h4: {49} # vi /tmp/printingfiles # remove ones you want to save h4: {50} # for i in ` cat /tmp/printingfiles ` ; do > if [ -f $i -a '!' -f $i.old ] ; then mv $i $i.old ; fi; > done On some systems there may be a cron file /var/spool/cron/crontabs/lp which is used to to periodically update and roll over error logs. You may want to remove this file or comment out its contents. Check the /etc/inetd.conf file for a line like the one below and comment it out. This line is not present on all systems. printer stream tcp nowait root /usr/lib/print/in.lpd in.lpd Use nlsadmin to force the TCP/IP listener to release the port, as illustrated below. This may not be present on all system. h4: {51} # nlsadmin -v tcp lpd \x00020203000000000000000000000000 ENABLED \ NORPC root NOMODULES /var/spool/lp/fifos/listenBSD # 0 \x00020ACE000000000000000000000000 ENABLED \ NORPC root NOMODULES /usr/lib/saf/nlps_server # lp NOADDR ENABLED NORPC root NOMODULES \ /var/spool/lp/fifos/listenS5 # h4: {52} # nlsadmin -r lpd tcp h4: {53} # nlsadmin -r lp tcp Run pmadm -l as shown below. h2.private: {2} # pmadm -l PMTAG PMTYPE SVCTAG FLGS ID <PMSPECIFIC> zsmon ttymon ttya u root /dev/term/a I - /usr/bin/login ... zsmon ttymon ttyb u root /dev/term/b I - /usr/bin/login ... If you see zsmon entries for SystemV lpsched support, then use pmadm -r to remove them. These may not be present on all system. See the pmadm man page for details on the -r literal. You must now reboot the host. h4: {54} # shutdown -y "Whooga! Whooga! Dive! Dive! System going down." When the system reboots, make sure that there is no process listening on port 515 (printer port) by using: h4: {55} # telnet localhost 515 If you can connect, then there is a problem beyond the scope of these instructions. Compile and/or install the &LPRng; software. Make sure that the &LPRng; startup files have been installed correctly in /etc/init.d/lprng and that the symbolic links to the file have been made correctly. The &LPRng; startup file will usually have the following contents and you should use the same filename formats that the lp startup files had for the links to the /etc/init.d/lprng startup file: LPD_PATH=/usr/sbin/lpd SHOWALL=-e case "$1" in start) # Start daemons. /bin/echo "Starting lpd: \c" ${LPD_PATH} /bin/echo "" ;; stop) # Stop daemons. /bin/echo "Shutting down lpd: \c" kill -INT `ps ${SHOWALL} \ | awk '/lpd/{ print $1;}'` >/dev/null 2>&1 /bin/echo " server stopped"; ;; *) echo "Usage: $0 {start|stop}" exit 1 ;; esac Start the lpd server and then test it: h4: {56} # checkpc -f h4: {57} # /usr/sbin/lpd (or /usr/local/sbin/lpd) h4: {58} # lpq Printer: lp Queue: no printable jobs in queue Emulation for UNIX SystemV <application/lp/ and <application/lpstat/ Many utilities in the UNIX System V environment require the lp, lpstat, and cancel programs. It is almost impossible to modify these utilities, as many are vintage software which is unsupported or which would be too costly to update. In order to support these applications &LPRng; emulates the lp, lpstat, and clean programs. See the &LPRng; man pages for The &LPRng; lpstat emulator accepts the lpstat command line options returns status in a format that is close to the one that common If the lpr program is invoked with the name lp, it will simulate the lp options. This can be done by making a symbolic link to the h4: {59} # cd /usr/bin h4: {60} # ln -s lpr lp h4: {61} # lp /tmp/hi request id is root@h4+489 Finally, if the lprm program is invoked with the name cancel it will simulate the cancel command. This can be done by making a symbolic link to the h4: {62} # cd /usr/bin h4: {63} # ln -s lprm cancel h4: {64} # cancel 489 cancel 513 Printer lp@h9: checking perms 'root@h9+513' dequeued 'root@h9+513' Many vintage or legacy applications have fully qualified paths to the lp and lpstat executables, and it may be necessary to make additional symbolic links or copies of the &LPRng; executables to satisfy their pathname requirements. h4: {65} # ln -s /usr/bin/lpr /usr/ucb/lpr SAMBA and &LPRng; The SMB network protocol is used by many Microsoft Operating Systems to implement file and printer sharing. SAMBA is a UNIX package that implements the SMB protocol and provides a simple and easy way to import and export file systems and printer facilities. The web site for SAMBA is http://www.samba.org. The SAMBA code is extremely easy to install and the SWAT (Samba Web Administration Tool) makes configuration almost trivial. See the SAMBA doc/text/Printing.txt and related documentation for details on printing. In the samba.conf file [global] section or in the SWAT page for printing configuration you need to specify the that you want to have Samba handle printing, the print, lpq, and lprm commands to be used when a user prints a job, asks for status, or removes a job, and a temporary directory to hold print jobs when they are submitted. The following is a simple example of to set up printing for authenticated users. [printers] path = /var/spool/lpd/samba # --- do not use the Samba default path = /tmp print ok = yes printing = lprng load printers = yes guest ok = no printcap name = /etc/printcap print command = /usr/bin/lpr -P%p -r %s lpq command = /usr/bin/lpq -P%p lprm command = /usr/bin/lprm -P%p %j lppause command = /usr/sbin/lpc hold %p %j lpresume command = /usr/sbin/lpc release %p %j queuepause command = /usr/sbin/lpc stop %p queueresume command = /usr/sbin/lpc start %p Samba will make a copy of the files to be printed in the directory specified by path. If the print operation fails then sometimes the print file is left in the directory. The directory should have the same ownership and permissions as /tmp, i.e.- owner and group A directory whose `sticky bit' is set becomes an append-only directory, or, more accurately, a directory in which the deletion of files is re- stricted. A file in a sticky directory may only be removed or renamed by a user if the user has write permission for the directory and the user is the owner of the file, the owner of the directory, or the super-user. This feature is usefully applied to directories such as /tmp which must be publicly writable but should deny users the license to arbitrarily delete or rename each others' files. The directory should be examined periodically and files older then a day should be removed. The following command can be used to do this, and should be put in a file that is periodically (one a day) executed by the find /var/spool/lpd/samba -type f -mtime 2d -exec rm -f {} \; You must specify the print method as printing = lprng. This will allow Samba to parse the &LPRng; You must put all of the printers which Samba has access to in the printcap file. Your Samba server may support reading the printcap file by using a program. In this case the printcap file entry can be one of the following: [printers] # printcap name = |/usr/local/libexec/filters/getpc # or printcap name = |/usr/bin/lpc client all #!/bin/sh # getpc program /usr/bin/lpq -as | /bin/sed -e 's/[@:].*//p' The Samba can be configured to allow guests or non-authenticated users to spool print jobs. Unfortunately, by default lpr -U%U@%M option causes allow_user_setting configuration option. If the userid of the submitter is not in this list, then the option is quietly ignored. The -U%U@M can also be used with the other LPRng commands as well. For example: [printers] guest ok = yes print command = /usr/bin/lpr -U%U@%M -P%p -r %s lpq command = /usr/bin/lpq -U%U@%M -P%p lprm command = /usr/bin/lprm -U%U@%M -P%p %j lppause command = /usr/sbin/lpc -U%U@%M hold %p %j lpresume command = /usr/sbin/lpc -U%U@%M release %p %j queuepause command = /usr/sbin/lpc -U%U@%M stop %p queueresume command = /usr/sbin/lpc -U%U@%M start %p When Samba gets a request for print queue status, it runs the One of the problems that might occur is when the &LPRng; h110: {588} % lpq Printer: t1@h110 'Test Printer 1' Queue: no printable jobs in queue Server: no server active Status: job 'papowell@h110+336' saved at 14:42:54.607 Filter_status: FILTER DONE Rank Owner/ID Class Job Files Size Time done papowell@h110+336 A 336 /tmp/hi 3 14:42:53 In this example, the lp: :done_jobs=0 :... Security Concerns While the &LPRng; software has been written with security as the primary goal there is always the problem with undetected errors in the &LPRng; software that when exploited could compromise system security. The most serious concern is that of gaining ROOT (UID 0) permissions. The simplest way to handle this problem is to not install LPRng with setuid ROOT permissions. Client programs will be able to connect to the A more radical step is to run the lpd requires root permissions to open and bind to port 515. The lpd server can use the lpd must originate connections from a reserved port in the range 721-731, although in practice port 1-1023 seems to be acceptable. If inter-operability with non-&LPRng; print spoolers is not desired, then it is trivial to configure &LPRng; to use a non-privileged port by using the lpd.conf file. For example, in the /etc/lpd.conf file, you only need to change the indicated lines: # Purpose: lpd port # default lpd_port=printer lpd_port=2000 # or lpd_port=localhost%2000 The lpd_port specifies the (optional) IP address and port to which the private set of print spoolers which can be used for testing See Testing and Diagnostic Facilities for more details. Some legacy print filters are not meta-char-escape proof. For example, suppose that a user decided to spool a job as follows: h4: {66} # lpr "-J`;rm -rf /;`" /tmp/a This would create a job file with the line: J`rm -rf /;` and gets passed to a print filter as /usr/local/printfilter -J`rm -rf /;` The observant reader will observe that the above line may have the most hideous consequences if it is processed by a shell. For this reason the &LPRng; software takes extreme precautions and Finally, you can use a Unix socket (i.e. - FIFO) for connections to the server on the localhost, and disable the &lpd; listening socket by setting the System Specific Notes The following are a set of suggestions and recommendations for specific systems. Solaris The Sun Microsystems Solaris printing system is derived from the System V UNIX system. Please see the Solaris, HP, and SysVR4 Derived Systems installation information for a detailed description of how to install &LPRng; and remove the Solaris Print Services. If you want to simply forward jobs from a Solaris system to a BSD print spooling system you can use the following commands to create a printer. Check your specific system references and man pages for options: h4: {67} # lpsystem -t bsd servername # add server h4: {68} # lpadmin -p pr -s servername -T unknown -I any # set up printer h4: {69} # accept pr # enable queueing h4: {70} # enable pr # enable printing h4: {71} # lpstat -t # show status scheduler is running system for pr: servername lp accepting requests since Mon Aug 6 12:00:00 PST 2000 Printer: pr@servername 'Hp : Laserwriter' (printing disabled) Queue: 1 printable job Rank Owner/ID Class Job Files Size Time 1 papowell@h4+207 A 207 h4.023205 3 18:24:54 The above commands will create the necessary directories and files for the printer. If you want to use the /etc/printers.conf (this may be in some other directory besides /etc) needs to be modified so that it allows options to be passed using the Solaris convention, which is to put them on the # # The preferred method of modifying this file is through the use of # lpset(1M) or fncreate_printer(1M) # pr:bsdaddr=servername,pr,Solaris: _default:use=pr: The Linux There is no universal way to install &LPRng; cleanly on all of the different Linux systems. The major difficulty is the fragmentation in the various libraries, location of files, and system dependencies. If the &LPRng; installation procedure does not install the software correctly, please use the following remedial steps. Check the &LPRng; web site http://www.lprng.com and see if there is a Linux Release RPM or other binary distribution for your version of Linux. If not, then you will have to do a source install. Obtain the source distribution and follow the instructions outlined in other sections to compile and install the software. Use the following configuration options which correspond to Linux: h4: {72} # ./configure --prefix=/usr \ --sysconfdir=/etc --mandir=/usr/share/man h4: {73} # make clean all install h4: {74} # checkpc -f You may need to modify your existing printcap file by adding the Test the system by printing a file. If this does not work, then you will have to determine if the problem is in the print spooling software or in the filter. See the section on ifhp for directions on how to replace the vendor supplied filters with ifhp. AIX This information was supplied by Dirk Nitschke, as of August 1997, and describes how to install the &LPRng; package on a workstation running AIX 4.1.x and possibly 3.x.x as well. Dirk would be interested in any comments or corrections. Printing on AIX systems is different. AIX provides a general queueing facility and printing is only one way to use it. You submit a print job to a print queue using one of the commands qprt or enq. You can use the BSD or System V printing commands lpr or lp, too. The qdaemon watches all (general) queues and knows how to handle your job. A (general) queue is defined in the file /etc/qconfig. The format of this file is different from the printcap format. OK, how to replace the AIX printing system? There is no group daemon on AIX. Therefore you have to change the default group for file ownership and process permissions or create a daemon user and group. We decided to use the printq group; on reflection it would have been easier to have created a daemon group. The user daemon exists on AIX but we have chosen lpd as the user who runs lpd and all filters and owns the spooling directories. You can change the values for group and user in your lpd.conf file or in the sources src/common/vars.c. This is an example for lpd.conf: # Purpose: group to run SUID ROOT programs # default group=daemon group=printq # Purpose: server user for SUID purposes # default user=daemon user=lpd Compile and install the &LPRng; package. Create your printcap, spooling directories, accounting and logfiles and so on. Don't forget to use checkpc to make sure that all the permissions are set correctly and the necessary files are created. Then stop all print queues defined on your workstation. Use # chque -q queuename -a "up = FALSE" for this (yes, blanks around = are needed). If you have local printers attached to your system you will have an lpd running. Stop this daemon using SMIT (Print Spooling, Manage Print Server, Stop the Print Server Subsystem). Choosing both also removes lpd from /etc/inittab. Maybe it's faster to do this by hand: h4: {75} # topsrc -p'pid of /usr/sbin/lpd' h4: {76} # rmitab "lpd" Now delete all print queues managed by qdaemon defined on your system. You can use {mk,ch,rm}que, {mk,ch,rm}quedev, {mk,ch,rm}virprt. The smit rmpq. To start the new lpd at system startup you have to add an entry to /etc/inittab: h4: {77} # mkitab "lpd:2:once:/full/path/lpd" Some work has to be done if have have a local printer attached to your workstation. You have to create a device file like /dev/lp0. The smit mkdev. Choose Printer/Plotter, then Printer/Plotter Devices, then Add a Printer/Plotter. To create a parallel printer device select the following: Plotter type: opp Other parallel printer Printer/Plotter Interface: parallel Parent Adapter: ppa0 Available Now define the characteristics of the device: Port Number: p Option p is for parallel. Go to the field: Send all characters to printer UNMODIFIED no and select yes! We have had a lot of trouble with no. This is very important! Expect erroneous output if you choose no. If you have already created a device file, change the characteristics! SMIT's fast path is smit chdev. Finally remove all AIX printing commands like qprt, lp, cancel, lpq, and lprm. You will find a lot of them in /usr/bin. Do not remove enq and friends if you want to use the general queueing facility. Now you can start your new lpd. AppleTalk Support Netatalk is used to communicate from TCP/IP to AppleTalk printers and vice versa. The Netatalk distribution FAQ is at: http://www.umich.edu/~rsug/netatalk. Also, The University of Melbourne web site http://www.cs.mu.oz.au/appletalk/ has additional tutorial and installation information. In addition, Anders Brownworth's Web page http://thehamptons.com/anders/netatalk/ has a useful collection of information for Linux users. The University of Michigan doesn't seem to be doing anything more for ftp://ftp.cobaltnet.com/pub/users/asun/testing/ He has improved There are a couple of very active mail lists for Netatalk: netatalk-admins@umich.edu and linux-atalk@netspace.org. After you have installed and gotten Your 32 Character Printer Name:\ :pr=|/your/path/to/lpr -Pprintername:\ :op=username.for.printing:\ :pd=/your/path/to/ppd/files/yourprinter.ppd: Examples: Student Printers:\ :pr=|/usr/bin/lpr -Pstudent:\ :op=root:\ :pd=/var/spool/lpd/student/HP4000.PPD: HP 2500c:\ :pr=|/usr/bin/lpr -Php2500c:\ :op=root:\ :pd=/var/spool/lpd/hp2500c/HP2500.PPD: Print Spooling Tutorial A print spooler is a program that accepts print jobs (which are usually one or more files) from a program or network interface, stores them in a spool queue, and then sends them to a printer or another print spooler. Usually there are facilities to submit jobs, check on the current job status, remove jobs from spool queues, and perform administrative functions such as starting or stopping printing. A print spooler is a client/server application. The client programs are used to submit jobs to the print spooler program which performs the actual printing operations. In order to carry out these operations, the server may need to use other programs to convert print job files into a format acceptable to a printer, or perform various accounting or administrative functions. Overview +---------+ +-----+ +-----+ +--------+ +---------+ | program | -> | lpr | -> | lpd | -> | filter | -> | printer | +---------+ +-----+ * +-----+ +--------+ +---------+ * * | printcap V +-----+ +--------+ +---------+ | lpd | -> | filter | -> | printer | +-----+ +--------+ +---------+ Figure 1 Figure 1 shows the flow of data between the individual components of the &LPRng; print spooling system. A program (or user) will use the lpr program to send a file to the lpd server over a TCP/IP connection. The lpd server will store the file temporarily in a spool queue directory. The information needed by the lpr and lpd programs to carry out this activity is stored in the printcap (usually called the /etc/printcap) database file. The lpd server sorts the queue entries and determines the print order. It will select a job to be printed, open a connection to the printer, and then use a filter program to convert the file contents into a format suitable for the printer. If the file does not need conversion, then the lpd server will send the file directly to the printer. The lpd server can also forward jobs to another print server over a network connection, optionally sending them through a filter as well. The destination server can in turn forward the job or send it to a printer. The protocol or commands used to do this job forward and transfer are specified by RFC1179. This protocol specifies how the lpr client program sends a job to the lpd server, as well as how the lpd server forwards jobs to another server. In addition to job submission, RFC1179 specifies commands to obtain queue status, to remove jobs from the queue, and to start and stop print queues. Sample Printcap Entry As described in the Print Spooling Overview, the information in the printcap database is used control printing operations. While there is no RFC specifying its format or content, there is a strong de facto standard for its format. For a complete description of the printcap database see Printcap Database. For a list of all of the printcap and configuration options see Index To All The Configuration and Printcap Options. Here is a sample printcap suitable for use by the &LPRng; clients: LPRng use :lp for destination (device and spool queue) lp:lp=lp@h4.private lpreal:lp=/dev/lp0 Vintage BSD uses :rp:rm for spooler queue and :lp for device lp:rp=lp:rm=h4.private lpdev:lp=/dev/lp0 The :lp=lp@h4.private printcap entry tells the client programs that jobs for the lp printer or print queue are sent to the lp print queue queue on host h4.private. This can also be specified using the The legacy BSD :rp and :rm. The :rp=queue option specifies the print queue :rm=host option specifies the host. When both lp and :rp:rm are present the :lp option has precedence. On the printserver the following is a sample printcap entry suitable for the lpd server: lp: :lp=/dev/lp0 :sd=/var/spool/lpd/%P :filter=/usr/local/libexec/filters/ifhp The :sd=directory option (spool queue directory) specifies the directory where print jobs will be placed. The %P will be replaced with the name of the spool queue. The :lp literal is now the path to the output device the lpd server will use to print the job. The :filter literal specifies the filter program to use to process the job before sending to the output device. Here is another example of a printcap entry using the %P notation: lp:lp=%P@h4.private This entry will cause all jobs sent to the lp spool queue to be sent to the lp queue on server. %X strings in the printcap entries are expanded as shown: KeyReplaced By printcap entry primary name (printer) queue requested short host name (host) fully qualified host name (host.dns.whatever) remote printer (rp value) remote host (rm value) date in YYYY-MM-DD format Setting Up the Tutorial Configuration The previous section has given a very high level view of printing operations and shown a sample of some printcap files. In order to do experiment with these LPRng facilities, we will need to be able to modify the printcap information and try various system configurations. We will use a series of simple printcap entries during this tutorial. We will assume that the &LPRng; system is using the /etc/printcap file. If your system is configured to use another one, then you can make a symbolic link from /etc/printcap or you can simply use your default printcap file. Save the existing printcap file and then create a new printcap file with the contents as shown below. You will need to have ROOT (superuser) permissions to change the file and perform some of the maintenance operations. h4: {78} # cd /etc h4: {79} # mv printcap printcap.orig h4: {80} # vi printcap # printcap file contents: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp lp2:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp2 h4: {81} # # set permissions so everybody can read file h4: {82} # chmod 666 printcap We save the original printcap file and create a new one. We give the file world writable permissions so that later we can modify this file without needing to have root permissions. The printcap file has two entries: lp and lp2. Each print queue on the server needs a spool file to hold print jobs, jobs, and the :sd value specifies its location. The %P value is replaced with the name of the printer when it is used. In classical BSD operation each host has an lpd print spooler running on the local host (we use localhost in this manual for simplicity). Files were copied to spool directories on the localhost and then then print spooler would send them to the destination, which could be another print spooler. This meant that each localhost machine had to have a print spooler and spool queue directory structure. Management of this becomes very difficult in large organizations. The force_localhost forces this mode of operation and means that the We use files for the output devices (:lp=) so that we can see easily view the output (and also to save trees). We will also need to have some simple test files. Create the files using the following commands. h4: {83} # cp /dev/null /tmp/lp h4: {84} # cp /dev/null /tmp/lp2 h4: {85} # chmod 666 /tmp/lp /tmp/lp2 h4: {86} # echo hi >/tmp/hi h4: {87} # echo there >/tmp/there We will use a dummy lpd.perms file that allows all users to do anything. This is useful for testing, but dangerous in a working environment. h4: {88} # # we modify the lpd.perms to allow an ordinary user to control h4: {89} # mv lpd.perms lpd.perms.orig h4: {90} # echo "DEFAULT ACCEPT" >lpd.perms h4: {91} # chmod 666 lpd.perms Finally we run checkpc to make sure that our printcap is correct and to create the necessary spool directories: h4: {92} # checkpc -f -V Checking printer 'lp' Checking directory: '/var/spool/lp' directory '/var' directory '/var/spool' directory '/var/spool/lp' Warning - changing ownership '/var/spool/lp' to 1/1 checking 'control.lp' file checking 'status.lp' file checking 'status' file cleaning 'status' file, 0K bytes: no truncation checking 'log' file cleaning 'log' file, 0K bytes: no truncation checking 'acct' file cleaning 'acct' file, 0K bytes: no truncation Checking printer 'lp2' Checking directory: '/var/spool/lp2' directory '/var' .... h4: {93} # lpc reread h4: {94} # lpd h4: {95} # lpq Printer: lp@h4 Queue: no printable jobs in queue performs consistency checks on the printcap file and spool queue entries. The checkpc -f (fix) option will change permissions and create directories and can only be executed by ROOT. has other functions as well - you can view printcap information, see default configuration values, and remove junk files from spool queues with it. The lpc reread command sends a request to the lpd command is added as insurance in case your lpd server is not running. The exit command restores ordinary user privileges, and the lpq command is used to check that the server is running. Finally, we check to see that the lpc reread command is accepted from an ordinary user. Restoring Original Configuration To restore the original configuration, you simply need to restore the original printcap and lpd.perms file and then restart the lpd server. These operations require ROOT permissions. h4: {96} # su h4: {97} # cd /etc OR cd /usr/local/etc h4: {98} # mv printcap.orig printcap h4: {99} # mv lpd.perms.orig lpd.perms h4: {100} # checkpc -f h4: {101} # lpc reread h4: {102} # rm -rf /var/spool/lpd/lp /var/spool/lpd/lp2 h4: {103} # rm -f /tmp/lp /tmp/lp2 Printing a File and Checking Status Try the following commands. The commands appear after the prompt, and sample output that you might see is shown. h4: {104} % lpr -V /tmp/hi Version LPRng-3.6.14 sending job 'papowell@h4+238' to lp@localhost connecting to 'localhost', attempt 1 connected to 'localhost' requesting printer lp@localhost sending control file 'cfA238h4.private' to lp@localhost completed sending 'cfA238h4.private' to lp@localhost sending data file 'dfA238h4.private' to lp@localhost completed sending 'dfA238h4.private' to lp@localhost done job 'papowell@h4+238' transfer to lp@localhost The lpr -V (Verbose) option causes lpd server on host localhost, then sends a print request (which is accepted), then sends a control file containing information about the job and a data file or files which are copies of the files to be printed. If you check the /tmp/lp file and you will find that a copy of /tmp/hi has been written to it. By default, the lpd print spooler acts as a store and forward system, accepting files to be printed, holding them in the print queue, and then forwarding them to the destination system or output device. You can use the lpq command to view the status of the print job. h4: {105} % lpq Printer: lp@h4 Queue: no printable jobs in queue Status: job 'papowell@h4+238' removed at 09:39:03.256 If you want to see more status information, use lpq -l, lpq -ll, or even lpq -L. The -L provides alL the status. h4: {106} % lpq -l Printer: lp@h4 Queue: no printable jobs in queue Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112 Status: job 'papowell@h4+238' removed at 09:39:03.256 h4: {107} % lpq -ll Printer: lp@h4 Queue: no printable jobs in queue Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112 Status: job 'papowell@h4+238' removed at 09:39:03.256 h4: {108} % lpq -L Printer: lp@h4 Queue: no printable jobs in queue Status: subserver pid 8240 starting at 09:39:03.105 Status: accounting at start at 09:39:03.105 Status: opening device '/tmp/lp' at 09:39:03.105 Status: printing job 'papowell@h4+238' at 09:39:03.106 Status: no banner at 09:39:03.107 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107 Status: printing done 'papowell@h4+238' at 09:39:03.107 Status: accounting at end at 09:39:03.108 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112 Status: job 'papowell@h4+238' removed at 09:39:03.256 There are different status formats available as well. The lpq -s (summary) produces a single line of status per spool queue, while the lpq -v (verbose) produces output that is very suitable for processing with programs such as Perl or awk: h4: {109} % lpq -s lp@h4 0 jobs h4: {110} % lpq -v Printer: lp@h4 Printing: no Aborted: no Spooling: no Queue: no printable jobs in queue SPOOLCONTROL= Status: subserver pid 8240 starting at 09:39:03.105 Status: accounting at start at 09:39:03.105 Status: opening device '/tmp/lp' at 09:39:03.105 Status: printing job 'papowell@h4+238' at 09:39:03.106 Status: no banner at 09:39:03.107 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107 Status: printing done 'papowell@h4+238' at 09:39:03.107 Status: accounting at end at 09:39:03.108 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112 Status: job 'papowell@h4+238' removed at 09:39:03.256 If you check the /tmp/lp file and you will find that a copy of /tmp/hi has been written to it. By default, the lpd print spooler acts as a store and forward system, accepting files to be printed, holding them in the print queue, and then forwarding them to the destination system or output device. Selecting the Print Queue In the previous section we used the default print queue. How does &LPRng; determine what print queue to use? First, you can explicitly specify the printer using the lpq -Pprintqueue option and the lpq -a or lpq -Pall to select all print queues: h4: {111} % lpq -Plp Printer: lp@h4 Queue: no printable jobs in queue h4: {112} % lpq -Plp2 Printer: lp2@h4 Queue: no printable jobs in queue h4: {113} % lpq -a Printer: lp@h4 Queue: no printable jobs in queue Printer: lp2@h4 Queue: no printable jobs in queue You can combine the lpq -a with the lpq -s option for a summary listing: h4: {114} % lpq -a Printer: lp@h4 Queue: no printable jobs in queue Printer: lp2@h4 Queue: no printable jobs in queue h4: {115} % lpq -s -a lp@h4 0 jobs lp2@h4 0 jobs There is another way to explicitly specify the printqueues listed by lpq -a; see the all Printcap Entry for details. Users can set their default printer by using the PRINTER (highest priority), LPDEST (next), NPRINTER (next), and NGPRINTER (lowest priority), environment variables. For example: h4: {116} % setenv PRINTER lp2 h4: {117} % lpq Printer: lp2@h4 Queue: no printable jobs in queue h4: {118} % unsetenv PRINTER h4: {119} % lpq Printer: lp@h4 Queue: no printable jobs in queue If the printer is not specified on the command line or by the environment variables, then the first printer in the printcap database will be used and then the default printer in the configuration database, and finally the compile time default. Printer and Server Information for details. Controlling the Print Queue The lpc command is used to examine and control the print server operation. The lpc status command displays the administrative status of a print queue. The lpc flush command will flush the cached information and cause the server to regenerate it. The lpc enable and lpc disable commands enable or disable spooling to the print queue, and the lpc stop and lpc start commands stop and start printing (or transfers) of jobs in the print queue. Let's look at the status displayed when we use these commands: h4: {120} % lpc status Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 enabled enabled 0 none none h4: {121} % lpc status all Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 enabled enabled 0 none none lp2@h4 enabled enabled 0 none none h4: {122} % lpc lpc>status Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 enabled enabled 0 none none lpc>status all Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 enabled enabled 0 none none lp2@h4 enabled enabled 0 none none lpc>quit The lpc command can be used in command line or interactive mode as shown above. When used with no parameters it will run in interactive mode, reading one or more commands from its standard input (lpc status command shows the administrative status of the select print queue. The all queue name selects all print queues for display. As shown in the above example, both print queues have printing and spooling enabled and there are no jobs in the print queue. The Server and Subserver information shows if there is a process which is printing jobs, and its helper process that does the actual communication with the printer. It might be puzzling at first why &LPRng; uses two processes for this operation, but the reason is very simple. Many operating system implementations have memory leaks that cause the actual process size to grow as it runs. This is especially true if a large number of databases such as the password, Domain Name Server, or other system database is consulted frequently with different queries. Since this is usually done quite a lot by the process which deals with the actual printing, the printing process would soon grow very large and then die when it could no longer obtain more memory. The Server process will fork or create a child process Subserver process that is responsible for the printing of a single job. When the job printing has been completed, the Subserver process will exit and the Server process will then create another child until there are no more jobs to be printed. The Redirect and Debug fields will be discussed in later sections. Now let's use the basic spool queue control commands: h4: {123} % lpc disable Printer: lp@h4 lp@h4.private: disabled h4: {124} % lpq Printer: lp@h4 (spooling disabled) Queue: no printable jobs in queue h4: {125} % lpc enable Printer: lp@h4 lp@h4.private: enabled h4: {126} % lpq Printer: lp@h4 Queue: no printable jobs in queue h4: {127} % lpc stop Printer: lp@h4 lp@h4.private: stopped h4: {128} % lpq Printer: lp@h4 (printing disabled) Queue: no printable jobs in queue h4: {129} % lpc start Printer: lp@h4 lp@h4.private: started h4: {130} % lpq Printer: lp@h4 Queue: no printable jobs in queue As we can see, the lpc command also reports on the status of the print queue. Let's see what happens when we print to a stopped queue: h4: {131} % lpc stop Printer: lp@h4 lp@h4.private: stopped h4: {132} % lpr /tmp/hi h4: {133} % lpr /tmp/hi /tmp/there h4: {134} % lpq Printer: lp@h4 (printing disabled) Queue: 2 printable jobs Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+17920 A 17920 /tmp/hi 3 18:14:22 2 papowell@h4+17922 A 17922 /tmp/hi,/tmp/there 9 18:14:30 h4: {135} % lpc status Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 disabled enabled 2 none none The lpc status shows that we have two jobs spooled. The Rank field shows the order, the Owner/ID shows the unique job ID that is assigned to the job and the Class field is the job class (this may be changed with the lpr -C class option). The Job field shows the job number assigned to this job in this particular spool queue. While the ID value never changes as a job moves through the &LPRng; system, the job number is specific to a particular spool queue and may change if a job is forwarded to another spool queue that has a job with the same job number. The Size field is the total number of printable bytes in the job, and the Time field shows the timestamp associated with the job. Now let's start the print queue and watch what happens. h4: {136} % lpc start Printer: lp@h4 lp@h4.private: started h4: {137} % lpq Printer: lp@h4 Queue: 2 printable jobs Server: pid 17928 active Unspooler: pid 17929 active Status: opening device '/tmp/lp' at 18:14:43.921 Rank Owner/ID Class Job Files Size Time active papowell@h4+17920 A 17920 /tmp/hi 3 18:14:22 2 papowell@h4+17922 A 17922 /tmp/hi,/tmp/there 9 18:14:30 h4: {138} % lpq -ll Printer: lp@h4 Queue: 2 printable jobs Server: pid 17928 active Unspooler: pid 17929 active Status: printing job 'papowell@h4+17920' at 18:14:43.921 Status: no banner at 18:14:43.921 Status: printing data file 'dfA017920h4.private', size 57 at 18:14:43.922 Rank Owner/ID Class Job Files Size Time active papowell@h4+17920 A 17920 /tmp/hi 3 18:14:22 2 papowell@h4+17922 A 17922 /tmp/hi,/tmp/there 9 18:14:30 The Rank value of the first job has been changed to active and there is new Status information. If we use lpq -ll we can see the times that the various print operations are carried out, and details of their success or failure. We can also use the lpc command to see the status of a particular job. We can select jobs by the user name, the ID, or the job number. For example: h4: {139} % lpc stop Printer: lp@h4 lp@h4.private: stopped h4: {140} % echo hi |lpr h4: {141} % echo there | lpr h4: {142} % echo test |lpr h4: {143} % lpq Printer: lp@h4 (printing disabled) Queue: 3 printable jobs Server: no server active Status: job 'papowell@h4+17922' removed at 18:15:13.981 Rank Owner/ID Class Job Files Size Time 1 papowell@h4+17959 A 17959 (stdin) 3 18:23:24 2 papowell@h4+17962 A 17962 (stdin) 6 18:23:30 3 papowell@h4+17970 A 17970 (stdin) 5 18:23:35 h4: {144} % lpq 17970 Printer: lp@h4 (printing disabled) Queue: 3 printable jobs Server: no server active Status: job 'papowell@h4+17922' removed at 18:15:13.981 Rank Owner/ID Class Job Files Size Time 3 papowell@h4+17970 A 17970 (stdin) 5 18:23:35 h4: {145} % lpq papowell Printer: lp@h4 (printing disabled) Queue: 3 printable jobs Server: no server active Status: job 'papowell@h4+17922' removed at 18:15:13.981 Rank Owner/ID Class Job Files Size Time 1 papowell@h4+17959 A 17959 (stdin) 3 18:23:24 2 papowell@h4+17962 A 17962 (stdin) 6 18:23:30 3 papowell@h4+17970 A 17970 (stdin) 5 18:23:35 h4: {146} % lpq -s 17970 lp@h4 1 jobs h4: {147} % lpq -s papowell lp@h4 3 jobs h4: {148} % lpq -s nobody lp@h4 0 jobs We use lpq -Pqueuename to select a specific print queue and lpq -a or lpq -Pall to select all queues: h4: {149} % lpc -a stop Printer: lp@h4 lp@h4.private: stopped Printer: lp2@h4 lp2@h4.private: stopped h4: {150} % lpc -Pall start Printer: lp@h4 lp@h4.private: started Printer: lp2@h4 lp2@h4.private: started You can use the lpc command in interactive mode: h4: {151} % lpc lpc>status Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 enabled enabled 3 17990 17993 lpc>status all Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 enabled enabled 3 17990 17993 lp2@h4 enabled enabled 3 none none lpc>stop lp Printer: lp@h4 lp@h4.private: stopped lpc>start lp Printer: lp@h4 lp@h4.private: started lpc>quit The lpc topq command can be used to put a job (or jobs) at the head of the spool queue. This command is very useful when some job requires priority service. You can select the job by using the job number or the job ID. h4: {152} % lpc topq lp 17970 Printer: lp@h4 lp: selected 'papowell@h4+17970' lp@h4.private: started h4: {153} % lpq Printer: lp@h4 Queue: 3 printable jobs Server: pid 17999 active Rank Owner/ID Class Job Files Size Time active papowell@h4+17970 A 17970 (stdin) 5 18:23:35 1 papowell@h4+17959 A 17959 (stdin) 3 18:23:24 2 papowell@h4+17962 A 17962 (stdin) 6 18:23:30 Job Removal Occasionally we print a file and then change our mind and want to cancel the job. The lprm command allows us to do this. h4: {154} % lpq Printer: lp@h4 (printing disabled) Queue: 3 printable jobs Server: no server active Status: job 'papowell@h4+17922' removed at 18:15:13.981 Rank Owner/ID Class Job Files Size Time 1 papowell@h4+17959 A 17959 (stdin) 3 18:23:24 2 papowell@h4+17962 A 17962 (stdin) 6 18:23:30 3 papowell@h4+17970 A 17970 (stdin) 5 18:23:35 h4: {155} % lprm Printer lp@h4: checking perms 'papowell@h4+17959' dequeued 'papowell@h4+17959' h4: {156} % lpq Printer: lp@h4 (printing disabled) Queue: 2 printable jobs Server: no server active Status: job 'papowell@h4+17922' removed at 18:15:13.981 Rank Owner/ID Class Job Files Size Time 1 papowell@h4+17962 A 17962 (stdin) 6 18:23:30 2 papowell@h4+17970 A 17970 (stdin) 5 18:23:35 h4: {157} % lprm 17970 Printer lp@h4: checking perms 'papowell@h4+17970' dequeued 'papowell@h4+17970' h4: {158} % lpq Printer: lp@h4 (printing disabled) Queue: 1 printable job Server: no server active Status: job 'papowell@h4+17922' removed at 18:15:13.981 Rank Owner/ID Class Job Files Size Time 1 papowell@h4+17962 A 17962 (stdin) 6 18:23:30 By default, the lprm command removes the first job in the queue that the user has permission to remove. Also, as shown in the example, you can remove a job by specifying the job ID or the job number. If you specify a user name, you remove all of the user's jobs. This can be dangerous: h4: {159} % lpq Printer: lp@h4 (printing disabled) Queue: 3 printable jobs Server: no server active Status: job 'papowell@h4+17922' removed at 18:15:13.981 Rank Owner/ID Class Job Files Size Time 1 papowell@h4+17962 A 17962 (stdin) 6 18:23:30 2 papowell@h4+18499 A 18499 /tmp/hi 3 18:56:00 3 papowell@h4+18501 A 18501 /tmp/there 6 18:56:02 h4: {160} % lprm papowell Printer lp@h4: checking perms 'papowell@h4+17962' dequeued 'papowell@h4+17962' checking perms 'papowell@h4+18499' dequeued 'papowell@h4+18499' checking perms 'papowell@h4+18501' dequeued 'papowell@h4+18501' h4: {161} % lpq Printer: lp@h4 (printing disabled) Queue: no printable jobs in queue Status: job 'papowell@h4+17922' removed at 18:15:13.981 The special user all matches all jobs in a print queue. Clearly you should be careful not to specify lprm all by accident. Even more dangerous is the following command: h4: {162} % lprm -a all As you might surmise, this removes all print jobs in all queues, which is an excellent way to purge print queues of all jobs. Print Job Filters A printer usually understands one or more Print Job Languages. Files sent to this printer must be in one of these languages and have the appropriate job format. The most common Print Job Languages are PostScript and PCL. Text files are PCL with no special PCL control sequences. In order for a printer to reliably print a job it needs to be reset to a known configuration and then at the end of job having it flush all of the output to the printing device. This is done by sending it start of job and end of job commands. These commands differ from printer to printer and depend on the print job language as well. Some vintage line printers also have a set of proprietary escape sequences that are used to set up margins, form size, and other printing characteristics. Usually a setup string with these escape sequences must be sent to the printer before a file can be printed. When sending a job to the printer the print spooler will first process the job using a When a print job is created the files in the print job are assigned a f is the default format and indicates normal processing and the l format indicates a literal or binary file. Job files that are flagged as having literal or binary format are usually passed directly to the printer or have at the most a minimal amount of processing. See Print Job Formats for more information about formats and their use with filters. There are two ways to specify filters: the default lp:sd=/var/spool/lpd/%P :filter=/usr/local/lib/filters/ifhp :rf=/usr/local/lib/filters/rfilter All jobs with formats other than We will set up a very simple filter and use it to demonstrate how filtering is done by the /tmp/testf file as shown below. #!/bin/sh # /tmp/testf - test filter for LPRng PATH=/bin:/usr/bin; export PATH echo TESTF $0 "$@" >&2 echo TESTF $0 "$@" echo ENV set echo LEADER /bin/cat echo TRAILER exit 0 Let us carefully examine the script line by line. The first couple of lines are The next lines echo the command line arguments to file descriptor 2 (set command to list the shell variables to LEADER to TRAILER to We can test our script, with the results shown below: h4: {163} % chmod 755 /tmp/testf h4: {164} % echo hi |/tmp/testf -a1 TESTF /tmp/testf -a1 TESTF /tmp/testf -a1 ENV USER=papowell HOSTNAME=h4 ... PATH=/bin:/usr/bin LEADER hi TRAILER Let's now use this filter. Edit the lp printcap entry so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=/tmp/testf Execute the following commands to print the /tmp/hi file and observe the results: h4: {165} % cp /dev/null /tmp/lp h4: {166} % lpr /tmp/hi h4: {167} % lpq -llll Printer: lp@h4 Queue: no printable jobs in queue Status: lp@h4.private: job 'papowell@h4+26593' printed at 21:37:21.312 Status: job 'papowell@h4+26593' removed at 21:37:21.323 Status: subserver pid 26683 starting at 21:39:21.908 Status: accounting at start at 21:39:21.908 Status: opening device '/tmp/lp' at 21:39:21.909 Status: printing job 'papowell@h4+26681' at 21:39:21.909 Status: no banner at 21:39:21.909 Status: printing data file 'dfA026681h4.private', size 3, \ IF filter 'testf' at 21:39:21.909 Status: IF filter msg - 'TESTF /tmp/testf -Apapowell@h4+26681 \ -CA -D2000-04 -11-21:39:21.877 -Ff -Hh4.private -J/tmp/hi \ -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \ -edfA026681h4.private -f/tmp/hi -hh4.private -j026681 \ -kcfA026681h4.private -l66 -npapowell -sstatus \ -t2000-04-11-21:39:21.000 -w80 -x0 -y0 acct' \ at 21:39:21.914 Status: IF filter finished at 21:39:22.070 Status: printing done 'papowell@h4+26681' at 21:39:22.070 Status: accounting at end at 21:39:22.070 Status: finished 'papowell@h4+26681', status 'JSUCC' at 21:39:22.070 Status: subserver pid 26683 exit status 'JSUCC' at 21:39:22.072 Status: lp@h4.private: job 'papowell@h4+26681' printed at 21:39:22.072 Status: job 'papowell@h4+26681' removed at 21:39:22.085 h4: {168} % more /tmp/lp TESTF /tmp/testf -Apapowell@h4+26681 -CA -D2000-04-11-21:39:21.877 \ -Ff -Hh4.private -J/tmp/hi -Lpapowell -Plp -Qlp -aacct -b3 \ -d/var/tmp/LPD/lp -edfA026681h4.private -f/tmp/hi -hh4.private \ -j026681 -kcfA026681h4.private -l66 -npapowell -sstatus \ -t2000-04-11-21:39:21.000 -w80 -x0 -y0 acct ENV USER=papowell LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib HOME=/home/papowell PRINTCAP_ENTRY=lp :force_localhost :filter=/tmp/testf :lp=/var/tmp/lp :sd=/var/tmp/LPD/lp PS1=$ OPTIND=1 PS2=> SPOOL_DIR=/var/tmp/LPD/lp LOGNAME=papowell CONTROL=Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+15850 D2000-04-26-18:13:55.505 Qlp N/tmp/hi fdfA015850h4.private UdfA015850h4.private PATH=/bin:/usr/bin SHELL=/bin/sh LOGDIR=/home/papowell IFS= PRINTER=lp LEADER test Test TRAILER The cp command clears out the /tmp/lp file we are using as a dummy output device. The lpr command prints the /tmp/hi file and the lpq -llll command shows the status information. The status information now contains the line that the testf script wrote to lpd server captures filter As we see from the lpq status, lpd passes a large number of command line options to our filter. These options and their meanings are discussed in detail in Filter Command Line Options and Environment Variables. We will discuss these in more detail in the next section. If we look at the /tmp/lp file, we see the command line options and values of the shell variables. For a full discussion of the environment variables passed to a filter see Filter Command Line Options and Environment Variables. The more interesting environment variables include PRINTCAP_ENTRY - the printcap entry for this printer, CONTROL - the control file, and HF - the hold file. Control Files and Filter Options When you transfer a print job the control file that contains information about the job and data files that contain the information to be printed. Here is sample control file: Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+15850 D2000-04-26-18:13:55.505 Qlp N/tmp/hi fdfA015850h4.private UdfA015850h4.private Lines starting with upper case letters contain job information such as the user who submitted the job. Lines starting with lower case letters indicate the data file to be printed and the corresponding format. For full details about the exact format of the control file see Job Files. shows the correspondence between lines in the control file and U indicates a data file is in a job and is present to meet RCF1179 and vintage print spooler requirements. Filter Options Control File Filter Option Purpose or Value -PPrinter Print queue name - printcap information H -HHost Host Name P -nUser User Login Name of job originator J -JJob name lpr -J option or file name C -CClass Print class (lpr -C option) L -LBanner Banner page requestA -AJobid Job Id D -DDate Date or time information Q -QQueue Original Print queue job was sent to N -NFilename Filename f,l,p,... -Ff Datafile format U Datafile (historical)
When a print filter processes these jobs the values in the control file are passed on the command line as options starting with upper case letters: /tmp/testf -Apapowell@h4+26681 -CA \ -D2000-04-11-21:39:21.877 -Ff -Hh4.private .... Sometimes we want to pass only a small subset of these command line options to a filter or provide them in a specific order in order to be compatible with legacy print filters. &LPRng; provides several different ways to do this and we will explore how to control command line options. If the filter entry starts with -$, this suppresses the automatic addition of command line options; we can then add our own options to the command line. Modify the printcap entry to have the following form: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter= -$ /tmp/testf '$P' $0P -X$-P ${lp} G\072 or \: Lets print our /tmp/hi test file and then look at the lpq status: h4: {169} % cp /dev/null /tmp/lp h4: {170} % lpr /tmp/hi h4: {171} % lpq -llll Printer: lp@h4 .... Status: IF filter msg - 'TESTF /tmp/testf -Plp -P lp -Xlp \ -Ylp /tmp/lp G: or :' at 01:20:21.560 The -$ suppresses the adding the default literals to the filter command line. You can pass specific options using $X; if the option has a non-null value then it will be expanded in the following format: Option Value Expansion $X -X<value> $'X -X'<value>' $0X -X '<value>' $-X <value> $'-X '<value>' ${X} control file X option value $'{X} control file X option value (in quotes) ${name} printcap option value if value nonzero length $'{name} printcap option value if value nonzero length in quotes \nnn single printable character $* all options in control file expanded using $X Command line options can be grouped and passed as a single argument by enclosing them in single or double quotes. You should be aware that &LPRng; has an extremely primitive way of handling quotes. When the /bin/sh -c parameter is not used, the the command line is broken on spaces and each unit is passed as an individual argument. If the first character after a space is a quote (single or double), the next quote is found, and then entire element is then used as a single parameter. Substitution of $X parameters is then done. As a special case, when you have a $0X, this causes a split and all of the string previous and including the -X flag is passed as a single option and all of the option value and following are passed as another option. If the result of the expansion is a zero length parameter then it is removed from the parameter list. When the /bin/sh -c is used the command line is not broken, and all non-empty option values are enclosed in single quotes. The ${name} option is used to pass a printcap option value. For example, you can pass the value of the printcap option form as shown below. You can experiment with this by using the /tmp/testf filter and printcap shown below. printcap: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=/tmp/testf -F ${form} :form=payroll h4: {172} % cp /dev/null /tmp/lp h4: {173} % lpr /tmp/hi h4: {174} % lpq -llll Printer: lp@h4 ... Status: IF filter msg - 'TESTF /tmp/testf -F payroll' at 09:55:31.276 ... If we have a legacy print filter that was originally written for the BSD print spooler, then we may find that it requires a small number of command line options in a very specific order. We can use the :bkf (BSD Kompatible Filter or BacKwards compatible Filter) flag to pass suitable options. Modify the printcap entry to have the following form: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=/tmp/testf :bk Lets print our /tmp/hi test file and then look at the lpq status: h4: {175} % cp /dev/null /tmp/lp h4: {176} % lpr /tmp/hi h4: {177} % lpq -llll Printer: lp@h4 .... Status: IF filter msg - 'TESTF /tmp/testf -Plp -w80 -l66 \ -x0 -y0 -Ff -Lpapowell -J/tmp/hi -CA -n papowell \ -h h4.private acct' at 08:07:46.583 Finally, there are times when we would like the print filter to be a simple shell command or to chain several programs together in a simple pipeline. While this is possible using a print filter, you can also do this in the filter specification. If your filter specification starts with a parenthesis (() or contains the IO redirection for pipeto (|), input redirection (<), or output redirection (>) then the lpd server will use the :shell configuration option value (default /bin/sh) and execute it using: ${shell} -c "( ${if} )" If this is done, then no command line options are added to the command. However, expansion of $X parameters are still done. Modify the printcap entry to have the following form: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=(echo "PREAMBLE"; /tmp/testf; echo "APPENDIX") Lets print our /tmp/hi test file and then look at the lpq status: h4: {178} % cp /dev/null /tmp/lp h4: {179} % lpr /tmp/hi h4: {180} % lpq -llll Printer: lp@h4 .... Status: printing data file 'dfA018881h4.private', size 3, \ IF filter 'echo' at 09:22:11.476 Status: IF filter msg - 'TESTF /tmp/testf' at 09:22:11.510 Status: IF filter finished at 09:22:11.514 If we examine the /tmp/lp file we find: PREAMBLE TESTF /tmp/testf ENV USER=papowell LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib ... PRINTER=lp LEADER hi TRAILER APPENDIX As we expected, no options were passed on the command line. If the printcap is modified to have the following contents, then you will see: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=(echo "PREAMBLE"; /tmp/testf $*; echo "APPENDIX") h4: {181} % lpr /tmp/hi h4: {182} % lpq -llll Printer: lp@h4 .... Status: IF filter msg - 'TESTF /tmp/testf -Apapowell@h4+18941 \ -CA -D2000-04-29-09:27:30.700 -Ff -Hh4.private -J/tmp/hi \ -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \ -edfA018941h4.private -f/tmp/hi -hh4.private -j018941 \ -kcfA018941h4.private -l66 -npapowell -sstatus \ -t2000-04-29-09:27:30.864 -w80 -x0 -y0 acct' at 09:27:30.879 Using the shell invocation is especially useful when you may have a parameter that has an empty string value, and need to pass this as a command line parameter. Modify the /tmp/testf filter, the printcap, and execute the following commands: printcap: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=( /tmp/testf -F '${form}' ) :form= #!/bin/sh # /tmp/testf - test filter for LPRng PATH=/bin:/usr/bin; export PATH echo TESTF $0 "$@" >&2 echo TESTF $0 "$@" while test $# -gt 0 ; do echo "PARM '$1'"; shift; done echo LEADER /bin/cat echo TRAILER exit 0 h4: {183} % cp /dev/null /tmp/lp h4: {184} % lpr /tmp/hi h4: {185} % lpq -llll Printer: lp@h4 ... Status: IF filter msg - 'TESTF /tmp/testf -F' at 09:59:27.365 h4: {186} % more /tmp/lp TESTF /tmp/testf -F PARM '-F' PARM '' LEADER hi TRAILER As you can see, there are $'{form} and using the :filter=(...) form. Filter Environment Variables In this section we will look further at the environment variables passed to the filter. We printed the shell variable values for the filter at the start of the file: h4: {187} % cat /tmp/lp /tmp/testf -Plp -P lp -Xlp -Ylp /tmp/lp G:' at 01:20:21.560 ENV CONTROL=Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+105 D2000-04-12-15:27:26.662 Qlp N/tmp/hi fdfA105h4.private UdfA105h4.private HF=H=h4.private P=papowell J=/tmp/hi C=A L=papowell A=papowell@h4+105 D=2000-04-12-15:27:26.662 Q=lp transfername=cfA105h4.private datafiles=N=/tmp/hi,transfername=fdfA105h4.private, HOME=/home/daemon LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib LOGDIR=/home/daemon LOGNAME=daemon OPTIND=1 PATH=/bin:/usr/bin:/usr/local/bin PRINTCAP_ENTRY=lp :force_localhost :filter=/tmp/testf :lp=/tmp/lp :sd=/tmp/LPD/lp PRINTER=lp PS1=$ PS2=> SHELL=/bin/sh SPOOL_DIR=/tmp/LPD/lp USER=daemon LEADER hi TRAILER The HOME, USER, SHELL, PS1, and PS2 variables are usually set by the shell, and are reflect the information for the UID of the user running the shell. The PATH and LP_LIBRARY_PATH are set by the lpd server to values specified in the printcap or configuration information. It is recommended that users set these to site specific values if the defaults are not suitable for their sites. The lpd server sets the Using Command Line and Printcap Options In Filters One of the problems commonly encountered problem in writing a filter is getting the command line values. The UNIX POSIX Standard provides a C Language getopt function that can be used for command line options, and some, but not all shell implementations have a corresponding shell getopt function. Also, many times it would be useful to get the values of the printcap options. These could be used to specify options or operations that are not easily done by passing command lines. Observe that all the command line options are single letters. If we set the shell variables to the corresponding option value, then we could access them by using $x, where x is the option letter. There is an exception to this rule, which is the -c command line literal, which for various historical and compatibility reasons does not take a value. But if it is present, we might as well assign it the value 1. Observe that by convention all printcap options have lowercase names of two or more letters, and that all environment variables have all upper case letters. If we set shell variables with the corresponding printcap entry values, then we can access them using $literal. If we need to create a local shell variable for use, we can use mIxEd case and not have a conflict. The decode_args_with_sh script which is in the UTILS directory of the &LPRng; distribution follows these conventions and sets the appropriate shell variables. We have also include a bit of code that will extract the control file control line values and put them into variables as well. Save the current /tmp/testf filter file in /tmp/testf.old and replace it with the following: #!/bin/sh # this is an example of how to use /bin/sh and LPRng # to get the command line and printcap option values # and set shell variables from them # Note that we use a couple of variables #PATH=/bin:/usr/bin Args="" vAr="" vAlue="" vAls="" iI="" Tf="" Debug=1 if -n $Debug ; then set >/tmp/before fi Args="$@" if -n $Debug ; then echo "$@" >>/tmp/before fi while expr "$1" : '-.*' >/dev/null ; do vAr=`expr "$1" : '-\(.\).*'`; vAlue=`expr "$1" : '-.\(.*\)`; case "$vAr" in - ) break;; c ) c=1;; [a-zA-Z] ) if test "X$vAlue" = "X" ; then shift; vAlue=$1; fi; eval $vAr='$vAlue'; #setvar $vAr "$vAlue" ;; esac; shift; done # set shell variables to the printcap options # flag -> flag=1 # flag@ -> flag=0 # option=value -> option='value' # setpcvals () { while test "$#" -gt 0 ; do iI=$1 if expr "$iI" : " *\:" >/dev/null ; then vAr=`expr "$iI" : " *\:\([^=][^=]*\)=.*"`; vAlue=`expr "$iI" : " *\:[^=][^=]*=\(.*\)"`; if test "X$vAr" = "X" ; then vAr=`expr "$iI" : " *:\(.*\)@"`; vAlue=0; fi if test "X$vAr" = "X" ; then vAr=`expr "$iI" : " *:\(.*\)"`; vAlue=1; fi if test "X$vAr" != "X" ; then eval $vAr='$vAlue'; #setvar $vAr "$vAlue" fi else vAr=`expr "$iI" : " *\([^|][^|]*\).*"`; if test "X$vAr" != "X" ; then eval Printer="$vAr" fi fi; shift done } # set shell variables to the printcap options # flag -> flag=1 # flag@ -> flag=0 # option=value -> option='value' # setcontrolvals () { while test "$#" -gt 0 ; do iI=$1 vAr=`expr "$iI" : " *\([A-Z]\).*"`; vAlue=`expr "$iI" : " *[A-Z]\(.*\)"`; if test "X$vAr" != "X" ; then eval $vAr='$vAlue'; #setvar $vAr "$vAlue"; fi; shift done } Tf=$IFS IFS=" " setpcvals $PRINTCAP_ENTRY setcontrolvals $CONTROL IFS=$Tf # # restore argument list set -- $Args Args="" vAr="" vAlue="" vAls="" iI="" Tf="" if test -n "$Debug" ; then set >/tmp/after echo "$@" >>/tmp/after diff /tmp/before /tmp/after fi /bin/cat exit 0 Lets print our /tmp/hi test file and then look at the results in /tmp/lp: h4: {188} % cp /dev/null /tmp/lp h4: {189} % lpr /tmp/hi h4: {190} % more /tmp/lp 0a1 > e=dfA021771h4.private 2a4,6 > l=66 > s=status > L=papowell 10a15,17 > j=021771 > C=A > J=/tmp/hi 12a20 > a=acct ... 33a58 > Printer=lp ... hi As we see from the output, shell variables have the values of our command line and printcap options. It is left as an exercise for the reader to add the necessary export statements to cause these values to be exported to subshells. It is not recommended that a wholesale export of the shell variables be done, but only selected ones. The paranoid and security minded reader will see some possible security problem with this script. The eval $vAr='$vAlue' command sets the value of the shell variable $vAr to the value $vAlue. The $vAr variable is always taken from either a single letter or is the name of an option in the printcap file. Clearly the printcap file must not be modifiable by users, and should have the same security considerations as any other system configuration file. The values of the $vAlue are taken directly from the control file, whose contents are under the control of the originator of the print job request. For this reason &LPRng; takes the rather brutal step of sanitizing the control file. Only alphanumerics or a character in the list @/:()=,+-%_ are used in the control file; all others replaced by the underscore (lpd user specified in the lpd.conf configuration file. The following is an example of how to extract the same information in Perl: #!/usr/bin/perl eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if $running_under_some_shell; # this emulates #! processing on NIH machines. # (remove #! line above if indigestible) use Getopt::Std; my(%args,%options); # get the arguments getopt( "a:b:cd:e:f:g:h:i:j:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:" . "A:B:C:D:E:F:G:H:I:J:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:", \%args ); # set :key=value -> $option{$key}=$value # set :key@ -> $option{$key}="0" # set :key -> $option{$key}="1" map { if( m/^\s*:([^=]+)=(.*)/ ){ $options{$1}=$2; } elsif( m/^\s*:([^=]+)\@$/ ){ $options{$1}="0"; } elsif( m/^\s*:([^=]+)/ ){ $options{$1}="1"; } elsif( m/^\s*([^|]+)/ ){ $options{"Printer"}=$1; } } split( "\n", $ENV{'PRINTCAP_ENTRY'}); # get the control file entries map { if( m/^\s*([A-Z])(.*)/ ){ $options{$1}=$2; } elsif( m/^\s*([a-z])/ ){ $options{'Format'}=$1; } } split( "\n", $ENV{'CONTROL'}); The Perl Getopt::Std routine parses the command line options and puts their values in the %args hash variable where they can be accessed using $args{'x'}. Similarly, the map and split functions process the PRINTCAP_ENTRY and CONTROL environment variable and set %options with the printcap entry options and the values from the control file. The map function could be replaced by a foreach loop, but this is Perl: There is more than one way to do it and no tutorial would be complete without at least one mind stretching example that has the reader reaching for the reference manual. Filter Exit Codes The lpd server uses the exit code of the filter to determine if the filter was successful or unsuccessful. The Filter Exit Codes section discusses these values in detail, but here are the most important: 0 - JSUCC A JSUCC exit code indicates that the filter was successful in doing its work. 1 - JFAIL A JFAIL exit code indicates that the filter was unsuccessful in doing its work, possibly due to a transient condition such as out of paper, printer jam, etc., but an additional attempt might be successful. Usually the lpd server will try at most send_try attempts before giving up. 2 - JABORT A JABORT exit code indicates that the filter was unsuccessful in doing its work and has detected a condition that would make it impossible to print the job. In addition, the printer may require administrative attention, and the print queue operation may need to be suspended until the problem is rectified. 3 - JREMOVE The JREMOVE exit code will cause the job to be removed from the print queue. 6 - JHOLD The JHOLD exit code will cause the job to be temporarily prevented from printing until release by the lpc release command. Other Values Usually any other value, including exit due to a signal, is treated as a JABORT exit, and the same action is taken. It should be obvious that the filter exit code is very important, and that care needs to be taken to return the correct value. Job Formats and Filter Selection In the previous sections we discussed how a print filter was executed and how it could be used. Now we will look at how the lpd spooler chooses a print filter program. Let us re-examine our example print job control file: Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+105 D2000-04-12-15:27:26.662 Qlp N/tmp/hi fdfA105h4.private UdfA105h4.private Each data file for a print job has a name with the format dfXnnnh4.private. The df is used to indicate that the file is a data file, and the remainder is a unique name for the file in the job. The X part of the name must be an upper or lower case letter, setting a limit of 52 different files in a single print job. The fdfA105h4.private line in the control file specifies that we are to print the job using the filter for the f format; the file printing information consists of the format assigned to the file and the name of the file. In the legacy BSD print spoolers, this format was used to select the print filter to be used. &LPRng; has expanded this by providing a Job Formats and Filter Selection Control File Line Printcap Option For Filter Filter Command Line (default) fdfAnnn :if=/path /path -Ff ... -b or -l ldfAnnn :if=/path /path ... -Ff -c -p pdfAnnn :if=/path pr | format f filter -c, -d, -n, -r, -t, -v, -FX XdfAnnn :Xf=/path ... /path -FX any format XdfAnnn :filter=/path ... /path -FX
shows the rather baroque relationship between the format options specified by the lpr command and the way that lpd uses them. The reason for this complexity lies in the various implementations and variations that occurred during the development and deployment of the original BSD print spooling software. Here is the complete, arcane, and baroque set of rules that are used to select and print filters. The default format used by f; unless some other format is specified this is used. The lpr -b and lpr -l (lpd to do as little processing as possible of this file before printing it. -c filter command line flag to be used as well. The The lpr -p (pretty-print literal) selects lpd is supposed to use the program specified by the :pr printcap option to format the file and then process the output of this program according to format the lpr -FX allows you to explicitly specify format X where If a filter is not specified for the format then the default filter specified by :filter=/path filter is used, and if there is no default, then the output is sent directly to the output device. If the Some Xf options have pre-assigned meanings and cannot be used for filter selection. Printcap Option Purpose Printcap Option Purpose :af=/path Accounting File :ff=formfeed Form Feed String :if=/path filter (l,b,p,f formats) :filter=/path Default filter :lf=/path Log file :of=/path OF filter :sf suppress form feed between job files The :of filter is a special case and is used for banner printing and accounting purposes. See OF Filter for details.
Job File Format Conversion with Filters One of the major problems that face new users to UNIX printing is when they have a printer that has a proprietary print job format such as the HP DeskJet series of printers. The solution to this problem is quite simple: generate your output in PostScript, and then use the GhostScript program to convert the GhostScript output to a format compatible with your printer. lp:filter=/usr/local/lib/filters/myfilter:... /tmp/myfilter: #!/bin/sh /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \ -sOutputFile=- - && exit 0 exit 2 This simple tutorial example suffers from some serious problems. If you accidentally send a non-PostScript file to the printer GhostScript will detect this and exit with an error message but only after trying to interpret the input file as PostScript. If the input file was a text file, this can result in literally thousands of error messages and hundreds of pages of useless output. In order to make a more robust filter we need to meet the following minimum requirements: The file type should be determined, and only files that are PostScript should be passed to GhostScript. We may have some conversion routines that can convert files into PostScript files and then we can send them to GhostScript for raster conversion. If we cannot convert a file, then we should simply terminate the printing and cause the spooler to remove the job. The http://www.debian.org) installed and in use on your system. The magicfilter developed by H. Peter Anvin http://www.debian.org is distributed with Debian Linux. The apsfilter by Andreas Klemm http://www.freebsd.org/~andreas/index.html is also widely used, although now most of its functionality is directly available in &LPRng;. Finally, the a2ps (Ascii to PostScript) converter by Akim Demaille and Miguel Santana is available from www-inf.enst.fr/~demaille/a2ps. This package provides a very nice set of facilities for massaging, mangling, bending, twisting, and being downright nasty with text or other files. Simple Filter with File Format Detection Since this is a tutorial, we will demonstrate a simple way to make your own multi-format print filter, and provide insight into how more complex filters work. The file utility developed by Ian F. Darwin uses a database of file signatures to determine what the contents of a file are. For example: h4: {191} % cd /tmp h4: {192} % echo hi >hi h4: {193} % gzip -c hi >hi.gz h4: {194} % echo "%!PS-Adobe-3.0" >test.ps h4: {195} % gzip -c test.ps >test.ps.gz h4: {196} % file hi hi.gz test.ps test.ps.gz hi: ASCII text hi.gz: gzip compressed data, deflated test.ps: PostScript document text conforming at level 3.0 test.ps.gz: gzip compressed data, deflated h4: {197} % file - <test.ps standard input: PostScript document text conforming at level 3.0 If we are given a file, we can now use #!/bin/sh # set up converters gs="/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \ -sOutputFile=/dev/fd/3 - 3>&1 1>&2" a2ps="/usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o-" decompress="" # get the file type type=`file - | tr A-Z a-z | sed -e 's/ */_/g'`; echo TYPE $type >&2 case "$type" in *gzip_compressed* ) decompress="gunzip -c |" compressed="compressed" ;; esac # we need to rewind the file perl -e "seek STDIN, 0, 0;" if test "X$decompress" != "X" ; then type=`$decompress head | file - | tr A-Z a-z | sed -e 's/ */_/g'`; echo COMPRESSED TYPE $type >&2 # we need to rewind the file perl -e "seek STDIN, 0, 0;" fi case "$type" in *postscript* ) process="$gs" ;; *text* ) process="$a2ps | $gs" ;; * ) echo "Cannot print type $compressed '$type'" >&2 # exit with JREMOVE status exit 3 ;; esac # in real life, replace 'echo' with 'exec' echo "$decompress $process" # exit with JABORT if this fails exit 2 Copy this to the /tmp/majik file, and give it 0755 (executable) permissions. Here is an example of the output of the script: h4: {198} % /tmp/majik <test.ps.gz TYPE standard_input:_gzip_compressed_data,_deflated... COMPRESSED TYPE standard_input:_postscript_document_level_3.0 gunzip -c | /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \ -sOutputFile=/dev/fd/3 - 3>&1 1>&2 h4: {199} % /tmp/majik </tmp/hi TYPE standard_input:_ascii_text /usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o- \ | /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \ -sOutputFile=/dev/fd/3 - 3>&1 1>&2 The first part of the script sets up a standard set of commands that we will use in the various conversions. A full blown package for conversion would use a database or setup file to get these values. We then use the file utility to determine the input file type. The output of the file utility is translated to lower case and multiple blanks and tabs are removed. We use a simple shell case statement to determine if we have a compressed file and get a decompression program to use. We reapply the file utility to the decompressed file (if it was compressed) and get the file type. Finally we use another case statement to get the output converter and then we run the command. For tutorial purposes, we use an echo rather than an exec so we can see the actual command, rather than the output. Just for completeness, here is majikperl: #!/usr/bin/perl eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if $running_under_some_shell; # this emulates #! processing on NIH machines. # (remove #! line above if indigestible) my($gs) = "/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \ -sOutputFile=/dev/fd/3 - 3>&1 1>&2"; my($a2ps)="/usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o-"; my($decompress,$compressed,$process,$type); $decompress=$compressed=$process=$type=""; # get the file type $type = ` file - `; $type =~ tr /A-Z/a-z/; $type =~ s/\s+/_/g; print STDERR "TYPE $type\n"; ($decompress,$compressed) = ("gunzip -c |", "gzipped") if( $type =~ /gzip_compressed/ ); print STDERR "decompress $decompress\n"; unless( seek STDIN, 0, 0 ){ print "seek STDIN failed - $!\n"; exit 2; } if( $decompress ne "" ){ $type = ` $decompress file - `; $type =~ tr /A-Z/a-z/; $type =~ s/\s+/_/g; print STDERR "COMPRESSED TYPE $type\n"; unless( seek STDIN, 0, 0 ){ print "seek STDIN failed - $!\n"; exit 2; } } $_ = $type; if( /postscript/ ){ $process="$gs"; } elsif( /_text_/ ){ $process="$a2ps | $gs" ;; } else { print STDERR "Cannot print $compressed '$type'" >&2; # JREMOVE exit 3; } exec "$decompress $process"; print "exec failed - $!\n"; exit 2; The <application/ifhp/ Filter The ifhp Print Filter is the companion print filter supplied with &LPRng; and is normally installed together with the &LPRng; software. Ifhp supports a wide range of PostScript, PCL, text, and raster printers, and can be configured to support almost any type of printer with a stream based interface. It provides diagnostic and error information as well as accounting information. It recognizes a wide range of file types by using the file utility and the pattern matching technique demonstrated in the previous section, and can do selective conversions from one format to others. The PostScript and PCL printer job languages are supported by most printer manufacturers. However, in order to have a job printed correctly the following steps must be taken. The printer must be put into a known If accounting is being done, then the printer accounting information must be obtained and recorded. See Accounting for more information about &LPRng; support for accounting. The file to be printed must be checked to see if it is compatible with the printer, and if not, a format conversion program invoked to convert it to the required format. If the user selects a set of printer specific options such as landscape mode, duplex printing, multiple copies, or special paper, the appropriate commands must be sent to the printer to select these options. The file must be transferred to the printer and the printer is monitored for any error conditions. Any required end of job commands are sent to the printer, and the printer monitored for error conditions while the job finishes printing. If accounting is being done, the printer accounting information such as page count and time used must be obtained and recorded. See Accounting for more information about &LPRng; support for accounting. The ifhp filter uses the ifhp.conf configuration file to determine the actions and commands appropriate for various models of printers. See the ifhp documentation for details about the format and contents of this file. This file contains entries for a large number of PostScript, PJL, and other printers. The default printer used by We will demonstrate how to add the ifhp filter to your printcap entry. Find the path to the ifhp filter using the find command as we did in the previous exercise. Modify the printcap as shown below and use lpc lpd to restart lpd. lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :ifhp=model=default # modify the path to ifhp appropriately :filter=/usr/local/libexec/filters/ifhp Now print the /tmp/hi and then display /tmp/lp using a text editor such as vi or emacs that shows control characters: h4: {200} % cp /dev/null /tmp/lp h4: {201} % lpr /tmp/hi h4: {202} % vi /tmp/lp ^[%-12345X@PJL @PJL JOB NAME = "PID 405" DISPLAY = "papowell" @PJL RDYMSG DISPLAY = "papowell" @PJL USTATUSOFF @PJL USTATUS JOB = ON @PJL USTATUS DEVICE = ON @PJL USTATUS PAGE = ON @PJL USTATUS TIMED = 10 @PJL ENTER LANGUAGE = PCL ^]E^]&^]&k2G^]&s0C^]&l0O^]9^](s0P^](s10.00H^](s4099Thi ^]E^]%-12345X@PJL @PJL RDYMSG DISPLAY = "papowell" @PJL EOJ NAME = "PID 405" @PJL USTATUSOFF @PJL USTATUS JOB = ON @PJL USTATUS DEVICE = ON @PJL USTATUS PAGE = ON @PJL USTATUS TIMED = 10 @PJL RDYMSG DISPLAY = "" ^[%-12345X The output now contains all of the control sequences and setup codes needed to print a text file on the default printer. The <application/:ifhp=/ Options Option Purpose Use Printer does or does not provide status information Printer does or does not indicate ready to operate at start of job, or use PostScript or PJL code sequence to determine if printer is ready. Printer does or does not have pagecount support, or use PostScript or PJL code sequence to determine pagecount. Wait or do not wait for end of job, or send PostScript or PJL code sequence to have printer report end of job.
The The The The Finally, For a complete list of all of the
The Jaggies - LF to CR-LF Conversion With lpf When printing to vintage hard copy devices or to printers that support a text mode, many UNIX users discover that their output suffers from a case of the jaggies. Input file: This is a nice day Output: This is a nice day UNIX systems terminate lines with a single NL (new line) character. This causes the printer to move down one line on the printing page but does not change its horizontal position and print the next character at the left margin. This is done by using the CR (carriage return) character. You need to convert the single NL to a CR-LF combination and the lpf filter supplied with &LPRng; does this. First, locate the lpf filter. You can find it by using the command: h9: {160} % find /usr/ -type f -name lpf -print /usr/libexec/lpr/lpf We will first see what the output is like without lpf, and then see what it does. Modify the lp printcap entry as shown below and then use lpc restart to restart the lpd server. lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp Print a file and view the output using the following commands. If you do not have the od (octal dump) program, try using hexdump or some other appropriate program that displays the numerical contents of the file. h4: {203} % cp /dev/null /tmp/lp h4: {204} % lpr /tmp/hi h4: {205} % od -bc /tmp/lp 0000000 150 151 012 h i \n 0000003 Now we will use the lpf filter. Modify the printcap as shown below and use lpc reread to cause lpd to reread the configuration information. lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp # modify the path to lpf appropriately :filter=/usr/local/libexec/filters/lpf Now reprint the file: h4: {206} % cp /dev/null /tmp/lp h4: {207} % lpr /tmp/hi h4: {208} % od -bc /tmp/lp od -bc /tmp/lp 0000000 150 151 015 012 h i \r \n 0000004 As you see, lpf changes the CR-LF sequence. Store and Forward Spool Queues Up to now we have assumed that associated with each spool queue is a hardware printing device. When a job is sent to the spool queue the lpd server will take actions to filter it and then send it to the printing device. However, we can also have store and forward spool queues. These queue act to simply buffer jobs and then forward them to another spooler. The following printcap entry shows how you can specify a store and forward queue. # store and forward using classical BSD :rm:rp lp:rp=pr:rm=host :sd=/var/spool/lpd/%P :server # store and forward using &LPRng; lp=pr@host lp:lp=pr@host :sd=/var/spool/lpd/%P :server The legacy :rp (remote printer) and :rm (remote host) format can be used to specify the print queue and destination host for jobs sent to this queue. The &LPRng; :lp=pr@host format serves the same function, and has precedence over the :rm:rp form. Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. lp:force_localhost lp:server :sd=/var/spool/lpd/%P :lp=lp2@localhost lp2:force_localhost lp2:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 Execute the following commands to print the /tmp/hi file and observe the results: h4: {209} % lpr /tmp/hi h4: {210} % lpq -lll Printer: lp@h4 (dest lp2@localhost) Queue: no printable jobs in queue Status: sending control file 'cfA029h4.private' \ to lp2@localhost at 09:39:57.719 Status: completed sending 'cfA029h4.private' \ to lp2@localhost at 09:39:57.724 Status: sending data file 'dfA029h4.private' \ to lp2@localhost at 09:39:57.727 Status: completed sending 'dfA029h4.private' \ to lp2@localhost at 09:39:57.925 Status: done job 'papowell@h4+29' transfer \ to lp2@localhost at 09:39:57.926 Status: subserver pid 29031 exit status 'JSUCC' at 09:39:57.953 Status: lp@h4.private: job 'papowell@h4+29' printed at 09:39:57.961 Status: job 'papowell@h4+29' removed at 09:39:57.993 Printer: lp2@h4 Queue: no printable jobs in queue Status: no banner at 09:39:58.054 Status: printing data file 'dfA029h4.private', size 3 at 09:39:58.054 Status: printing done 'papowell@h4+29' at 09:39:58.054 Status: accounting at end at 09:39:58.054 Status: finished 'papowell@h4+29', status 'JSUCC' at 09:39:58.054 Status: subserver pid 29033 exit status 'JSUCC' at 09:39:58.056 Status: lp2@h4.private: job 'papowell@h4+29' printed at 09:39:58.056 Status: job 'papowell@h4+29' removed at 09:39:58.069 As we see from the status, our job was sent to the lp spool queue first. It was store there and then the lpd server transferred it to the lp2 spool queue, where it was printed to the file /tmp/lp2. Filtering Job Files In Transit One of the major problems with store and forward operation is that the destination spool queue may not actually be a spool queue - it can be a printer. Many network printers provide an RFC1179 compatible network interface and act, for job forwarding purposes, like a host running a limited capability BSD print spooler. By adding a filter to the printcap information we can modify the format of a job file so that it is compatible with the destination printer. Edit the printcap and /tmp/testf files so they have the contents indicated below, give /tmp/testf executable permissions, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. # set /tmp/testf to contain the following # and chmod 755 /tmp/testf #!/bin/sh echo TESTF $0 $@ /bin/cat exit 0 # printcap lp:force_localhost lp:server :sd=/var/spool/lpd/%P :lp=lp2@localhost :filter=/tmp/testf :bq_format=ffl lp2:force_localhost lp2:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 Execute the following commands to print the /tmp/hi file and observe the results: h4: {211} % lpr /tmp/hi h4: {212} % lpq -llll h4: {213} % lpq -llll Printer: lp@h4 (dest lp2@localhost) Queue: no printable jobs in queue Status: no banner at 09:55:53.681 Status: printing data file 'dfA086h4.private', size 3, \ IF filter 'testf' at 09:55:53.683 Status: IF filter finished at 09:55:53.713 Status: printing done 'papowell@h4+86' at 09:55:53.714 Status: sending job 'papowell@h4+86' to lp2@localhost at 09:55:53.734 Status: connecting to 'localhost', attempt 1 at 09:55:53.735 Status: connected to 'localhost' at 09:55:53.739 Status: requesting printer lp2@localhost at 09:55:53.740 Status: sending control file 'cfA086h4.private' to lp2@localhost at 09:55:53.752 Status: completed sending 'cfA086h4.private' to lp2@localhost at 09:55:53.757 Status: sending data file 'dfA086h4.private' to lp2@localhost at 09:55:53.758 Status: completed sending 'dfA086h4.private' to lp2@localhost at 09:55:53.939 Status: done job 'papowell@h4+86' transfer to lp2@localhost at 09:55:53.940 Status: subserver pid 29088 exit status 'JSUCC' at 09:55:53.980 Status: lp@h4.private: job 'papowell@h4+86' printed at 09:55:53.983 Status: job 'papowell@h4+86' removed at 09:55:53.998 Printer: lp2@h4 Queue: no printable jobs in queue Status: subserver pid 29092 starting at 09:55:54.005 Status: accounting at start at 09:55:54.005 Status: opening device '/tmp/lp2' at 09:55:54.005 Status: printing job 'papowell@h4+86' at 09:55:54.005 Status: no banner at 09:55:54.006 Status: printing data file 'dfA086h4.private', size 298 at 09:55:54.006 Status: printing done 'papowell@h4+86' at 09:55:54.006 Status: accounting at end at 09:55:54.006 Status: finished 'papowell@h4+86', status 'JSUCC' at 09:55:54.006 Status: subserver pid 29092 exit status 'JSUCC' at 09:55:54.008 Status: lp2@h4.private: job 'papowell@h4+86' printed at 09:55:54.008 Status: job 'papowell@h4+86' removed at 09:55:54.020 We have displayed a bit more status information so that we can see what the actions the lp queue carries out. It first processes the job data file using the testf filter and puts the results in a temporary file. Then it sends the contents of the temporary file to the lp2 queue. The lp2 queue receives the converted job file and then prints it to the /tmp/lp2 file in turn. By default, each file in a job is processed by a print file and the processed output is then sent to the destintion as individual job files, each with the format specified by the value of the
Printcap Basics In the previous sections we have used simple printcap entries to define how to set up filters and pass parameters to them. We will now examine the printcap database in more detail. The &LPRng; server and client software gets their configuration information from: Compile time settings which set the default values for the configuration information. A lpd.conf file that contains values that override the compile time defaults. This information can effect the behavior of the lpd server and clients. Printcap entries which have configuration information for individual print queues. The information in the printcap entries for the queue override the lpd.conf and compile time defaults. The system printcap file is read first, followed by the user printcap file. Command line and environment variable values. These can be used to override or select particular configuration information or to select one of a set of options for use. Each print queue or printer has a name which is used to look up the printcap information for the printer. The /etc/printcap file is the default location for the printcap information, although it can also be obtained from database servers, or generated by programs. See the Using Programs To Get Printcap Information section for details. We will use a more complex printcap file to explore how &LPRng; gets the printcap information. Put the following lines in the /etc/printcap file: # client entry lp:tc=.client lp2:tc=.client .client: :lp=%P@localhost :force_localhost lp:server :cm=The Main Print Queue :lp=/tmp/lp :tc=.common lp2:server :cm=The Second Print Queue :lp=/tmp/lp2 :tc=.common .common: :sd=/var/spool/lpd/%P :mx=0 The lpc client command is very useful to see how &LPRng; uses this printcap information: h4: {214} % lpc client Config h4: {215} % lpc client all Config :lpd_port=2000 :printcap_path=/var/tmp/LPD/printcap Names :.client=.client :.common=.common :lp=lp :lp2=lp2 :main=lp All :lp :lp2 Printcap Information lp|main :force_localhost :lp=lp@localhost lp2 :force_localhost :lp=lp2@localhost The lpc client all command shows all of the configuration and printcap information, and is the handiest one for system debugging and diagnostics. The Name information is the names of the printcap entries that have been found in the database and is listed in sorted order. The All are entries that correspond to actual queues or printers and are listed in the order that they appear in the printcap or according to an order specified by the system administrator. (See the for details.) Printcap Processing Format Queue or printer names must start with an alphanumeric character, and contain only alphanumerics, hyphens (-) and underscores (_). To avoid known and nasty problems with sending and receiving print jobs from case sensitive and case insensitive systems, &LPRng; brutally lowercases all printcap entry names and printer names. The printcap file is processed by reading it line by line and composing the individual printcap entries. Each entry has an name and one or more aliases. The entries in the printcap assign values to options. These can have the format: option=string value \n with escapes flag # equivalent to flag=1 flag@ # equivalent to flag=0 option#value # equivalent to option=value An option will have the last value that occurs in the printcap entry. Our example shows the use of the tc (termcap include) facility. The :tc value is a list of printcap entries that should be prefixed to the start of the printcap entry in which it appears. This allows options to be set in the printcap entry which will override the values in the :tc included entry. For convenience, the options are displayed in sorted order. The &LPRng; clients and lpd server may require a different set of options for the same spool queue. The clients require options whose values tell the clients how the contact the lpd server and transfer a print job or query to it. The lpd server needs options that tell it how to either print a job or forward it to another lpd server. The :client or :server option marks a printcap entry as for client or lpd server use only; unmarked entries are used by both server and client. The lpc client command shows the printcap information that the &LPRng; clients would use. For example, here is what the lpd server would use: h4: {216} % lpc server all Config :lpd_port=2000 :printcap_path=/var/tmp/LPD/printcap Names :.client=.client :.common=.common :lp=lp :lp2=lp2 :main=lp All :lp :lp2 Printcap Information lp|main :cm=The Main Print Queue :force_localhost :lp=/tmp/lp :mx=0 :sd=/var/spool/lpd/%P :server lp2 :cm=The Second Print Queue :force_localhost :lp=/tmp/lp2 :mx=0 :sd=/var/spool/lpd/%P :server When we select the server printcap information, we see that the :sd option has been added, and the :lp replaced with new values. We can use the :oh (on this host) option to mark printcap entries for use by a selected set of hosts. For example: lp:oh=10.0.0.0/255.255.255.0,*.private,!10.0.0.10 :lp=%P@10.0.0.10 The :oh option takes a list of IP addresses and masks or glob patterns, and applies these to the IP addresses or list of Fully Qualified Domain Names for the current host. If there is a for at least one IP address or pattern in the list match then the entry is used. An exclamation mark (!) inverts the sense of the match, and the entry is used if the match fails. Finally, we can use the wildcard facility to cause a default printcap entry to be used: lp|*:cm=Wildcard Alias - %P=lp, %Q=wanted :lp=%P@10.0.0.10 *:cm=Wildcard Name- %P=wanted, %Q=wanted :lp=%P@10.0.0.10 The &LPRng; software first searches the printcap information for an exact match. If none is found then it searches for the first wildcard entry that matches the printer name. If the wildcard is used as an alias, then the printcap entry is simply selected for use, with the printer name and queue name selected as shown above. We can also use partial matching as well: lp|lp_* :lp=%P@10.0.0.10 qt|qt_* :lp=%P@10.0.0.12 In the example above the first entry matches Printcap Information From Programs and Databases There many administrators store system information on a database server and having programs or utilities get their configuration information from this server. The use of the database allows easier system administration and also centralizes the administration. Rather than build in the various types of database access, the LPRng software allows the use of programs to obtain printcap information. This not only allows any type of database to be used, but also removes any legal or license restrictions on the redistribution of the actual software. We will use very simple example to show how you can use a program to get printcap information. First, you must configure the &LPRng; software to use a program to get the filter information. This is done by setting a value in the lpc.conf file (usually /etc/lpd.conf or /usr/local/etc/lpd.conf). Copy your lpd.conf file to lpd.conf.bak and then add the following line to the end of the file: printcap_path=|/tmp/getpc h4: {217} % cd /etc h4: {218} % cp lpd.conf lpd.conf.bak h4: {219} % echo 'printcap_path=|/tmp/getpc' >>lpd.conf Next, edit the /tmp/getpc file and set its values as shown below. set /tmp/getpc: #!/bin/sh # /tmp/getpc echo PROG $0 "$@" >>/tmp/trace cat >>/tmp/trace cat <<EOF lp:lp=test@host EOF exit 0 h4: {220} % chmod 755 /tmp/getpc h4: {221} % echo testing | /tmp/getpc -aoption lp:lp=test@host h4: {222} % cat /tmp/trace PROG /tmp/getpc -aoption testing After you have tested the getpc script, use the lpc client all command: h4: {223} % lpc client all Config :lockfile=/var/tmp/LPD/lpd :lpd_port=2000 :printcap_path=|/tmp/getpc Names :lp=lp All :lp Printcap Information lp :lp=test@host h4: {224} % cat /tmp/trace h4: {225} % cat /tmp/trace PROG /tmp/getpc -Pall -aacct -l66 -sstatus \ -t2000-05-05-08:40:51.000 -w80 -x0 -y0 acct all PROG /tmp/getpc -Pall -aacct -l66 -sstatus \ -t2000-05-05-08:40:51.000 -w80 -x0 -y0 acct * As seen from the /tmp/trace file, the getpc program is invoked with the standard filter parameters. The -P command line literal is set to the name of the printcap entry and the name of the entry is written to the filter's -P literal is not set to *, as this has the possibility of opening a security loophole when a shell script parses the filter's command line options. You restore the original lpd.conf file to restore the system to normal operation. h4: {226} % cd /etc h4: {227} % cp lpd.conf.bak lpd.conf When using the program method to return information, special consideration should be given to the all request. If there is not an explicit all entry, then the program should take appropriate steps to enumerate the values in the database, or report that there is a missing all entry to the appropriate administrative authority. User Printcap Information In addition to the system printcap, each user can define a private printcap file that will be read after the system printcap. Users can define &LPRng; client entries and can augment the system printcap information. By default, ${HOME}/.printcap is the the user printcap file. Here is a simple example of a user printcap file. # remote printer - default lp:lp=raw@localhost :ifhp=model=laserjet4 :filter=/usr/local/libexec/filters/ifhp # direct connection to printer over TCP/IP connection lp:lp=10.0.0.5%9100 :direct :ifhp=model=phaser :filter=/usr/local/libexec/filters/ifhp The two examples show how a simple printer definition can be created. The first example shows how to create a simple way to send a file directly to a remote print queue after passing it through a filter. This is usually called The second example is more interesting. Here we do the same thing, but we open a connection to the remote port on a host and send the print job. We do not spool the print job but send it directly. This is called While the user printcap file is read after the system printcap file, the order of printcap entries is modified so that any entries that appeared in the user printcap file will appear before entries in the system printcap file. This allows users to modify the order in which printer entries are displayed. Banner Printing and the OF filter Banner or header pages can be printed at the beginning, end, or both at the beginning or end. The following flags control how and where banners are printed. These flags are listed in order of precedence. :sh Suppress all banner printing or header pages. This prevents any banners from being generated by the lpd print spooler and if present in a client printcap entry, will cause lpr not to put any banner printing information in the control file. Even if this flag is not present, then &LPRng; will not print a banner unless a banner printing program is specified with the :bp, :bs, :be, or :sb option. :ab Print a banner or header page, even if the user has not requested one. The :sh option has precedence over the :ab option. :hl The banner (header) is at the end (last page) of the job. :bs=/... and :be=/... and The :bs and :be options specify that a banner page is to be generated at the start and end of the job respectively, using indicated filter program. If the :hl flag has been set, only the :be will be used. :bp=... If there is no :bs or :be value when printing a banner at the start or end of the job respectively, then use the indicated filter program to generate a banner. :sb and :bl=.... If there is no program specified to generate the banner and the :sb flag is set, send the :bl (banner line) string to the printer. :of=filter A filter used to process banners and other non-job file information. :suspend_of_filter Controls whether the :of filter is suspended or has its input terminated. The pclbanner, psbanner, and lpbanner programs are part of the &LPRng; distribution and are usually installed in the same location as the &LPRng; supported filters. They produce a PCL, PostScript, or text banner respectively. The OF filter (:of=/path) is used to process banner pages and to do any necessary setup to initialize the printer to handle banner pages. This filter has the following unusual behavior: It must be explicitly specified in the printcap file. It is not run by default. If specified, it is started at the beginning of job printing and stays present throughout the entire job printing session. When printing individual files, the :of filter is sent a special suspend yourself two character string, \031\001. This will cause the :of filter to send itself a SIGSUSP (suspend) signal. The :of filter is restarted when any information not part of a print job file, such as the initialization string (:ld option), termination string (:ld option), or form feeds at start (:fo), end (:fq), and between job files (:ff_separator), and when banners are generated by the :bp, :bs, :be, or :sb option and need to be sent to the printer. This rather baroque behavior is mostly historical in origin, and is very much embedded in the existing documentation and methodologies of the BSD print spooling system. Originally, when a printer port was opened, a special device initialization string was sent by the printer port device driver; this usually resulted in an extra page of paper being ejected by the printer. By opening the device once and then keeping it open, the print spooler would avoid the wasted paper. The reason for suspending the :of filter was simply to save the overhead of creating an extra processes. Unfortunately, the :of filter suspension behavior is now a problem rather than a benefit. For example, for many devices to finish printing a page correctly the filter must be closed in order for it to flush buffers and for the low level drivers to properly finish. In order to provide this functionality, the suspend_of_filter@ flag can be used. This will cause the lpd server to close the :of filters input, rather than sending it the suspend string, and to restart a new :of filter process when necessary. Printing from <application/lpr/ Directly To A Device While the most reliable way to print is to send jobs to a print spooler, sometimes it is desirable to print directly to a printer. This method is supported by the special lpr -Y -Phost%port file1 file2 ... Eqivalent to: ( for i in file1 file2 ... ; do $filter $i done ) | tcpip_connection( host%port) lpr -Y -P/dev/lp file1 file2 ... Eqivalent to: ( for i in file1 file2 ... ; do $filter $i done ) >>/dev/lp lpr -Y -P '|/program' file1 file2 ... Eqivalent to: ( for i in file1 file2 ... ; do $filter $i done ) | /program The above examples show how we can use the command line options to send files directly to a printer. You can also create a printcap that will do the same: lp:direct:lp=/tmp/a:remote_support=R Command: lpr -Plp file1 file2 ... Equivalent to: lpr -P/tmp/a -Y file1 file2 ... Example: h4: {228} % lp -P/tmp/a /tmp/hi h4: {229} % cat /tmp/a /tmp/hi hi h4: {230} % lp -Plp /tmp/hi h4: {231} % cat /tmp/a /tmp/hi hi hi The /tmp/pass file with the following contents, and give it executable permissions as shown below: #!/bin/sh # /tmp/pass file echo LEADER cat echo TRAILER exit 0 Execute the following commands to print the /tmp/hi file and observe the results: h4: {232} % cp /dev/null /tmp/a h4: {233} % lpr -P/tmp/a -X /tmp/pass /tmp/hi h4: {234} % cat /tmp/a LEADER hi TRAILER As we see from the example, our filter has processed the input file and added the lp:direct:lp=/tmp/a:filter=/tmp/pass h4: {235} % cp /dev/null /tmp/a h4: {236} % lpr -Plp /tmp/hi h4: {237} % cat /tmp/a LEADER hi TRAILER Moving Jobs From Queue to Queue and Redirecting Queues The lpc move command is used to move jobs in one queue to another queue on an individual basis, while the lpc redirect command redirects all incoming jobs to a new queue. Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. lp:force_localhost lp:server :sd=/var/spool/lpd/%P :lp=lp2@localhost lp2:force_localhost lp2:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 Execute the following commands to print the /tmp/hi file and observe the results: h4: {238} % lpc stop lp lp2 Printer: lp@h4 lp@h4.private: stopped Printer: lp2@h4 lp2@h4.private: stopped h4: {239} % lpr /tmp/hi h4: {240} % lpq -a Printer: lp@h4 (printing disabled) Queue: 1 printable job Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+659 A 659 /tmp/hi 3 08:04:03 Printer: lp2@h4 (printing disabled) Queue: no printable jobs in queue h4: {241} % lpc move lp papowell lp2 Printer: lp@h4 lp: selected 'papowell@h4+659' lp@h4.private: move done h4: {242} % lpq -a Printer: lp@h4 (printing disabled) Queue: no printable jobs in queue Status: job 'papowell@h4+659' removed at 08:19:24.962 Printer: lp2@h4 (printing disabled) Queue: 1 printable job Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+659 A 659 /tmp/hi 3 08:19:24 We first stop the queues so that the jobs will remain in them. We then use the lpc move fromqueue id toqueue command to select a job in the fromqueue and move it to the toqueue. A list of job numbers, job IDs, or glob patterns to match job IDs can be used to select the job. The lpc redirect fromqueue toqueue will cause all incoming jobs to be redirected to the specified queue. You can execute the following commands and observe the results. h4: {243} % lpc redirect lp lp2 Printer: lp@h4 forwarding to 'lp2' lp@h4.private: redirected h4: {244} % lpq -a Printer: lp@h4 (printing disabled) (redirect lp2) Queue: no printable jobs in queue Printer: lp2@h4 (printing disabled) Queue: no printable jobs in queue h4: {245} % lpr /tmp/hi h4: {246} % lpq -a Printer: lp@h4 (printing disabled) (redirect lp2) Queue: no printable jobs in queue Status: job 'papowell@h4+935' removed at 09:08:21.410 Printer: lp2@h4 (printing disabled) Queue: 1 printable job Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+935 A 935 /tmp/hi 3 09:08:21 To turn redirection off, use lpc redirect queue off as shown in the example below: h4: {247} % lpc redirect lp off Printer: lp@h4 forwarding off h4: {248} % lpq Printer: lp@h4 (printing disabled) Queue: no printable jobs in queue Status: job 'papowell@h4+935' removed at 09:08:21.410 Print Job Classes, User Requested Job Priority, and Form Support The &LPRng; software allows users to assign a class name to print jobs using the lpr -Cname option. This causes the lpr command to put the line Cname in the control file. By default, the (upper cased) first letter of the class name is used to assign a user requested priority to the job, with A being the default lowest priority and Z being the highest. The ignore_requested_user_priority printcap option can be used to ignore the user requested priority and jobs will be printed in the normal first-in first-out order. &LPRng; also makes use of the class information to do form support and restrict printing to a specific set of classes. By default the job class information is ignored, but the lpc class command can be used to specify one or more classes (actually glob patterns) to be printed. This facility can be used to do support printing of jobs that require a specific form setup. Here is a simple example of how to use this facility. Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. lp:force_localhost lp:server :sd=/var/spool/lpd/%P :lp=lp2@localhost lp2:force_localhost lp2:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 Execute the following commands to print the /tmp/hi file and observe the results: h4: {249} % lpc class lp red Printer: lp@h4 classes printed 'red' lp@h4.private: class updated h4: {250} % lpq Printer: lp@h4 (classes red) Queue: no printable jobs in queue h4: {251} % lpr /tmp/hi h4: {252} % lpq Printer: lp@h4 (classes red) Queue: no printable jobs in queue Holding: 1 held jobs in queue Server: no server active Rank Owner/ID Class Job Files Size Time holdclass papowell@h4+82 A 82 /tmp/hi 3 09:29:52 h4: {253} % lpr -Cred /tmp/hi h4: {254} % lpq Printer: lp@h4 (classes red) Queue: no printable jobs in queue Holding: 1 held jobs in queue Server: no server active Status: job 'papowell@h4+89' removed at 09:30:13.569 Rank Owner/ID Class Job Files Size Time holdclass papowell@h4+82 A 82 /tmp/hi 3 09:29:52 As seen in the example, we set the queue class to red, and then sent a (default) class A job to the printer. It was not printed, and is listed with holdclass status. We sent another job which was immediately printed. We can change the print queue class at any time, and then new class will then control what jobs are printed. To disable the class selection, use the lpc class queue off command. h4: {255} % lpc class lp off Printer: lp@h4 all classes printed lp@h4.private: class updated Holding and Releasing Jobs The &LPRng; software has a wide range of facilities to hold or temporarily prevent jobs from printing. Jobs can be individually held or all jobs submitted to a queue can be held until released by an operator. Some administrators use the holdall facility and a cron script to cause jobs to be printed at specific times. The lpc holdall command causes all jobs submitted to a queue to be held until released with the lpc release command. The lpc noholdall command disables the holdall operation. Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. lp:force_localhost lp:server :sd=/var/spool/lpd/%P :lp=lp2@localhost lp2:force_localhost lp2:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 Execute the following commands to print the /tmp/hi file and observe the results: h4: {256} % lpc holdall lp Printer: lp@h4 lp@h4.private: holdall on h4: {257} % lpq Printer: lp@h4 (holdall) Queue: no printable jobs in queue h4: {258} % lpr /tmp/hi h4: {259} % lpq Printer: lp@h4 (holdall) Queue: no printable jobs in queue Holding: 1 held jobs in queue Server: no server active Rank Owner/ID Class Job Files Size Time hold papowell@h4+213 A 213 /tmp/hi 3 09:45:05 h4: {260} % lpc release lp 213 Printer: lp@h4 lp: selected 'papowell@h4+213' lp@h4.private: started h4: {261} % lpq Printer: lp@h4 (holdall) Queue: no printable jobs in queue Status: job 'papowell@h4+213' removed at 09:45:22.570 The lpc holdall command causes all jobs to be held. We spool a job, and then use the lpc release command to release the selected job. We disable the holdall operation using the lpc noholdall command. h4: {262} % lpc noholdall lp Printer: lp@h4 lp@h4.private: holdall off You can also use the lpc hold command to select individual jobs in a spool queue to be held. This command is useful if there is a set of jobs which require special handling or printing at a later date. The following example shows how this command is used. We use the lpc stop and lpc start commands to simulate the normal delays in print spooling operations. h4: {263} % lpc stop lp Printer: lp@h4 lp@h4.private: stopped h4: {264} % lpq Printer: lp@h4 (printing disabled) Queue: no printable jobs in queue Status: job 'papowell@h4+495' removed at 10:10:50.629 h4: {265} % lpr /tmp/hi h4: {266} % lpr /tmp/hi h4: {267} % lpq Printer: lp@h4 (printing disabled) Queue: 2 printable jobs Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+459 A 459 /tmp/hi 3 10:40:32 2 papowell@h4+461 A 461 /tmp/hi 3 10:40:34 h4: {268} % lpc hold lp 459 Printer: lp@h4 lp: selected 'papowell@h4+459' lp@h4.private: updated h4: {269} % lpq Printer: lp@h4 (printing disabled) Queue: 1 printable job Holding: 1 held jobs in queue Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+461 A 461 /tmp/hi 3 10:40:34 hold papowell@h4+459 A 459 /tmp/hi 3 10:40:32 In the next example we show how to use the lpc hold command to select and hold an individual job. Then we start the queue and see what happens: h4: {270} % lpc start Printer: lp@h4 lp@h4.private: started h4: {271} % lpq Printer: lp@h4 Queue: no printable jobs in queue Holding: 1 held jobs in queue Server: no server active Status: job 'papowell@h4+461' removed at 10:41:24.873 Rank Owner/ID Class Job Files Size Time hold papowell@h4+459 A 459 /tmp/hi 3 10:40:32 h4: {272} % lpc release lp 459 Printer: lp@h4 lp: selected 'papowell@h4+459' lp@h4.private: started h4: {273} % lpq Printer: lp@h4 Queue: no printable jobs in queue Status: job 'papowell@h4+459' removed at 10:41:39.457 As we see, the held job is not printed until we release it, and then is processed normally. The printcap :ah (autohold) option has the same effect as the lpc holdall command but its actions cannot be disabled by the lpc noholdall command. Load Balance Queues and Printer Pools A Load Balance Queue provides a way to use multiple printers for a single print queue. All jobs are normally sent to the main or load balance queue which then dispatches the jobs to server queues or printers that do the actual printing as they become available. You can also send jobs to the individual server printers if they have special processing or setups required for a particular job. Because all of the server printers are shared by the load balance queue, they are said to be in a printer pool. Edit the printcap file so it have the contents indicated below, create the /tmp/lp2 and /tmp/lp3 files with 0777 permissions, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. # printcap lp:force_localhost lp:server :sd=/var/spool/lpd/%P :sv=lp2,lp3 lp2:force_localhost lp2:server :ss=lp :sd=/var/spool/lpd/%P :lp=/tmp/lp2 lp3:force_localhost lp3:server :ss=lp :sd=/var/spool/lpd/%P :lp=/tmp/lp2 The :sv=... option flags the queue as a load balance queue and lists the queues that are used for load balancing. The :ss=... option flags the queue as a server for a load balance queue and specifies the name of the load balance queue. When a job is sent to the load balance queue the lpd server checks to see which server queues are available and then the first one to become available. Execute the following commands to print the /tmp/hi file and observe the results: h4: {274} % lpq Printer: lp@h4 (subservers lp2, lp3) Queue: no printable jobs in queue Status: job 'papowell@h4+42' removed at 07:29:57.924 Server Printer: lp2@h4 (serving lp) Queue: no printable jobs in queue Server Printer: lp3@h4 (serving lp) Queue: no printable jobs in queue h4: {275} % lpr /tmp/hi h4: {276} % lpq Printer: lp@h4 (subservers lp2, lp3) Queue: 1 printable job Server: pid 4063 active Status: waiting for subserver to finish at 07:31:08.074 Rank Owner/ID Class Job Files Size Time 1 papowell@h4+62 A 62 /tmp/hi 3 07:31:07 Server Printer: lp2@h4 (serving lp) Queue: no printable jobs in queue Server Printer: lp3@h4 (serving lp) Queue: no printable jobs in queue h4: {277} % lpq Printer: lp@h4 (subservers lp2, lp3) Queue: no printable jobs in queue Status: no more jobs to process in load balance queue at 07:31:12.317 Server Printer: lp2@h4 (serving lp) Queue: no printable jobs in queue Server Printer: lp3@h4 (serving lp) Queue: no printable jobs in queue Status: job 'papowell@h4+62' removed at 07:31:10.311 The first lpq command shows how the status is displayed for a load balance queue - the queue and its server queues are shown as well. Next, we use lpr to print a job (job id papowell@h4+62). We then use a couple of lpq commands to see how the job is first sent to the lp queue, which then forwards it to the lp3 queue, which then processes it and removes it. (For purposes of demonstration we have artificially slowed down the operation of the load balance queue so that the jobs will remain in the queue for sufficient time for us to display their status.) We can send another job to the load balance queue: h4: {278} % lpr /tmp/hi h4: {279} % lpq Printer: lp@h4 (subservers lp2, lp3) Queue: no printable jobs in queue Status: no more jobs to process in load balance queue at 07:37:17.953 Server Printer: lp2@h4 (serving lp) Queue: no printable jobs in queue Status: job 'papowell@h4+89' removed at 07:37:15.936 Server Printer: lp3@h4 (serving lp) Queue: no printable jobs in queue Status: job 'papowell@h4+81' removed at 07:36:40.116 This time we see that the job was put in lp2. The normal load balance queue operation is to use the server queues in round robin order. While this simple configuration is suitable for a large number of configurations, there are situations where server queue must be chosen dynamically. For example, if the server queues are actually transferring jobs to remote clients then as soon as the job is sent to the remote client the queue appears empty and available for use. To correctly check to see if the queue is available, the status of the remote queue or destination of the server queue must be checked. To handle this situation, a :chooser program or filter can be used. When the load balance queue is trying to decide where to send a job, it first checks the server queues to see if they are enabled for printing. If a :chooser program is specified in the load balance queue printcap entry, then it is started with the normal filter options and environment variables, supplemented as discussed below. The :chooser program will read a list of candidate queues from its lpd server checks the :chooser exit code - if it is zero (successful) then the chosen queue is used otherwise the exit code is used for the result value of processing the job. This allows the chooser process to not only control the destination of the job but also to hold, remove, or abort the job handling process. If the :chooser does not specify a queue, then the job is skipped and another job is chosen. One side effect of the using a chooser program is that while there are jobs that can be processed in the queue the lpd server needs to periodically check to see if a server queue has become available. If it did this continually then a very high load would be put on the system. Instead, the chooser_interval option specifies a maximum time in seconds (default 10 seconds) between the times that the lpd server checks to see if there is an available server. Normally, the chooser_interval time. However, the chooser can also be used to direct jobs by their characteristics, or other criteria. This means that the entire spool spool queue has to be scanned for work. If the Edit the printcap file so it have the contents indicated below, create the /tmp/lp2 and /tmp/lp3 files with 0777 permissions. Then create the /tmp/chooser.script with the contents indicated below, and give it 0755 (executable) permissions. Make sure that the path to the head program used in chooser.script is correct. Use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. # printcap lp:force_localhost lp:server :sd=/var/spool/lpd/%P :sv=lp2,lp3 :chooser=/tmp/chooser.script lp2:force_localhost lp2:server :ss=lp :sd=/var/spool/lpd/%P :lp=/tmp/lp2 lp3:force_localhost lp3:server :ss=lp :sd=/var/spool/lpd/%P :lp=/tmp/lp2 # /tmp/chooser.script #!/bin/sh echo CHOOSER $0 $@ >>/tmp/chooser set >>/tmp/chooser /usr/bin/head -1 exit 0 Now run the following commands: h4: {280} % lpr /tmp/hi h4: {281} % lpq -lll Printer: lp@h4 (subservers lp2, lp3) Queue: no printable jobs in queue Status: CHOOSER selected 'lp3' at 14:02:50.605 Status: transferring 'papowell@h4+178' to subserver 'lp3' at 14:02:50.614 Status: transfer 'papowell@h4+178' to subserver 'lp3' finished at 14:02:50.624 Status: job 'papowell@h4+178' removed at 14:02:50.632 Status: starting subserver 'lp3' at 14:02:50.632 Status: waiting for server queue process to exit at 14:02:50.651 Status: subserver pid 10182 exit status 'JSUCC' at 14:02:52.872 Status: no more jobs to process in load balance queue at 14:02:52.879 Server Printer: lp2@h4 (serving lp) Queue: no printable jobs in queue Server Printer: lp3@h4 (serving lp) Queue: no printable jobs in queue Status: waiting for subserver to exit at 14:02:50.748 Status: subserver pid 10183 starting at 14:02:50.820 Status: accounting at start at 14:02:50.821 Status: opening device '/tmp/lp3' at 14:02:50.833 Status: printing job 'papowell@h4+178' at 14:02:50.834 Status: processing 'dfA178h4.private', size 3, format 'f', \ IF filter 'none - passthrough' at 14:02:50.838 Status: printing finished at 14:02:50.839 Status: accounting at end at 14:02:50.839 Status: finished 'papowell@h4+178', status 'JSUCC' at 14:02:50.841 Status: subserver pid 10183 exit status 'JSUCC' at 14:02:50.843 Status: lp3@h4.private: job 'papowell@h4+178' printed at 14:02:50.856 Status: job 'papowell@h4+178' removed at 14:02:50.871 As you see from the example above, the CHOOSER selected lp3 for use. Let us look at the /tmp/chooser file and see how the chooser.script program was run: CHOOSER -Apapowell@h4+113 -CA -D2000-06-01-14:02:13.313 -Hh4.private \ -J/tmp/hi -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \ -hh4.private -j113 -kcfA113h4.private -l66 -npapowell -sstatus \ -t2000-06-01-14:02:13.379 -w80 -x0 -y0 acct USER=papowell LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib HOME=/home/papowell PRINTCAP_ENTRY=lp :chooser=/var/tmp/LPD/chooser :lp=/tmp/lp :sd=/var/tmp/LPD/lp :server :sv=lp2,lp3 lp2=change=0x0 done_time=0x1 held=0x0 move=0x0 printable=0x0 printer=lp2 printing_aborted=0x0 printing_disabled=0x0 queue_control_file=control.lp2 server=0 spooldir=/var/tmp/LPD/lp2 lp3=change=0x0 done_time=0x2 held=0x0 move=0x0 printable=0x0 printer=lp3 printing_aborted=0x0 printing_disabled=0x0 queue_control_file=control.lp3 server=0 spooldir=/var/tmp/LPD/lp3 PS1=$ OPTIND=1 PS2=> SPOOL_DIR=/var/tmp/LPD/lp LOGNAME=papowell CONTROL=Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+113 D2000-06-01-14:02:13.313 Qlp N/tmp/hi fdfA113h4.private UdfA113h4.private As you can see, the program is invoked with the same options as a normal filter. In addition, the printcap information for each server queue is passed in an environment variable with the name of the server queue. This means that if there is information needed by the chooser program to test for the availability of hardware, etc., this can be placed in the printcap information. One of the limitations of using the :chooser program is that you may have a high overhead associated with running the program. The &LPRng; software provides support for linking in a user provided routine that does the same thing as the :chooser program. This routine has the following API or interface: Printcap Option: chooser_routine chooser_routine@ - default - do not use chooser routine chooser_routine - use chooser routine Configuration: configure --with-chooser_routine=name --with-user_objs=objectfile.o defines the CHOOSER_ROUTINE compilation option to name includes the objectfile.o in the library. extern int CHOOSER_ROUTINE( struct line_list *servers, struct line_list *available, int *use_subserver ); servers: all subserver queues for this load balance queue available: subserver queues to choose from use_subserver: chosen subserver queue RETURNS: 0 - use the 'use_subserver' value as index into servers list for server to use != 0 - set job status to value returned. See the LPRng/src/common/lpd_jobs.c and LPRng/src/common/user_objs.c files for details of the servers, available, and user_subserver parameters. The user_objs.c file provides a simple template that can be used as a starting point for a more complex routine. You should modify the code in the user_objs.c file and then use the configure options shown above to cause the user_objs.c file to be compiled and linked into the &LPRng; executables. Routing Jobs To Print Queues A routing queue is similar in concept to a load balance queue in that it transfers a job to a (different) print queue, but the job destination is chosen at the time the job is submitted to the queue rather than at the time the job is removed from the queue. A routing queue can modify the job control file, multiple copies of the same job can be sent to the same or different printers, and the job can be held, rejected, or processed immediately. Edit the printcap file so it have the contents indicated below, create the /tmp/lp2 and /tmp/lp3 files with 0777 permissions. Create the /tmp/router.script with the contents indicated below, and give it 0755 (executable) permissions. Use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. # printcap lp:force_localhost lp:server :lp=/dev/null :sd=/var/spool/lpd/%P :router=/tmp/router.script lp2:force_localhost lp2:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 lp3:force_localhost lp3:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 # /tmp/router.script #!/bin/sh /bin/cat <<EOF dest lp2 copies 2 Cred end dest lp3 end EOF exit 0 The router.script will write the routing information to its lp2 to get two copies of the job and we want to change the class to red. Now run the following commands: h4: {282} % lpc stop all Printer: lp@h4 lp@h4.private: stopped Printer: lp2@h4 lp2@h4.private: stopped Printer: lp3@h4 lp3@h4.private: stopped h4: {283} % lpq Printer: lp@h4 (dest lp@localhost) (printing disabled) (dest lp2, lp3) Queue: no printable jobs in queue Printer: lp2@h4 (printing disabled) Queue: no printable jobs in queue Printer: lp3@h4 (printing disabled) Queue: no printable jobs in queue h4: {284} % lpr /tmp/hi h4: {285} % lpq Printer: lp@h4 (dest lp@localhost) (printing disabled) (dest lp2, lp3) Queue: 1 printable job Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+235 A 235 /tmp/hi 3 16:14:22 - papowell@h4+235.1 ->lp2 <cpy 0/2> - papowell@h4+235.2 ->lp3 Printer: lp2@h4 (printing disabled) Queue: no printable jobs in queue Printer: lp3@h4 (printing disabled) Queue: no printable jobs in queue The status reported for the spooled job indicates that the job is routed to lp2, and that two copies will be sent. The :destinations option in the printcap entry causes lpq to display the contents of the specified queues. Now execute the following commands: h4: {286} % lpc start Printer: lp@h4 lp@h4.private: started h4: {287} % lpq Printer: lp@h4 (dest lp@localhost) (destinations lp2, lp3) Queue: no printable jobs in queue Status: job 'papowell@h4+235' removed at 16:14:37.491 Printer: lp2@h4 (printing disabled) Queue: 2 printable jobs Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+235.1C1 A 235 /tmp/hi 3 16:14:36 2 papowell@h4+235.1C2 A 236 /tmp/hi 3 16:14:37 Printer: lp3@h4 (printing disabled) Queue: 1 printable job Server: no server active Rank Owner/ID Class Job Files Size Time 1 papowell@h4+235.2 A 237 /tmp/hi 3 16:14:37 h4: {288} % more /var/spool/lpd/lp2/cfA235* Hh4.private Ppapowell J/tmp/hi Cred Lpapowell Apapowell@h4+235.1C1 D2000-06-01-16:03:25.237 Qlp N/tmp/hi fdfA235h4.private UdfA235h4.private As you can see, two copies of the job has been transferred to lp2 and one to lp3, each with a different job number, If we examine the control file for the jobs in the lp2 spool queue, we will find that the C or class information has been changed to red. For details about all of the capabilities of the routing filter, see Dynamic Routing. Here is a summary of the information that the routing filter can put into the routing file. dest queue Route this job to queue. The queue@host form will transfer the job to the queue on the named host. copies N Send N copies of this job to the destination. priority C Set the job priority letter to C, where C is a single upper case letter. Cvalue Set the control file line starting with C to Cvalue. The exit status of the routing filter controls how the job will be processed. If the exit code is JSUCC (0), then the job will be processed normally, JHOLD will hold the job until released, JREMOVE will remove the job, and so forth. Job Options and the Z Control File Entry Many printers have special capabilities such as printing in landscape mode, duplex printing, binding, or stapling. These capabilities are usually invoked or enabled by the print spooler sending special printer control commands to the printer based on values it finds in the control file. The &LPRng; print spooler uses the Job formatting options are specified using the lpr -Z option. The lpr program concatenates the -Z options and puts them in the control file as a single Z line. For example: h4: {289} % lpc stop Printer: lp@h4 lp@h4.private: stopped h4: {290} % lpr -Zthis -Zthat /tmp/hi h4: {291} % cat /var/spool/lp/cf* Hh4.private Ppapowell J/tmp/hi CA Lpapowell Zthis,that Apapowell@h4+115 D2000-05-05-10:05:41.351 Qlp N/tmp/hi fdfA115h4.private UdfA115h4.private As we see, the Z options have been put into the control file on the ifhp configuration where possible. Many of these options rely on the printer supporting PostScript or having the appropriate PCL commands to do the indicated operation. -Zlandscape -Zportrait - select landscape or portrait orientation. -Zduplex -Zsimplex - select duplex (both sides of a page) or simplex (single side of a page) printing. -Zletter -Zlegal -Zledger -Za4 -Za5 -Zenvelope -Ztransparency - select a paper size -Zinupper -Zinmiddle -Zinlower - select input media from the appropriate input tray -Zmanual - select input from the manual feed Setting Job Options Using the Printcap An alternative to this method of using :prefix_z, :append_z, and :delete_z are prefixed, appended, or deleted from the current set of Z control file options by the landscape:lp=%P@server landscape:server:tc=.common :lp=raw@server:append_z=landscape:delete_z=portrait raw:server:tc=.common:lp=.... :filter=/usr/local/libexec/filters/ifhp .common:sd=/var/spool/lpd/%P When a job is sent to the Converting SystemV Options to LPRng Options On some SystemV lp print spoolers, the lp -o option, puts the option information into the control file :prefix_option_to_option=from,from... to facility to prefix the # System V to LPRng - S and O to Z options convert:server:tc=.common :lp=raw@server:prefix_option_to_option=S,O Z # LPRng to System V O options convert:server:tc=.common :lp=raw@server:prefix_option_to_option=Z O Selecting a Single Option - Muliple Queues Here is an example of how you can set up queues that will append the appropriate landscape:lp=%P@server landscape:server:tc=.common :lp=raw@server:append_z=landscape duplex:lp=%P@server duplex:server:tc=.common :lp=raw@server:append_z=duplex ledger:lp=%P@server ledger:server:tc=.common :lp=raw@server:append_z=ledger legal:lp=%P@server legal:server:tc=.common :lp=raw@server:append_z=legal raw:server:tc=.common:lp=.... :filter=/usr/local/libexec/filters/ifhp .common:sd=/var/spool/lpd/%P The problem with this method is that for each option we need to define a queue whose only purpose is to append the appropriate option and then forward this to the main print queue. Selecting Multiple Options - Single Queue In the previous section, we showed how to set up a queue that would append a single option to the control file The solution to this problem originated with the The incoming_control_filter=/path filter processes the incoming job control or job ticket file. It can be used to values in the job ticket of incoming jobs. It reads the control file on its incoming_control_filter=/path filter. The input and output have the format: INPUT: X<option> - option from control file X=<option> - alternative option format key=<option> - spooler option X==<option> - option starting with = sign OUTPUT: X - delete option or value X= - delete option or value X<option> - set option value X=<option> - set option value key=<option> - set value of 'key' to option key= - delete option or value In addition to modifying job options, the contents of the jobs data files can be modified or the data files removed. Any data files with a 0 length will be removed from the job. If all of the data files have a 0 length then the job will be discarded. Modification of job options may have unforseen effects on The following shows how we can set up a single queue that will allow various combinations of options to be selected by the format of the queue name: # for clients pr|pr_*:lp=%Q@server # for server pr|pr_*:server :tc=.common:lp=.... :incoming_control_filter=/usr/local/libexec/filters/update_z :filter=/usr/local/libexec/filters/ifhp .common:sd=/var/spool/lpd/%P The UTILS directory. You should note that additional options can be specified as desired. # #!/usr/bin/perl # update_z script: # Determine the options according to the format of the queue name # Inspired by the psfilter code of Andreas Klemm # and Thomas Bueschgens # First, get command line arguments # use Getopt::Std; my(%args,$Q,$Zopts,@file); getopts( "A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:" . "a:b:cd:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:", \%args ); # read stdin @file = <STDIN>; $Zopts = ""; # first use command line Queue name $Q = $args{"Q"}; if( not $Q and (($Q) = grep(/^Q/,@file)) ){ # next use control file Queue name chomp $Q if $Q; } # now we split up the name and use as parameters for Z options while( $Q =~ /_([^_]+)/g ){ # you can add them or test and then add them if( $1 eq "landscape" or $1 eq "legal" or $1 eq "ledger" ){ $Zopts .= ",$1" } } if( $Zopts ){ # remove leading comma $Zopts = substr( $Zopts, 1 ); #replace or prefix Z options if( not (grep { s/$/,$Zopts/ if /^Z/; } @file) ){ print "Z" . $Zopts . "\n"; } } print @file if( @file ); exit 0 Example Input: ... Z=over Q=lp_landscape_ledger ... Example output: Z=over,landscape,ledger Q=lp_landscape_ledger The Perl script first uses the while( $Q =~ /_([^_]+)/g ){ # you can add them or test and then add them Zopts .= ",landscape" if( $1 eq "ld" ); Zopts .= ",ledger" if( $1 eq "11" ); Zopts .= ",legal" if( $1 eq "15" ); Zopts .= ",a4" if( $1 eq "a4" ); } Interfacing to Non-LPRng Spoolers Given the large number of defective RFC1179 implementations that are currently in use, there will come a time when the administrator will discover that their printer with its built-in network interface, the non-UNIX based print spooler on a mainframe, or even a new print spooler on a new OS distribution will not accept jobs from the &LPRng; system. Usually this is caused by the presence, absence, or order of lines in the control file being sent to the remote system. To deal with this particular problem, the :bk, :control_file_line_order, :nline_after_file, and :control_filter options are used. The :bk (BSD Kompatibility) option causes the lpd server to remove all but an extremely small subset of lines from the control file, and to put the lines in the most commonly used order. In addition it will make the control and data files names extremely short and simple. This almost always solves compatibility problems when sending jobs to older vintage print spoolers or UNIX systems. If this does not solve the problem, then you can specify the allowed control file lines and their order using the control_file_line_order=... option. For example, control_file_line_order=CJPMD would allow only control file lines starting with C, J, P, M, and D, and this order in the control file. Note that this does not provide missing line values, it only controls line values that are present in the control file. You should also use the :bk option as well. You might run into some really unusual implementations where the control file N (file name) information must come after the control file name. This is forced by the :nline_after_file option. If these horrible kludges do not solve your compatibility problems then we turn to the very large hammer and edit the control file. The very last step before transfering the control file to the remote server is to filter it using the :control_filter=/path program specified in the printcap. The :control_filter reads the control file from :control_filter exits with a 0 status, then the normal processing continues. Any other status will cause the transfer operation to be aborted and an error reported. Debugging, Tracing, and Log Files The &LPRng; software was designed and written to provide as high a level of diagnostic information as possible. This was largely in part due to the problems with portability, coding errors, and other human frailties. Approximately 80% of the &LPRng; source code concerns itself with checking return values from system functions and producing error messages, debugging and tracing information, and various facilities used for regression testing and diagnosis. The approach used by &LPRng; is to produce trace output for the &LPRng; clients or log files for the lpd server that show the various events or flow of information through the &LPRng; system. There are several classes or types of actions that can be traced, and various levels of trace information generated. The interface used to control these actions are the command line -D literals flags and the lpc debug command. First, we will look at how you can use the debugging facilities for the clients. Enter the following commands: h4: {292} % lpr -D= debug usage: -D [ num | key=num | key=str | flag | flag@ | flag+N ]* keys recognized: network[+N,@], database[+N,@], lpr[+N,@], lpc[+N,@], lprm[+N,@], lpq[+N,@], test=num, job=num, log[+N,@] h4: {293} % lpr -V /tmp/hi LPRng-3.7.2, Copyright 1988-2000 Patrick Powell, <papowell@lprng.com> sending job 'papowell@h4+981' to lp@localhost connecting to 'localhost', attempt 1 connected to 'localhost' requesting printer lp@localhost sending control file 'cfA981h4.private' to lp@localhost completed sending 'cfA981h4.private' to lp@localhost sending data file 'dfA981h4.private' to lp@localhost completed sending 'dfA981h4.private' to lp@localhost done job 'papowell@h4+981' transfer to lp@localhost h4: {294} % lpr -D1 /tmp/hi 09:38:08.707 h4 [13991] lpr Get_printer: original printer '<NULL>' 09:38:08.708 h4 [13991] lpr Get_all_printcap_entries: starting 09:38:08.708 h4 [13991] lpr Select_pc_info: looking for 'all', depth 0 09:38:08.708 h4 [13991] lpr Select_pc_info: returning '<NULL>' 09:38:08.708 h4 [13991] lpr Select_pc_info: looking for '*', depth 0 09:38:08.708 h4 [13991] lpr Select_pc_info: returning '<NULL>' 09:38:08.708 h4 [13991] lpr Dump_line_list: Get_all_printcap_entries ... The lpr -D= causes the lpr (and other &LPRng; programs) to show what debugging flags are available. The lpr -V flag causes lpr to run in verbose mode and show its activities. Finally, we use lpr -D1 to enable the simplest level of debugging. This will produce a trace of the various activities that lpr carries out. Try lpr -D2, lpr -D3, and so forth to see the increasing amount of detail that you get. The network and database debug flags turn on debugging for the network facilities and the database (lpd.conf, printcap, and lpd.perms) lookups. Lets see what lpr -Dnetwork shows us: Ch4: {295} % lpr -Dnetwork /tmp/hi lp: getconnection: START host localhost, timeout 10, connection_type 1 lp: getconnection: fqdn found localhost.private, h_addr_list count 1 lp: Link_dest_port_num: port 2000 = 2000 lp: getconnection: sock 3, src ip 127.0.0.1, port 65209 lp: getconnection: dest ip 127.0.0.1, port 2000 lp: getconnection: connection to 'localhost' sock 3, errmsg 'No Error' lp: Link_send: host 'localhost' socket 3, timeout 6000 lp: Link_send: str '^Blp ', count 4, ack 0x80447a0 lp: Link_send: final status NO ERROR lp: Link_send: host 'localhost' socket 3, timeout 6000 lp: Link_send: str '^B135 cfA276h4.private ', count 22, ack 0x8044370 lp: Link_send: final status NO ERROR lp: Link_send: host 'localhost' socket 3, timeout 6000 lp: Link_send: str 'Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+276 D2000-06-02-09:44:52.369 Qlp N/tmp/hi fdfA276h4.private UdfA276h4.private ', count 136, ack 0x8044370 lp: Link_send: final status NO ERROR lp: Link_send: host 'localhost' socket 3, timeout 6000 lp: Link_send: str '^C3 dfA276h4.private ', count 20, ack 0x8044310 lp: Link_send: final status NO ERROR lp: Link_send: host 'localhost' socket 3, timeout 6000 lp: Link_send: str '', count 1, ack 0x8044310 lp: Link_send: final status NO ERROR As we see, we get a detailed exposition of the network connection and transfer steps. If you need or want more detail, try using lpr -Dnetwork+2 or lpr -Dnetwork+3. You may want to try lpr -Ddatabase and observe the actions of the lpr program as it extracts information from the lpd.conf and printcap files. If you need or want more detail, try using lpr -Ddatabase+2 or lpr -Ddatabase+3. If you need to trace the activities of the lpd server, it becomes a little more complex. The lpd server has a single listening process that forks and creates individual processes to handle incoming requests. Debug or diagnose the main process actions by using lpd -D.... You may also want to use lpd -F to keep the server in the foreground so you can kill it off easily. Needless to say, you should also redirect the lpd process debugging session using the C Shell. h4: {296} % lpd -F -D1 >&/tmp/logfile & [2] 14299 h4: {297} % tail -f /tmp/logfile 2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \ select status 1 2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \ read status 1 2000-06-02-09:53:39.716 h4 [1200] Waiting Dump_line_list: \ Read_server_status - input - 0x8047980, count 0, max 0, list 0x0 2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \ select status 0 2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: LOOP START 2000-06-02-09:53:39.716 h4 [1200] Waiting Get_max_servers: \ getrlimit returns 64 2000-06-02-09:53:39.716 h4 [1200] Waiting Get_max_servers: \ returning 32 2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: \ max_servers 32, active 0 2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: \ starting select timeout 'yes' ^C h4: {298} % jobs [1] - Running lpd -F -D1 >& /tmp/logfile h4: {299} % kill %1 We start the debugging session by running the lpd server in foreground mode. This causes it to send its output to lpd server in the background. Then we use tail -f to read from the log file. Finally, we kill off the lpd server. This method is extremely difficult to use, as all of the output produced by the server and its subprocesses is sent to a single output file. If we want to debug the actions concerning a single queue, then we can use the queue log file and lpc debug command instead. The following options control debugging of an individual print queue. lf=log The log file for the queue. The queue server process will open this file and place debugging information into this file. max_log_file_size=nnn The maximum size of the log file in K bytes. When the queue server process first opens this file it will check to see if the file is larger than the maximum size. If it is, then it will truncate it. A zero (0) value suppress truncation. min_log_file_size=nnn When the log file is truncated only the the last nnn K bytes are retained. db=options These are debugging options for the spool queue. These options are permanent and cannot be changed by using the lpc debug facility. The lpc debug command is used to set the debugging options in force for the spool queue. This is done by writing the debug options into the spool queue control file. Let us see how we can use this facility to trace the actions of printing a file. Edit the printcap file so it have the contents indicated below, create the /tmp/lp and /tmp/lp2 files with 0777 permissions. Use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server. # printcap lp:force_localhost lp:server :lp=/dev/null :sd=/var/spool/lpd/%P :lf=log lp2:force_localhost lp2:server :sd=/var/spool/lpd/%P :lp=/tmp/lp2 :lf=log Now execute the following commands: h4: {300} % lpq Printer: lp@h4 Queue: no printable jobs in queue h4: {301} % lpc debug lp 1 Printer: lp@h4 debugging override set to '1' lp@h4.private: updated h4: {302} % lpc status Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug) lp@h4 enabled enabled 0 none none (1) h4: {303} % lpr /tmp/hi h4: {304} % more /var/spool/lpd/lp2/log 2000-06-02-10:10:50.589 h4 [1201] (Server) lp: \ Update_spool_info: printer 'lp' 2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \ Do_queue_jobs: printable 1, held 0, move 0 2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \ Do_queue_jobs: after Scan_queue next fd 5 2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \ Do_queue_jobs: MAIN LOOP 2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \ Do_queue_jobs: Susr1 before scan 0 2000-06-02-10:10:50.591 h4 [1201] (Server) lp: \ Do_queue_jobs: chooser '<NULL>', chooser_routine 0 The lpc debug command sets the debug level to 1. We can use the lpc status command to see what debug flags or actions are currently specified for the spool queue. We then send a job to the spool queue and examine the log file contents. Each line in the log file has a timestamp, the name of the host, the process id that produced it, and a heading that tells the action or activity that the process is performing, and the name of the print queue that is being processed and a trace message. By convention, the trace message lists the name of the routine that processed it and then the actual information. Some messages may extend over several lines, but each line has the standard header at the start of the line. The default debug or trace actions were designed to trace problems with printing, as these are the most common. However, you can also use the lpr, lpc, lprm, or lpq option to cause the lpd server to trace the actions during the execution of an lpr, lpc, lprm, or lpq request. The log option is used to test various logging facilities and is usually not used for general purpose debugging.
&LPRng; Clients - lpr, lprm, lpq, lpc, lpstat The &LPRng; software is a true set of client/server applications. The &LPRng; clients, lpr, lpq, lprm, and lpc connect to a lpd server using a TCP/IP connection. This means that you must have TCP/IP networking enabled on your workstation to use &LPRng;. However, you do not need to have an external network connection to the Internet. For most single system users, the lpd server is running on the same workstation as the client program, and the clients will simply talk to the localhost. Printer and Server Information Options used: PRINTER, LPDEST, NPRINTER NGPRINTER Environment variables force_localhost FLAG force clients to send requests to localhost require_explicit_q FLAG require queue to be specified When an &LPRng; client such as lpr, lpq, lprm, or lpc needs to communicate with a print server, the only information they normally need is: The remote printer (:rp) value to be used in requests to the lpd print server. This is sometimes referred to as the printer or print queue name. The remote server (:rm) which is the The IP address or hostname of the print server. The original queue name specified by the user which may be used as part of the job information sent to the print server. &LPRng; has several ways to specify the printer queue and server information. Command Line -Pprinter@host The -P printer@host literal explicitly provides a printer and server value. This is equivalent to the printcap entry shown below: lpr -Plaser@10.0.0.1 equivalent to printcap entry: laser:lp=laser@10.0.0.1 lpq -Plp@myserver equivalent to printcap entry: lp:lp=lp@myserver Command Line -Pprinter Use the printcap entry with the name or alias printer and use the information in that printcap entry. Example: lpr -Plp look up printcap entry for 'lp' PRINTER, LPDEST, NPRINTER, and NGPRINTER Environment Variables If no command line option is specified, the &LPRng; clients will use the first defined PRINTER, LPDEST, NPRINTER, or NGPRINTER environment variable value and will use the value as though specified as a -P$PRINTER command line literal. If the value has the form -Pprinter@host the print queue will be printer on server host and not consult the printcap database. If the value has the form printer then the printcap will be searched for a printer printcap entry. For example: export PRINTER=laser@10.0.0.1; lpr equivalent to printcap entry: laser:lp=laser@10.0.0.1 export PRINTER=pr; lpr look up printcap entry for 'lp' Wildcard Printcap Entry If you specify a printcap name or alias as * (wildcard), then if the &LPRng; system cannot find a printcap with the exact name then the first matching wildcard will be used. For example: pr|*:rm=server lpr -Ppr2 will match, and result in a printcap entry pr|pr2:rm=server *|pr:rm=server lpr -Ppr2 will match, and result in a printcap entry pr2|pr:rm=server First Printcap Entry If you do not specify a printer on the command line or in the environment variables, and the Default Printer and Server Host Options used: default_remotehost=default rm (remote host) default_printer=default rp (remote printer) If the preceding steps do not yield a printer name and the default_printer and default_remote_host values from the lpd.conf configuration file will be used. If there is no configuration file, then the compile time defaults will be used. Using the fallback values is usually not a desirable event and may indicate that you have a misconfigured host, so the compile time defaults are usually set to missingprinter@localhost to provide an annoying message for users. Force Connection to Localhost Options used: force_localhost FLAG force localhost to be remote host The legacy BSD print spooler required an lpd print server to be running on each host. During the initial stages of development and deployment, the default &LPRng; configuration and deployment was to always allow lightweight operation, that is, clients would always connect to the remote host specified in the printcap. While this default was appropriate for experienced system administrators, novice administrators or those who had already configured print spooling systems and simply wanted to upgrade found themselves confused by this change. This problem resulted in over 700 postings to the &LPRng; mailing list in a five year period. This problem was solved by providing a force_localhost option in the configuration, and setting the default value to 1 or TRUE. When this option is TRUE, then all &LPRng; clients will connect to the server on the localhost, unless they use the lpr -Pserver@host command line form. If lightweight operation is wanted, the administrator can either compile the &LPRng; software with the appropriate value or can explicitly set the force_localhost@ flag. # default: lp:lp=lp@10.0.0.1:... lpr -Plp -> lp:lp=lp@localhost:... lpr -Plp@10.0.0.1 -> lp:lp=lp@10.0.0.1 (no other options) lp:lp=lp@10.0.0.1:force_localhost@:... lpr -Plp -> lp:lp=lp@10.0.0.1:... To disable at compile time: configure --disable-force_localhost User Identification Options used: allow_user_setting=privileged users When an client program sends a command to the lpd server it may need to provide the name of the user who is originating the request for service. This name is obtained by looking up the UID of the user running the client in the appropriate user information database; if the information is not found the UID is used instead. Also, the client machine hostname may also be needed. This is usually determined by using a DNS lookup and trying to determine if there is a canonical or Fully Qualified Domain Name for the host and using this. The lpr -U name@host (and for lpq, lprm, and lpc) option allows privileged users to cause the client software to use the name value as the originator and host as the machine name. This allows privileged users to impersonate other users. This is most useful for programs such as Samba and PCNFS, which need to act as proxies for users. By default, ROOT (UID 0) is the only user that can masquerade as another user. The allow_user_setting=name,name... configuration option can be used to specify a list of names or UIDs that can also perform masquerading. For example, if the Samba server was running as user samba, then allow_user_setting=samba would allow it to specify the name of print job originator as a remote user, and the remote user would not need a login account on the system. <application/lpr/ - Job Spooler Program The lpr client program is used to submit a job to a print spooler. It does this by collecting information about the job, putting it in a control file, and then sending the control file and files to be printed to the print server. The lpr command line options are used to control or specify the values placed in the control file and how the job is to be transferred to the remote host. In addition, there are printcap or configuration level options that provide a further degree of administrative control over additional facilities. You can get the currently supported command line options by using the following command: h4: {305} % lpr -= lpr: Illegal option '=' usage summary: lpr [-Pprinter[@host]] [-A] [-B] [-Cclass] [-Fformat] [-Jinfo] [-(K|#)copies] [-Q] [-Raccountname] [-Ttitle] [-Uuser[@host]] [-V] [-Zoptions] [-b] [-m mailaddr] [-h] [-i indent] [-l] [-w num ] [-r] [-Ddebugopt ] [ filenames ... ] -A - use authentication specified by AUTH environment variable -B - filter job before sending -C class - job class -D debugopt - debugging flags -F format - job format -b,-l - binary or literal format c,d,f,g,l,m,p,t,v are also format options -J info - banner and job information -K copies, -# copies - number of copies -P printer[@host] - printer on host -Q - put 'queuename' in control file -Raccntname - accounting information -T title - title for 'pr' (-p) formatting -U username - override user name (restricted) -V - Verbose information during spooling -Z options - options to pass to filter -h - no header or banner page -i indent - indentation -m mailaddr - mail final status to mailaddr -r - remove files after spooling -w width - width to use PRINTER, NPRINTER environment variables set default printer. If you are interested in the exact details of the job transfer, control file, and data file formats, please see RFC 1179 - Line Printer Daemon Protocol for the exact details. Job Format Options Options used: default_format=default print job format (f) fx=supported formats for printing The legacy or vintage BSD print spooling system assigned each job a format. This format was used by the lpd server to select an appropriate filter program that would process the job and format it correctly for the printer. By convention, lower case letters were used to specify job formats. The &LPRng; lpr client supports the legacy or vintage BSD formats, and also provides the -Fx literal to allow addition formats to be specified. If a format is specified the default_format value (usually f) is used. The fx=... option value is a list of the (lower case) characters corresponding to the formats allowed for a spool queue. For example, fx=flv would allow only jobs with format f, l, or v to be spooled to a queue. By default, all job formats are allowed. A couple of job formats that require special treatment: the b (binary) and its alias the l (literal) format, and the p (pretty print) format. The -b or -l command line literal will select job format l (literal) for the job. By default, jobs marked with l format are supposed to have a minimum amount of handling and passed directly to a printer. The p (pretty print) format is just the opposite - these jobs are supposed to be pretty printed according to the various facilities available to the lpd print spooler. Job Pretty Printing, Banners, Priority, and Accounting Options used: ab FLAG always print banner sh FLAG suppress header (banner) The legacy or vintage BSD pretty printing facility support allowed users to specify the title (-T title), indentation (-i indent), width (-i indent) of output for pretty printing. These probably will not have any effect, but their values are placed in the control file and sent to the lpd server. The job name (-J name) and no banner page (-h) literals also cause information to be placed in the control file. The -J value is used to specify a job name on a banner page, and is placed in the control file. The -h (no header) has the strange effect of not putting banner name information in the control file. Apparently, if you do not have a name for your banner then no name will be sent. If the :sh option is explicitly specified in a printcap entry for the lpr client, then it has the effect of the -h literal. However, the :ab (Always Print Banner) option can be used on the print server to always force a banner to be printed even if the user does not specify a banner. Finally, the -R literal allows additional accounting information to be placed in the control file. Job Class and Priority Options used: break_classname_priority_link FLAG classname does not set priority default_priority=default priority ignore_requested_user_priority FLAG ignore requested user priority The -Cclass information is used placed in the control file; the default class and priority is set by the default_priority option (default A). The first letter of the class information is uppercased and then used as the job priority. The break_classname_priority_link option causes lpr to place class information in the control file but not to use it to set the job priority. This is usually used in institutions where users should not specify their priority. Finally, the ignore_requested_user_priority is actually used by lpd to ignore the priority requested by users. Job Copies and Job Size Options used: mc=maximum number of copies mx=maximum job size (Kbytes) The :mc (maximum number of copies) and :mx (maximum job size) options are used by both the lpr and lpd programs. The lpr -Kn or lpr -#n option sets the numbers of copies wanted to n; if this is larger than the :mc value then the lpr client will not print the job and the lpd server will discard the job with an error. The job size is the product of the number of copies and the sum of the individual file sizes. This is, in effect, the total number of bytes to be transferred to the printer. If the job size is larger than the :mx limit, then the lpr client will not print the job and the lpd server will discard the job with an error. Job Completion Notification Requested Options used: allow_user_logging FLAG users can send status lpr_bsd FLAG lpr -m acts as legacy BSD flag The lpr -m mailaddr option will put the specified mailaddr value into the control file. How this is processed is left to the particular server implementation. See the Abnormal Termination section for details. &LPRng; extends the lpr -m mailaddr to allow mail addresses of the form host[%port][/(TCP|UPD)], and when the job is being transferred or printed the lpd server to open a connection to the specified TCP/IP address and send job progress information. This is a security loophole and the allow_user_logging flag must be present to allow this operation. The lpr_bsd will cause the lpr -m option not to take an argument, as in the BSD lpr, and will place the users name and host information in the control file as the destination for notification. Remove Files After Spooling The lpr -r option is extremely dangerous and has proven to be fatal to a large number of system administrators, so it should be used with caution. Unfortunately, it is used in a large number of shell scripts and by default in a vast number of programs so it is present in the &LPRng; software. Its action is quite simple. After sending the print job to the spooler, it will try to unlink the original files. It will do the unlinking operation as the original user, but if the original user had permissions to remove, say, /etc/password, then the file will be removed. The -Z Passthrough to Filter Options Options used: append_z=Append values to Z options list prefix_z=Prefix values to Z options list remove_z=Remove values from Z options list prefix_s_to_z FLAG Prefix control file S to Z information prefix_s_to_z FLAG Prefix control file Z to S information By convention all of the values specified with the lpr -Z option are placed in the job control file and passed to the lpd server, which turn passes them to the print filters. This allows users to pass options that are not part of the RFC1179 repetoire to filters. In addition to this simple method of providing options, we can have the lpr and lpd systems add options to the control file information. The prefix_z and append_z options allow us to put Z options at the start or end of the user provided list; the remove_z removes specified options from the list. The prefix_s_to_z and prefix_z_to_s options are usually used by the lpd server, and provides a simple way to have the SystemV lp -o option values put into the Z options value. This is very useful when existing SysV lp print spooling systems need to be interfaced to &LPRng; and the options need to be handled in a meaningful manner. The prefix_z_to_s is similarly used when you want to forward jobs to a SysV spooling system and have the options passed correctly. If present in the printer configuration, the various requested actions are are done by the lpr client, the lpd server on job reception, and the lpd server again on job processing. This redundancy ensures that jobs which are sent to this queue, arrive at this queue, or are processed by this queue have the options set appropriately. Record Queue Name in Control File Options used: qq FLAG Insert queue name into control file force_queuename=Queuename to be used The :qq use queuename option tells &LPRng; to record the queue name that a job was originally queued to in the control file as the Q information. The force_queuename=... entry forces the queue name to be the specified value. For example, the following printcap entry and lpr commands will result in a filter being passed the arguments shown below. #printcap pr1_landscape|pr1_portrait|pr_raw :filter=/.../filter :lp=/dev/pr :qq lpr -Ppr1_landscape lpr -Ppr1_portrait control file - Qpr1_landscape Qpr1_portrait filter command line: ... -Qpr1_landscape ... -Qpr1_portrait The force_queuename can be used for force a specific value into the control file Q information. john|tom|frank :filter=/.../filter :lp=/dev/pr :force_queuename=office lpr -Pjohn control file - Qoffice filter command line: ... -Qoffice Check For Nonprintable File Options used: check_for_nonprintable FLAG lpr checks for non-printable file ml=minimum number of printable characters By default lpr does not check files for for non-printable characters (i.e., escape characters) at the start of the print file. You can set the check_for_nonprintable flag to cause it to do so. The :ml value specifies the number of characters that are to be checked. Clearly, if it is 0, none will be checked. Job Filtering By LPR Options used: lpr_bounce FLAG lpr does filtering Some users would like the advantages of the filtering and processing capabilities of a lpd daemon without running a lpd daemon on their system. By having the lpr program process the job by passing it through the various filters and then send the output of the filters as the print job you can get the desired effect. # Simple example of an lpr_bounce entry bounce :lpr_bounce :lp=lp@remote :filter=/usr/local/bin/lpf The lpr_bounce flag, if present in the printcap entry, will force lpr to process the job using the specified filters and send the outputs of the filters to the remote printer for further processing. Restrict Queue Use to Group Members Options used: rg=Restricted group list The :rg value specifies a list of groups. If this value is present use of a printer or operation is restricted to only users in a particular group. This check is done by both the lpr client and the lpd server if the option is present. Fixing Bad Control Files and Metacharacters Options used: safe_chars=additional safe characters for control file RFC1179 defines a simple protocol and standard for print jobs to be interchanged between print spooling systems. Unfortunately, there were some major mistakes in not specifying the exact form that text would take when placed in the control file. By default, &LPRng; will brutally convert a non-conforming RFC1179 control file into one that is acceptable to most, if not all, existing RFC1179 implementations. In order to prevent problems with &LPRng; ruthlessly purges all characters but upper and lower case letters, spaces, tabs, and -_.@/:()=,+-% from the control file, replacing suspicious characters with underscore ( For some installations, the default set of safe characters may be overly restrictive. For example, vintage software may generate files with # characters in the J line of the control file. The replacement of this character by underscore (:safe_chars option allows the user to specify an additional set of safe characters in the lpd.conf configuration file(s). For example, :safe_chars=#" would allow the # and " characters to appear in the control file. Minimum Spool Queue Free Space Options used: minfree=Size in Kbytes If this value is non-zero, then the lpd receiving server checks to see that there is the specified number of Kbytes of file space available before accepting a job. If there is not enough space then the job is rejected. FQDN Host Information Options used: force_fqdn_hostname FLAG Update control file with FQDN name force_ipaddr_hostname FLAG Update control file with IP address of remote host use_shorthost FLAG short control and data file names Some lpr clients do not put a FQDN host name in their control file. The force_fqdn_hostname flag will cause lpd to put a FQDN host name in the control file. This option will assume that the domain is where the connection originated from. Similarly, the force_ipaddr_hostname flag will cause lpd to put a FQDN host name in the control file. Some systems cannot handle FQDN hostnames in control and data file formats. The use_shorthost option will send control and data files with very short names to the remote print server. <application/lpq/ - Status Monitoring Program The lpq program is the main method used to monitor queues. You can obtain the available options by using: h4: {306} % lpq -= lpq: Illegal option '=' usage: lpq [-aAclV] [-Ddebuglevel] [-Pprinter] [-tsleeptime] -a - all printers -c - clear screen before update -l - increase (lengthen) detailed status information additional l flags add more detail. -L - maximum detailed status information -n linecount - linecount lines of detailed status information -Ddebuglevel - debug level -Pprinter - specify printer -s - short (summary) format -tsleeptime - sleeptime between updates -V - print version information <application>lpq</application> Queue Selection (lpq -Pprinter, lpq -a) If no queue is specified, then the default queue is used, with -Pprinter@host causing a direct connection to the named host. The -a literal selects all queues. <application>lpq</application> Job Selection Jobs are selected by providing a job number, or If no selection information is provided or the all pattern is provided then all jobs in the queue are displayed. <application>lpq</application> Short Format (lpq -s) This is one line per spool queue: % lpq -sa t1@astart110 (printing disabled) 1 job t2@astart110 (routed/bounce to t1@h10.private) 0 jobs t3@astart110 (forwarding to t3a@h10.private) t3a@astart110 (forwarding to t2@h10.private) t4@astart110 (subservers t5, t6) 0 jobs t5@astart110 (serving t4) 0 jobs t6@astart110 (serving t4) 0 jobs Note that the name of the printer/host is first, followed by optional status information, followed by the number of jobs. Only printcap entries with spool queues have a jobs word in the last position. The -a literal forces status for all queues or the queues in the all printcap entry to be returned. The stalled_time (default 120 seconds) printcap option can be used to set a time after which active jobs will be reported as stalled. <application>lpq</application> Long Format (lpq, lpq -l, lpq -L) This is the default status display. It is a nicely formatted, extremely verbose format that is suitable for humble human interpretation. For example: % lpq -a Printer: t1@astart110 'Test Printer 1' (printing disabled) Queue: 1 printable job Server: no server active Status: finished operations at 09:44:00 Rank Owner/ID Class Job Files Size Time 1 papowell@lprng110+202228663 A 10663 /tmp/hi 3 20:22:29 Printer: t2@astart110 'Test Printer 2' (routed/bounce to t1@h10.private) Queue: no printable jobs in queue Status: finished operations at 16:30:08 Printer: t3@astart110 (forwarding to t3a@h10.private) Printer: t3a@astart110 (forwarding to t2@h10.private) Printer: t4@astart110 (subservers t5, t6) Queue: no printable jobs in queue Status: finished operations at 09:44:06 Server Printer: t5@astart110 (serving t4) Queue: no printable jobs in queue Status: finished operations at 09:44:06 Server Printer: t6@astart110 (serving t4) Queue: no printable jobs in queue Status: finished operations at 09:10:00 The lpq -l (longer information) option causes more of the status information to be printed. You can use increasing numbers of lpq -l options ( lpq -ll also works) to get more status. Use lpq -L for the maximum amount of status information. <application>lpq</application> Verbose Format (lpq -v) This uses an extension to the RFC1179 protocol, and is supported only by &LPRng;. The amount of information displayed is the brutal, and in effect does a total database dump of the LPD. This has been developed in order to provide diagnostic and status information for databases that need to keep track of job progress through a spool queue. % lpq -v Printer: t1@astart110 Comment: Test Printer 1 Printing: no Spooling: yes Queue: 1 printable job Server: no server active Status: accounting at end 'papowell@lprng110+094352860' at 09:44:00 Status: printing 'papowell@lprng110+094352860', \ closing device at 09:44:00 Status: printing 'papowell@lprng110+094352860', finished at 09:44:00 Status: subserver status 'JSUCC' for 'papowell@lprng110+094352860' \ on attempt 1 at 09:44:00 Status: finished operations at 09:44:00 Job: papowell@lprng110+202228663 status= 1 Job: papowell@lprng110+202228663 CONTROL= - Hh10.private - Ppapowell - J/tmp/hi - CA - Lpapowell - Apapowell@lprng110+202228663 - Qt1 - fdfA010663h10.private - N/tmp/hi - UdfA010663h10.private Job: papowell@lprng110+202228663 HOLDFILE= - active 0 - done 0 - hold 0 - move 0 .... Job Taking Too Long - Stalled Options used: stalled_time=seconds after which to report a stalled active job The stalled_time option is actually used by the lpd server to report that a job has been active more than the indicated time with no change in state. This is useful for spotting things such a printers that are out of paper, and so forth. Configuring Format and Displayed Information The following sections describe options that are used by the lpd server to control how it will return status information to a lpq request. Display Class Information Options used: class_in_status FLAG show class name in status Setting the class_in_status option causes the class name rather than priority to be displayed in the status information. Reverse Short and Long <application/lpq/ Formats Options used: reverse_lpq_format= FLAG reverse Various Solaris and other System V implementations support an RFC1179 interface to remote printers. Unfortunately, there is a problem in that when they send a status request, the status format is reversed. That is, when LONG status format is wanted, they send SHORT, and vice versa. The reverse_lpq_format= specifies a list of printers or IP addresses for which the lpd server will return LONG status when SHORT is requested, and vice versa. For example: reverse_lpq_format=*.eng.com,130.192.0.0/16 will cause hosts whose Fully Qualified Domain Name (FQDN) ends in eng.com or from subnet 130.192.0.0 to have reversed status returned. Status Line Length and Line Count Options used: return_short_status=return short short_status_length=short In order to be compatible with non-&LPRng; client programs, some administrators would like lpd to return a short or brief status to normal status queries. The return_short_status= specifies a list of printers or IP addresses for which the lpd server will return an abbreviated status when LONG status is requested. For example: return_short_status=*.eng.com,130.192.0.0/16 short_status_length#3 will cause hosts whose Fully Qualified Domain Name (FQDN) ends in eng.com or from subnet 130.192.0.0 to get only 3 lines of detailed status returned. <application>lpq</application> Status Format Determined by Requesting Host Address Options used: force_lpq_status=force In order to be compatible with non-&LPRng; client programs which are totally unpredictable, this allows the administrator to specify the format for The force_lpq_status= specifies a list of formats and printers or IP addresses for which the lpd server will return status in the specified format. The entry has the format KEY=list;KEY=list... where KEY is s for short and l for long format, and list is a list of hosts or IP addresses. For example: force_lpq_status=s=pc*.eng.com,130.192.12.0/24,l=sun*.eng.com will cause hosts whose Fully Qualified Domain Name (FQDN) matches pc*eng.com or from subnet 130.192.12.0 to get short status returned and hosts which match sun*.eng.com get long status. <application/lprm/ - Job Removal Program The h4: {307} % lprm -= lprm: Illegal option '=' usage: lprm [-A] [-a | -Pprinter] [-Ddebuglevel] (jobid|user|'all')* -a - all printers -A - use authentication -Pprinter - printer (default PRINTER environment variable) -Uuser - impersonate this user (root or privileged user only) -Ddebuglevel - debug level -V - show version information user removes user jobs all removes all jobs jobid removes job number jobid Example: 'lprm -Plp 30' removes job 30 on printer lp 'lprm -a' removes all your jobs on all printers 'lprm -a all' removes all jobs on all printers Note: lprm removes only jobs for which you have removal permission <application>lprm</application> Queue Selection (lprm -Pprinter, lprm -a) If no queue is specified, then the default queue is used, with -Pprinter@host causing a direct connection to the named host. The -a literal selects all queues. <application>lprm</application> Job Selection Jobs are selected by providing a job number, or If no selection information is provided then the user name performing the request is used as the selection pattern, and only the first job that matches is removed. The all pattern will match all jobs in the queue. The user must have permission to control the queue or to remove a selected job in order for job removal to succeed. <application/lpc/ - Administration Program The lpc command is the main way that the lpd server is controlled. Here is the help information displayed by the command: h4: {308} % lpc -= lpc: Illegal option '=' usage: lpc [-Ddebuglevel][-Pprinter][-Shost][-Uusername][-V] [command] with no command, reads from stdin -Ddebuglevel - debug level -Pprinter - printer or printer@host -Shost - connect to lpd server on host -Uuser - identify command as coming from user -V - increase information verbosity commands: active (printer[@host]) - check for active server abort (printer[@host] | all) - stop server class printer[@host] (class | off) - show/set class printing disable (printer[@host] | all) - disable queueing debug (printer[@host] | all) debugparms - set debug level for printer down (printer[@host] | all) - disable printing and queueing enable (printer[@host] | all) - enable queueing hold (printer[@host] | all) (name[@host] | job | all)* - hold job holdall (printer[@host] | all) - hold all jobs on kill (printer[@host] | all) - stop and restart server lpd (printer[@host]) - get LPD PID lpq (printer[@host] | all) (name[@host] | job | all)* - run Most of the lpc command line options are common to all &LPRng; Clients, with the exception of the -S server literal. This option allows the lpd host to be explicitly specified. The lpc commands can be classified as informational, queue management, problem management, job scheduling, and diagnostic. Informational Commands - status, flush, active, reread The lpc status command displays the current status of various activities of interest to the system administrator. This information includes the process ID of the server and other processes. During normal operation, when requested for job status information the lpc flush command will flush (delete) this cache information and cause the The lpc active command connects to the print server and gets the Process ID of the lpd process. This is useful to determine if the lpd server is running on the print server. The reread command connects to the lpd print server and requests that the server reread the printcap, lpd.conf, and lpd.perms database files. Queue Management - enable, disable, up, down The enable and disable commands enable and disable queuing or sending jobs to the print queue. The up command combines the enable and start commands while the down command combines the disable and stop commands. Printing Management - start, stop, up, down These commands are used to start and stop printing. The up combines start and enable, while down combines stop and disable. Problem Management - abort, redo, kill These commands are usually used when there is a problem printing a job. The abort command is used to kill off all printing activity associated with a job. It also has the side effect of stopping printing on the print queue. The redo command will redo or attempt to reprint a job. The kill command combines the abort and redo command, and is useful when there are problems with the job currently being printed and it should be reprinted. Job Scheduling - topq, holdall, noholdall, hold, release The topq command effectively puts the select job or jobs at the top of the queue for printing. The holdall, noholdall, and release commands implement a simple holding queue for jobs. By default, holdall is disabled. When the holdall command enables it, then jobs will remain in the spool queue until explicitly released with the release command. The hold command can also be used to hold individual jobs until released. Queue Management - class, redirect, move The class command is used to restrict printing to jobs whose class value, set using the lpr -C class option, is in the class list or matches one of the classes. This allows the administrator to restrict printing. For more details and an example of its use, see Form Support. The redirect command allows the administrator to accepts jobs at this queue and then to have them forwarded or redirected to another print queue. This is useful when a printer temporarily is unavailable. The move command allows an individual job (or jobs) to be forwarded or redirected to another print queue. <application/checkpc/ - Configuration Validation Utility The checkpc (check printcap file) is one of the most useful utilities in the &LPRng; package. It will read all the configuration files, printcap files and tests whether devices are set up correctly. Optionally, it will also set the permissions for spool directories and device files. Additionally, it will truncate the accounting and log files to a maximum size. Another use for checkpc is to remove old entries from queue directories. For a new installation, you will want to run checkpc -f -V to set the permissions right. The -f flag instructs the program to correct file permissions. If you don't run this as ROOT, you'll receive a warning about that fact and any chown(2) calls will (most likely) fail. The program reports everything it changes. Since it isn't too clever about some things (visit the man page), you should keep an eye on the output, and run it again if needed. If it keeps failing, change the permissions yourself. Maintenance Later, you will want to use checkpc for the daily maintenance of your system. I have this line in user lp's crontab: 32 5 * * * checkpc -t 10K -A3 -r >/dev/null 2>&1 This job will: truncate all log and accounting files to 10KB (-t 10K). Actually, it will keep the last 10K from the file, starting on a complete line. remove all stale files older than three days (-A3 -r). I'm redirecting output to /dev/null, because checkpc is a little noisy to my taste. (But too noisy is better than too silent :) Printcap Information You can use checkpc -V -P to examine printcaps and tell you what they contain. This is identical to the lpc server all operation, but with a higher level of verbosity. Printer Communication and Protocols Common communication methods between a printer and a host system are network connections, parallel ports, or serial ports; while Fibre Channel, SCSI, USB, FireWire, InfraRed, and other interesting technologies have been used, they are either very specialized or not directly support by the &LPRng; software. In this section we will discuss Network, Parallel Port, and Serial Printers, as well as the different protocols and standards that apply to them. Network Printers The most flexible and highest throughput printer interface is via a network (TCP/IP) connection. Most high performance printers have a built in network interface, or you can attach them to a printer server box which provides a network interface. The network interface usually supports multiple network printing protocols. The most common are the LPD (RFC1179), Socket API, AppSocket, SMB, and Novell Netware interfaces. &LPRng; directly supports the LPD (RFC1179) and Socket API interfaces, and you can use the smbclient program from the Samba Software Package for the SMB interface. RFC1179 (LPD) Connection In this mode of operation the print server actually operates as a very limited BSD print spooler. These limitations include: No error messages or status capability Limited or very primitive banner printing. On some systems it may be impossible to turn banner printing off. On most known print servers high connection activity caused by multiple systems attempting to get status or spool jobs may cause catastrophic failure of the printer. For the above reasons, using RFC1179 to transfer jobs to a printer should be regarded as the least desirable option. Please see The RFC1189 Protocol for a detailed discussion of the RFC1179 protocol. In order to use the RFC1179 transfer operation you must have a printcap entry for the printer that provides: The IP address or name of the printer that can be resolved to an IP address The name of the spool queue. In practice, this is usually used only to determine which of several printer ports on the print server the job will be sent to, or what type of processing the print server will do. Most cards usually do not do any processing and simply pass the job through to the printer. The following is an example of a simple printcap entry that can be used to send a job to a remote printer using the RFC1179 protocol: # &LPRng; syntax # :lp value is 'where to print the job' lp: :lp=raw@10.0.0.1 # OR Vintage BSD Print Spooler Syntax # (&LPRng; supports this as well) # :rp = remote printer, :rm = remote machine or host lp: :rp=raw:rm=10.0.0.1 If you wish to transfer jobs to a print spooler without using the full &LPRng; lpr program, the Perl lpr_in_perl program in the &LPRng; Distribution UTILS directory can be used for testing and tutorial purposes. Socket API The Socket API is a very flexible job transfer protocol. It is widely support by most Print Server manufacturers, with the Hewlett Packard JetDirect setting the de facto standard. The Socket API is extremely simple. The user establishes a connection to TCP/IP port on the Printer or Network Print spooler. The HP JetDirect uses port 9100 by default, but other ports are used as well. This connection may be refused if the printer is busy printing a job. When the network connection is established to a system which has an internal printer or for which the Network Print Spooler is an integral part of the system, the printer usually flushes all internal buffers and readies itself to receive a new job. However, when you are using an external Print Server box, you may need to send specific initialization sequences to the printer to ensure that it is reset correctly and is ready to receive new jobs. When the connection is made, all bytes sent to the connection are either transferred to and external interface to directly to a print buffer used by the printer's Print Engine. The connection is bidirectional, and information sent to the external port by an external printer or error messages and status generated by the printer's Print Engine will be transferred over the data link to the user. The Network Print spooler will keep the connection open until it is closed by the user. During this period it may continue to report status or other information such as printer On Line, paper outages, and so forth. If the connection to the printer is half-closed, that is, the shutdown() network system call is used to indicate to the remote printer that no further data will be sent, then the printer may immediately terminate the network connection. This means that no further network or status messages will be sent to the user. If the connection is to a External Print Server, then usually the connection can be immediately re-established. It is the responsibility of the user to ensure that a the printer has finished its work before sending a new job. If the connection is to an internal Print Server, then usually the printer will not allow the connection to be made, or will refuse all data transfers on the connection until the printer finishes with the previous job and all internal buffers have been cleared. The following is a sample printcap showing how to use the Socket API: lp: # make a socket connection to port 9100 :lp=10.0.0.2%9100 You can use the netcat utility by Hobbit Hobbit@avian.org to test that the Socket interface is available and working. If ellipse.ps is a test file, then: The simplest and easiest way to print a file to a network printer appears nc printer.ip.addr 9100 < file Example: nc 10.0.0.25 9100 < ellipse.ps AppSocket TCP/IP Protocol The AppSocket interface is supported by Tektronix and some other printer vendors. It is similar to the Socket API, with a couple of significant differences. The printer has two ports for network connections: a TCP port 9100 for TCP/IP stream connections and a UDP port for UDP packet connections. When a 0 length UDP packet or a UDP packet containing only CR/LF is sent to UDP port 9101, the printer will return a packet to the sender containing print status information. This information indicates the printers current status (busy, idle, printing) and any error conditions. The format, reliability, and repeatability of the UDP format and information is totally undocumented, and the UPD port facility should be regarded as, at best, an advisory function of the printer. To send a job to the printer, a connection to TCP port is made. This connection will be refused while the printer is busy or has a connection to another host. When the TCP connection is established, the information to be printed can be sent over the TCP connection. Bytes sent on this stream will be placed in the input buffer of the Print Engine and processed. An end of job (EOJ) sequence indication in the data stream will cause the printer to terminate the connection. This is different than the Socket API, where the printer will keep the connection open. This means that if the PostScript CTRL-D (end of job) character is sent in a job, then the connection will be terminated. Some models of printers modify this behavior slightly and will not terminate the connection, but will simply ignore any data following the EOJ indication. Some printers support bidirectional AppSocket communication, and while the connection is open will return error indications or status information. Once all the data has been received and the job has finished printing, the connection will be terminated by the printer. The ifhp filter, one of the helper programs for &LPRng;, is used with &LPRng; to provide AppSocket support. For details, please see the IFHP-HOWTO in the ifhp Distribution and Tektronix P450 and Family for details. The following is a typical printcap entry for the AppSocket protocol. The actual network connection to the printer is made by the ifhp filter: lp: # &LPRng; opens a dummy connection for consistency :lp=/dev/null # we pass the ifhp filter options indicating that the # Tektronics printer will need the appsocket protocol # and to use port 35 at 10.0.0.1 to make the connection # The ifhp filter may open and close the connection several # times during the file transfer in order to ensure that # the printer handles the job correctly. :ifhp=model=tek,appsocket,dev=10.0.0.1%35 :filter=/usr/local/libexec/filters/ifhp Network Print Server Boxes A The print server may support multiple printing protocols, such as RFC1179 (TCP/IP printing using the LPD print protocol), Novell Printer Protocols, SMB print protocols, and AppleTalk protocols. One of the observed problems with Network Print servers is that while they can usually support one protocol and one user at a time quite well, when you try to use multiple protocols and/or multiple users try to transfer print jobs to the printer, the printer may behave in a very odd manner. Usually this results in a printer failing to finish a job currently being printed, and unable to accept new jobs. Several of the newer models of print servers have Simple Network Management Protocol (SNMP) agents built into them, and can provide detailed information about their internal functions. By using a SNMP manager such as SunNetmanage or HP-Openview, you can monitor your network printers activities. I recommend that you use only a single protocol to send jobs to the printer. If you can, I also recommend that you use a print spooler and have only a single host system send a job to the printer. My best advice on connecting to network printers is not to use the the built-in LPD server, but to use the direct TCP/IP connection to the print engine. Usually this is done to particular TCP/IP port on the printer. For the HP JetDirect and other HP products, this is usually TCP port 9100. Once you have the direct connection, you can now use various filters to preprocess the print job, insert PJL and PCL commands, or convert text to PostScript or PCL for better print quality. Network Print Server Configuration Information The following is a list of print server manufacturers, models, and with hints on how to access these boxes with various protocols. Network Print Server Configuration Information ManufacturerModelRFC1179 Port Name (rp=XXX)Send to TCP portCannon PrinterCannon 460 PS, no hard drivexjdirect- Unknown if supported -Cannon 460 PS hard drivexjprint - print immediately,xjhold - print later- Unknown if supported -Digital Products Inc.NETPrint Print ServerPORTn, where n is port on server- Unknown if supported -Electronics For Imaging Inc.Fiery RIP i seriesnormalq or urgentq- Unknown if supported -Fiery RIP XJ seriesxjprint- Unknown if supported -Fiery RIP XJ+ and SI seriesprint_Model, e.g. print_DocuColor- Unknown if supported -Fiery models ZX2100, ZX3300, X2, X2eprint- Unknown if supported -Emulex Corp.NETJet/NETQue print serverPASSTHRU- Unknown if supported -Extended Systems Inc.ExtendNet Print ServerPrintern, where n is port on server- Unknown if supported -Hewlett-PackardJetDirect interface cardraw9100Hewlett-PackardJetDirect Multiport Serverport 1 - raw1, port 2 - raw2, etc.port 1 - 9100, port 2 - 9101, etc.I-DataEasycom 10 Printserverpar1 (parallel port 1)- Unknown if supported -Easycom 100 PrintserverLPDPRT1- Unknown if supported -IBMNetwork Printer 12, 17, 24, and 24PSPASS- Unknown if supported -LantronixEPS1, EPS2EPS_X_S1 (serial) port 1, EPS_X_P1 (parallel) port 2, etc.3001 (port 1), 3002 (port 2), etc.QMSVarious ModelsRAW35 (AppSocket)TektronixTektronix printer network cardsPS (PostScript), PCL (PCL), or AUTO(Auto-selection between PS, PCL, or HPGL). Not reliable.9100 (AppSocket on some models)Rose ElectronicsMicroserve Print Serverslp9100XeroxModels 4505, 4510, 4517, 4520PASSTHRU2501 (AppSocket on some models)Model 4512PORT110001 (programmable)Model N17RAW9100Models N24 and N32RAW2000Models 4900, 4915, 4925, C55PS2000Document Centre DC220/230lp- Unknown if supported -
All company, brand, and product names are properties of their respective owners.
HP JetDirect Interface The HP JetDirect Interface is one of the most widely used for network printers. For this reason it also has the most widely known set of problems. The user is strongly urged to upgrade to the latest version of firmware available for the unit. Problems with older versions of firmware include system lockups that require powerup level resets. Newer versions of the HP JetDirect Interface have a Web Browser based configuration system. After you have assigned an IP address to the printer you can connect to the configuration port and configure the printer using the browser. If you run into configuration problems then you will most likely need to use the Microsoft Windows based JetDirect Configuration Software to reset or reconfigure the printer. Older HPJetDirect cards can be configured only through through the front panel or through a set of network files. Here is a summary of the methods used from UNIX systems, or to use when you are desperate, to configure the printer. Resetting To Factory Defaults Most internal HP JetDirect print servers can be reset to factory defaults (or cold-reset) by turning the printer off and holding down the Setting Up IP Networking and Address You can set the network address from the front panel. Reset the printer, put it in offline mode. and then use the MENU, +-, SELECT keys as follows: MENU -> MIO MENU (use MENU to display MIO MENU) ITEM -> CFG NETWORK=NO* + -> CFG NETWORK=YES ENTER -> CFG NETWORK=YES* ITEM -> TCP/IP=OFF* (use ITEM to display TCP/IP) + -> TCP/IP=ON ENTER -> TCP/IP=ON* ITEM -> CFG TCP/IP=NO* (use ITEM to display TCP/IP) + -> CFG TCP/IP=YES ENTER -> CFG TCP/IP=YES* ITEM -> BOOTP=NO* (Enable BOOTP if you want to - see below) ITEM -> IP BYTE 1=0* This is IP address MSB byte. Use +- keys to change value, and then ENTER to change Use ITEM keys to get IP BYTE=2,3,4 ITEM -> SM BYTE 1=255* This is the subnet mask value Use +- keys to change value, and then ENTER to change Use ITEM keys to get IP BYTE=2,3,4 ITEM -> LG BYTE 1=255* This is the Syslog server (LoGger) IP address Use +- keys to change value, and then ENTER to change Use ITEM keys to get IP BYTE=2,3,4 ITEM -> GW BYTE 1=255* This is the subnet gateway (router) IP address Use +- keys to change value, and then ENTER to change Use ITEM keys to get IP BYTE=2,3,4 ITEM -> TIMEOUT=90 This is the connection timeout value. It puts a limit on time between connections. A value of 10 is reasonable. BOOTP Information If you have a bootp server, you can put this information in the bootptab file. To use this, you must enable the bootp option on the printer. The T144 option specifies a file to be read from the bootp server. This file is read by using the TFTP protocol, and you must have a TFTPD server enabled. Here is a sample bootptab entry. # Example /etc/bootptab: database for bootp server (/etc/bootpd). # Blank lines and lines beginning with '#' are ignored. # # Legend: # # first field -- hostname # (may be full domain name) # # hd -- home directory # bf -- bootfile # cs -- cookie servers # ds -- domain name servers # gw -- gateways # ha -- hardware address # ht -- hardware type # im -- impress servers # ip -- host IP address # lg -- log servers # lp -- LPR servers # ns -- IEN-116 name servers # rl -- resource location protocol servers # sm -- subnet mask # tc -- template host (points to similar host entry) # to -- time offset (seconds) # ts -- time servers # # Be careful about including backslashes where they're needed. # Weird (bad) things can happen when a backslash is omitted # where one is intended. # peripheral1: :hn:ht=ether:vm=rfc1048: :ha=08000903212F: :ip=190.40.101.22: :sm=255.255.255.0: :gw=190.40.101.1: :lg=190.40.101.3: :T144="hpnp/peripheral1.cfg": If you are using the T144 option, you will need to create the configuration file. The sample configuration file from the HP Direct distribution is included below. # # Example HP Network Peripheral Interface configuration file # # Comments begin with '#' and end at the end of the line. # Blank lines are ignored. Entries cannot span lines. # Name is the peripheral (or node) name. It is displayed on the # peripheral's self-test page or configuration plot, and when sysName # is obtained through SNMP. This name can be provided in the BOOTP # response or can be specified in the NPI configuration file to # prevent the BOOTP response from overflowing the packet. The domain # portion of the name is not necessary because the peripheral does # not perform Domain Name System (DNS) searches. Name is limited to # 64 characters. name: picasso # Location describes the physical location of the peripheral. This # is the value used by the interface for the MIB-II sysLocation # object. The default location is undefined. Only printable ASCII # characters are allowed. Maximum length is 64 characters. location: 1st floor, south wall # Contact is the name of the person who administers or services the # peripheral and may include how to contact this person. It is # limited to 64 characters. This is the value used by the interface # for the MIB-II sysContact object. The default contact is undefined. # Only printable ASCII characters are allowed. Maximum length is 64 # characters. contact: Phil, ext 1234 # The host access list contains the list of hosts or networks of # hosts that are allowed to connect to the peripheral. The format # is "allow: netnum [mask]", where netnum is a network number or a # host IP address. Mask is an address mask of bits to apply to the # network number and connecting host's IP address to verify access # to the peripheral. The mask usually matches the network or subnet # mask, but this is not required. If netnum is a host IP address, # the mask 255.255.255.255 can be omitted. Up to ten access list # entries are permitted. # to allow all of network 10 to access the peripheral: allow: 10.0.0.0 255.0.0.0 # to allow a single host without specifying the mask: allow: 15.1.2.3 # Idle timeout is the time (in seconds) after which an idle # print data connection is closed. A value of zero disables # the timeout mechanism. The default timeout is 90 seconds. idle-timeout: 120 # A community name is a password that allows SNMP access to MIB # values on the network peripheral. # Community names are not highly secure; # they are not encrypted across the network. The get community name # determines which SNMP GetRequests are responded to. By default, # the network peripheral responds to all GetRequests. The get # community name is limited to 32 characters. # # For hpnpstat and hpnpadmin, the community name can be stored in # /usr/lib/hpnp/hpnpsnmp. get-community-name: blue # The set community name is similar to the get community name. The # set community name determines which SNMP SetRequests are responded # to. In addition, SetRequests are only honored if the sending host # is on the host access list. By default, the network peripheral # does not respond to any SetRequests. The set community name is # limited to 32 characters. # # The set community name can come from /usr/lib/hpnp/hpnpsnmp if it # is the same as the get community name. We recommend that the set # community name be different from the get community name though. set-community-name: yellow # SNMP traps are asynchronous notifications of some event # that has occurred. # SNMP traps are useful only with network management software. # Traps are sent to specific hosts and include a trap community # name. Up to four hosts can be sent SNMP traps. # The trap community name is limited to # 32 characters. The default name is public. trap-community-name: red # The SNMP trap destination list specifies systems to which SNMP # traps are sent. Up to four IP addresses are allowed. If no # trap destinations are listed, traps are not sent. trap-dest: 15.1.2.3 trap-dest: 15.2.3.4 # The SNMP authentication trap parameter enables or disables the # sending of SNMP authentication traps. Authentication traps indicate # that an SNMP request was received and the community name check # failed. By default, the parameter is off. authentication-trap: on # The syslog-facility parameter sets the source facility identifier # that the card uses when issuing syslog messages. Other facilities, # for example, include the kernel (LOG_KERN), the mail system # (LOG_MAIL), and the spooling system (LOG_LPR). The card only allows # its syslog facility to be configured to one of the local user values # (LOG_LOCAL0 through LOG_LOCAL7). The selectible option strings, # local0 through local7 (configured to LOG_LOCAL0 through LOG_LOCAL7, # respectively) are case insensitive. The default syslog-facility # for the card is LOG_LPR. syslog-facility: local2 # This parameter allows the card to treat hosts on other subnets as # if the hosts were on the card's subnet. This parameter determines # the TCP Maximum Segment Size (MSS) advertised by the card to hosts # on other subnets and affects the card's initial receive-window # size. The card will use a TCP MSS of 1460 bytes for local hosts, # and 536 bytes for a non-local host. The default is off, that is, # the card will use the maximum packet sizes only on the card's # configured subnet. # # The configuration utility does not allow access to this parameter. # If you want to configure it, you must manually edit the NPI # configuration file and add it to the bottom of the entry for the # network peripheral. subnets-local: on # This parameter affects how the card handles TCP connection requests # from the host. By default, the JetDirect MPS card will accept a # TCP connection even if the peripheral is off-line. If this parameter # is set to "on", then the card will only accept a TCP connection # when the peripheral is on-line. old-idle-mode: off Telnet Configuration You can telnet to the JetDirect interface and use the command line mode. There is a very simple help facility available that you can invoke by entering Different versions of HP JetDirect cards have different commands. You will have to experiment to find out which ones are supported. Disabling Banner Page Generation You can do this by making a telnet connection to the JetDirect interface and use the command line mode. There is a very simple help facility available that you can invoke by entering The configuration command Problems With Network Print Servers Most of the Network Print Servers are implemented using extremely simply software. The following is a list of some problems and what options the &LPRng; software uses to handle them. Network Print Server Not Responding Only a single TCP/IP connection is accepted at a time. This means that when one user is sending a job then the unit will not accept other connections. There is another side effect of this problem, which is that some implementations will accept a network connection but not read any data from the connection until the previous connection is finished. The deal with these problems the Printing Job Files and Opening the Output Device for details. Network Print Server Does Not Handle LPQ, LPRM Some Network Print Servers do not respond to :remote_support=RMQVC R = lpr, M = lprmg, Q = lpq, V = lpq -v, C = lpc :remote_support=R # printer only handles LPR Incomplete Job Transfers This is the result of a defective or buggy TCP/IP stacks. A common problem is the habit that some Network Print Servers occaisionally discard data at the end of a print job when a network connection is The Normal Termination for more details. lp:lp=lp@remote # shutdown(fd,WRITE) connection, wait for end lp:lp=lp@remote:half_close@ # close() connection and do not wait Printing to a SMB (MicroSoft) Printer Microsoft use the SMB (Simple Message Block) protocol to transfer files and print jobs to hosts and printers. SMB can be used over TCP/IP, NetBEUI, IPX, and other lower level network protocols. Unfortunately, most printers do not provide detailed status or error reports when using the SMB protocol. There are a very large number of printers that have deficient SMB support that causes problems when used in a high traffic or high throughput environment. It is highly recommended that this protocol not be used unless there is no alternative. If you have a printer or a remote print spooler that supports SMB You can use the SAMBA smbclient program to send a print job to an SMB client. The following is a sample Shell Script script which you can use: #!/bin/sh -x # This script is an input filter for printing on a unix machine. It # uses the smbclient program to print the file to the specified smb-based # server and service. # The 'smb' printcap entry below shows how to configure LPRng # for printing # # smb: # :lp=|/usr/local/samba/smbprint # :sd=/var/spool/smb: # :filter= ... filter ... # # The /var/spool/smb/.config file should contain: # server="PC_SERVER" # service="PR_SHARENAME" # password="PASSWORD" # # Set PC_SERVER to the server, PR_SHARENAME to the printer, # and PASSWORD to the password for this service. # # E.g. # server=PAULS_PC # service=CJET_371 # password="" # # config_file=.config if [ -f $config_file ] ; then eval `/bin/cat $config_file` fi # # NOTE You may wish to add the line `echo translate' # if you want automatic # CR/LF translation when printing. ( # echo translate echo "print -" /bin/cat ) | /usr/local/bin/smbclient "\\\\$server\\$service" \ "$password" -U "$server" -N -P 1>&2 If the above script was in /usr/local/libexec/filters/smbprint, the printcap entry for this printer would be: pauls_pc: :sd=/var/spool/lpd/%P # we filter the output :lp=|/usr/local/libexec/filters/smbprint # you can add filters if you want to do specific actions :ifhp=model=hp4 :filter=/usr/local/libexec/filters/ifhp Printing to AppleTalk Printers The netatalk package comes with the pap program that can be used to transfer jobs using the AppleTalk protocol. A printcap entry for a network printer looks like the following: atalk: :lp=| -$ /usr/local/atalk/bin/pap -e -p "npbname" :sd=/var/spool/lpd/atalk :ifhp=model=ps,status@ :filter=/usr/local/libexec/filters/ifhp The pap program must be SETUID root and executable only by root or group daemon. This can be done by using the following script: h4: {309} cd /usr/local/atalk/bin h4: {310} chown root pap h4: {311} chgrp daemon pap h4: {312} chmod 550 pap h4: {313} chmod s+u pap Parallel Port Printers When installing a parallel port printer, there are three elements of concern: the physical hardware connection (cable and connectors), the IO device and Operating System support, and finally the print spooler support for the device. Originally most parallel port devices were strictly write only with a minimum amount of error information - only hardware signals for online, out of paper and fault were present. Due to the lack of any other way to interface devices to the Intel based PC platform, desperate hardware designers in search of a cheap (free?) way to interface their IO devices developed ways to manipulate the signals and do bidirectional communication. Needless to say, no two companies developed the same methods, and no two companies were compatible with any other companies method. In 1994, the IEEE 1284 standard was first developed,. The following information is courtesy of Warp Nine Engineering, of San Diego, CA, from the http://www.fapo.com/1284int.htm web page. When IBM introduced the PC, in 1981, the parallel printer port was included as an alternative to the slower serial port as a means for driving the latest high performance dot matrix printers. The parallel port had the capability to transfer 8 bits of data at time whereas the serial port transmitted one bit at a time. When the PC was introduced, dot matrix printers were the main peripheral that used the parallel port. As technology progressed and the need for greater external connectivity increased, the parallel port became the means by which you could connect higher performance peripherals. These peripherals now range from printer sharing devices, portable disk drives and tape backup to local area network adapters and CD ROM players. The problems faced by developers and customers of these peripherals fall into three categories. First, although the performance of the PC has increased dramatically, there has been virtually no change in the parallel port performance or architecture. The maximum data transfer rate achievable with this architecture is around 150 kilobytes per second and is extremely software intensive. Second, there is no standard for the electrical interface. This causes many problems when attempting to guarantee operation across various platforms. Finally, the lack of design standards forced a distance limitation of only 6 feet for external cables. In 1991 there was a meeting of printer manufacturers to start discussions on developing a new standard for the intelligent control of printers over a network. These manufacturers, which included Lexmark, IBM, Texas Instruments and others, formed the Network Printing Alliance. The NPA defined a set of parameters that, when implemented in the printer and host, will allow for the complete control of printer applications and jobs. While this work was in progress it became apparent that to fully implement this standard would require a high performance bi-directional connection to the PC. The usual means of connection, the ordinary PC parallel port, did not have the capabilities required to meet the full requirements or abilities of this standard. The NPA submitted a proposal to the IEEE for the creation of a committee to develop a new standard for a high speed bi-directional parallel port for the PC. It was a requirement that this new standard would remain fully compatible with the original parallel port software and peripherals, but would increase the data rate capability to greater than 1M bytes per second, both in and out of the computer. This committee became the IEEE 1284 committee. The IEEE 1284 standard, "Standard Signaling Method for a Bi-directional Parallel Peripheral Interface for Personal Computers", was approved for final release in March of 1994. Even if your hardware has support for the IEEE 1284 high speed bidirectional data transfers, your Operating System drivers must support it. Unfortunately, there is no universal agreement on the capabilities that should be provided by the low level printer port device drivers, and the method for supporting them. A good example of the problem of OS support and drivers is given by the Linux Parallel Port group, currently headed by Tim Waugh, and which is documented in the http://people.redhat.com/twaugh/parport/ and http://people.redhat.com/twaugh/parport/html/parportguide.html web pages. Given this state of affairs, it should be no surprise that there is no support for bidirectional parallel port printers in &LPRng;. In fact, it turns out that there are severe problems with many Unix implementations that cause extreme headaches. These include: While a parallel port may be opened Read/Write, a read() will either block indefinitely or cause a major system failure (crash). There is no buffering of data in the low level driver; that is, once a write() starts, then the process will block until the data is written out. If the write() call is interrupted by a signal and not immediately restarted, then the status returned by the write() may be an error indication (-1) or the numbers of bytes that were actually written. If an error is returned, then an unknown number were written and the print job must be aborted. Many Operating systems do not implement a select() functionality for the parallel port, which means that it is difficult to do a multi-threaded implementation. Instead, a polling method must be used. The good news is that on all known systems, if the parallel port device is opened exclusively for writing, and a blocking write() is used, and the write() is not interrupted, and there are no device errors, then data is delivered correctly to the device. In most UNIX systems the printer port has the name /dev/lpt, /dev/prn, or something similar. On most systems the dmesg utility will print a list of IO devices found during system configuration. Use the following commands to get the information and scan for the device. You should also make sure that the printer device is available. dmesg >/tmp/a grep lp /tmp/a ls /dev/lp* Gordon Haverland haverlan@agric.gov.ab.ca supplied this little script, that will assist with this: #!/bin/sh #set -v -x # uncomment for debugging PATH=/bin:/usr/bin printer= for printer in /dev/lp* ; do echo PRINTER TEST to $printer 1>&2 for i in 1 2 3 4 5 6 7 8 9; do echo PRINTER $printer $i > $printer; done echo -e \\r\\f > $printer done exit 0; If your printer is connected to the device name you provided, then you should get a page of something out. If the output suffers from the Serial Printers If your printer is attached by a serial line, then you may need to set the serial line characteristics before sending the job to the printer. Here are a set of guidelines to following when attaching a serial port printer to a serial line. 1. Check to make sure that the line is not enabled for login. Logins are usually managed by the getty (BSD) or ttymon (Solaris, SystemV). Check your system documentation and make sure that these daemons are not managing the serial line. 2. Check the permissions and ownership of the serial line. For the most easy testing, set the permissions to 0666 (everybody can open for reading and writing). After you have made sure that you can send jobs to the printer, you might want to change the ownership of the serial line to the 3. Make sure that you can print a test file on the printer via the serial port. This may require setting the line characteristics and then sending a file to the printer. You should try to use 8 bit, no parity, with hardware flow control and no special character interpretation, and definitely no LF to CR/LF translation. The problem is that different versions of UNIX systems have different sets of stty(1) commands to do this. The following simple test script can help in this. #!/bin/sh # 9600, no echo, no CR FLAGS= 9600 -raw -parenb cs8 crtscts DEV= /dev/tty01 (stty $FLAGS; stty 1>&2; cat $1 ) <$DEV >$DEV This shows using stty to set the flags, then to print the current settings, and then using cat a file to the output. If you attach a dumb terminal to the serial port, you can even use this script to ensure that input from the device is echoed to the output with the correct speed, parity, etc. Experience has shown that serially connected printers are the least reliable and lowest speed. Where possible, it is strongly recommended that they be attached to a network print box which will provide a Socket API interface and handle the low level network to serial port protocol conversions.
Printcap Database As described in the Print Spooling Overview, the heart of the &LPRng; system is information in the printcap file. The printcap information specifies: The print queues available to users. How client programs communicate with the lpc print server. The configuration, location, and other information for each print queue on the print server. How the lpd server processes jobs in each print queue. In order to explain a complex subject, we will start with a set of simple printer configurations, and explain the purpose and effect of each entry in the printcap. For details about individual printcap options, see the Index To All The Configuration and Printcap Options to find a specific printcap option and its effects. The Printcap Parsing Rules Options used: client FLAG printcap entry valid only for client programs oh= hosts where printcap entry valid server printcap entry valid only for lpd server tc add named printcap entry contents In this section, we will discuss the remaining tricky parts of the &LPRng; printcap database: combined client and server printcaps, host specific printcap entries, and the tc include facility. The following is a complete description of how a printcap file is processed: When processing a printcap file, the &LPRng; software reads and parses each entry individually. Leading whitespace is removed. Lines starting with # and blank lines are ignored. Lines ending with \ will have the \ discarded, and all lines of a printcap entry are joined by removing the line separators (\n) and replacing them with a space. The printcap entry is parsed, and the printcap name, aliases, and options are determined. Colons : act as option separators, and leading and trailing whitespaces are removed. Options are sorted and except for the tc=... option only the last option setting is retained. If an option value requires a colon, then the Client programs will discard a printcap entry with a server option and server programs will discard a printcap entry with a client options. The oh (on this host) option specifies a list of IP addresses and mask pairs or glob strings which are used to determine if this printcap entry is valid for this host (see discussion below). After the above processing, if there is an existing termcap entry with the same name, the two sets of options are combined, with the last option setting retained except for the tc entries which are combined. When a printcap entry is actually used, the printcap entries listed by the tc include option are extracted and combined in order. This allows include entries to appear after the referring printcap entry. Then printcap options will be combined with the included ones. This has the effect that the options specified in the printcap entry will override the ones from the tc included entries. Finally, each string printcap option with a %X value has %X replaced by the following values. Unspecified values will not be modified. KeyValuePurpose %Pprintcap entry primary name %Qqueue requested %hshort host name (host) %Hfully qualified host name (host.dns.whatever) %Rremote printer (rp value) %Mremote host (rm value) %Ddate in YYYY-MM-DD format When parsing multiple printcap files, these are processed in order, and all of their printcap entries are combined according to the above procedures. The tc resolution and %X expansion is done after all the files have been processed. The following examples show how to use the above rules to your advantage. You can combine both client and server printcap information in a single file as well as dividing a printcap entry into several parts. Here is an example: # seen by both client and server lp1:lp=lp@pr1:mx=100 lp1:sd=/usr/local/spool/lp1:mx=0 # seen only by client lp2:lp=lp@pr2:client # seen only by server lp2:lp=/dev/lp:server Printcap entries with the same name are combined. The first printcap entry, lp1, the information is seen by both client and server. The next printcap entry, with the same name lp1, will be combined with the first one. The order of options is important - the entries are scanned in order and an option will have the last value set. Thus, after having read both the lp1 printcap entries, both client and server will have: lp1:lp=lp@pr1 :mx=0 :sd=/usr/local/spool/lp1 The lp2 has a client and server version. This is recommended when complex printcaps on multiple hosts and servers are used. Thus, the &LPRng; clients will see: lp1 :lp=lp@pr1 :mx=0 :sd=/usr/local/spool/lp1 lp2 :client :lp=lp@pr2 and the server will see: lp1 :lp=lp@pr1 :mx=0 :sd=/usr/local/spool/lp1 lp2 :lp=/dev/lp :server If you have multiple printers of the same type whose configuration is almost identical, then you can define a set of tc only printcap entries containing common information and use the tc include facility. Printcap entry names may start with period (.), question mark (?), or exclamation mark (!), followed by one or more alphanumeric , underscore (_) or hyphen (-) characters. Queue or printer names start with an alphanumeric character. Printcap entries whose names do not start with an alphanumeric character can only be used as targets of the tc include facility. For example: .hp: :sd=/usr/local/spool/%P :mx=0 hp1:tc=.hp,.filter :lp=lp@10.0.0.1 hp2:tc=.hp,.filter :lp=lp@10.0.0.2 .filter :filter=/usr/local/libexec/filters/ifhp The .hp and .filter printcap entities are not spool queue definitions. After tc include processing is completed, the printcap information would resemble: hp1 :lp=lp@10.0.0.1 :filter=/usr/local/libexec/filters/ifhp :mx=0 :sd=/usr/local/spool/%P hp2 :lp=lp@10.0.0.2 :filter=/usr/local/libexec/filters/ifhp :mx=0 :sd=/usr/local/spool/%P The %X processing will replace %P with the printcap name, so we would have: hp1 :lp=lp@10.0.0.1 :filter=/usr/local/libexec/filters/ifhp :mx=0 :sd=/usr/local/spool/hp1 hp2 :lp=lp@10.0.0.2 :filter=/usr/local/libexec/filters/ifhp :mx=0 :sd=/usr/local/spool/hp2 Simple Client Printcap Entry Options used: client FLAG client printcap entry lp=destination printer information rm=remote host (machine) rp=remote printer I'll use this simple example to explain the basics of the &LPRng; printcap format and introduce some of the &LPRng; network configuration options. Here is a simple printcap file used to provide client programs (lpr, lprm, etc) with remote printer and server information. # printer lp1 lp1|printer1 :rm=localhost # printer lp2 with continuation lp2:\ :lp=pr@10.0.0.1:client # printcap lp3, to printer pr, with overrides lp3:rp=pr:rm=hp.private :force_localhost@ # Simplest possible printcap entry - defaults for everything lp4 Lines starting with a # sign are comments, and all leading and trailing whitespace, i.e. - spaces, tabs, etc, are ignored. Empty lines are ignored as well. A printcap entry starts with the printcap entry name, followed by one or more aliases, followed by one or more options. In the above example we have three printcap entries: lp1 with an alias printer1 and lp2, lp3, and lp4 with no aliases. Aliases start with the | character and options with the : character; tabs and spaces before and after the | or : characters and at the start and end of lines are ignored. You can use backslash (\) at the end of a line to create a multi-line value for an option. The backslash will cause the next line to be appended to the current line; watch out for comments and ends of printcap entries if you use this facility. As you can see from the example, there is no Name printcap entry - this is part of the cm option on the previous line. Options take the form of a keyword/value pair, i.e.- :option=value :option#value (legacy, not advised for new systems) :option :option@ Option names are case insensitive, but option values are not. While Ts and ts are the same option name, ts=Testing and ts=testing have their case preserved. A string or integer value is specified by option=value or option#value. The use of the legacy option#value form is NOT recommended as some preprocessors and database systems will treat # as the start of a comment and delete the remainder of the line. This has caused great consternation for sysadmins who wonder why their NIS distributed printcap entries have been mysteriously truncated. If you want to set a string option to empty value, use option=. The option will set it to 1. If an option value contains a colon, then use the C (or Perl or Tck/Tk) string escape \072 to represent the value. Boolean options are set TRUE (1) if no value follows the keyword and FALSE (0) by appending a @. For example sh will set sh to TRUE and sh@ to FALSE. There may be multiple options on the same line, separated by colons. Now let's examine the first printcap entry in detail. It is reproduced here for convenience: # printer lp1 lp1|printer1 :rm=localhost We start with a comment, followed by the printcap entry name and and alias. Aliases are useful when you want to refer to a single printer or print queue by different names. This can be useful in advanced printcap and print queue setups. By default, the remote printer name is the printcap entry name. The rm (remote machine or host) option specifies the name or IP address of the lpd host running lpd. In this example the remote host is localhost or the machine that the client is running on and we assume that the lpd server is running on the localhost. Thus, we would communicate with printer lp1@localhost. Let's look at the next printcap entry: # printer lp2 with continuation lp2:\ :lp=pr@10.0.0.1:client The lp2 printcap entry illustrates the use (and abuse) of the \ continuation. If you think about this, we have really defined a printcap entry of the form: lp2: :lp=pr@10.0.0.1:client Luckily, &LPRng; ignores empty options like ::. While it is strongly recommended that \ be avoided it may be necessary for compatibility with other system utilities. The lp=pr@10.0.0.1 literal is an alternate way to specify a remote queue and server. If the force_localhost default is being used, then the &LPRng; clients will ignore the 10.0.0.1 address and still connect to pr@localhost. There is further discussion about this in the next section. The client option explicitly labels client only printcap information. The lpd server will ignore any printcap with the client option. When constructing complex printcaps, this option is used to keep ensure that you have consistent printcap information. The following printcap entry shows how to override the force_localhost default, and force the &LPRng; clients to connect directly to a remote server: lp3:rp=pr:rm=hp.private :force_localhost@ The rp= (remote printer) remote print queue name to used when sending commands to the lpd print server. The force_localhost@ literal is an example of a flag option. The @ sets the literal value to 0 (false). We set force_localhost to false, which now allows the &LPRng; clients to connect directly to the specified remote printer. In this example, the hp.private could be a HP LaserJet Printer with a JetDirect interface, which supports the RFC1179 protocol. One disadvantages of sending a job directly to a printer using the above method is that lpr program will not terminate or exit until all of the files have been transferred to the printer, and this may take a long time as the printer processes the files as they are received. Now let's look at the last printcap entry: # Simplest possible printcap entry - defaults for everything lp4 The last example is the simplest possible printcap entry. This will cause &LPRng; clients to use the default values for everything. The printer will be lp4, i.e. - the name of the printcap, and the server will be localhost if force_localhost is set, or the value of the default_remote_host configuration option if it is not. Simple Server Printcap Example Options used: cm=comment for status filter=job filter lf=log file af=accounting file lp=output device mx=maximum job size sd=spool directory file The previous section discussed printcap entries for use by the client programs. Now we will discuss printcap entries for use by the lpd server. In simple configurations or when we have the force_localhost option enabled we can use the same printcap for both &LPRng; clients and the lpd server. # Local ASCII printer lp1|printer :server :cm=Dumb printer :lp=/dev/lp1 :sd=/var/spool/lpd/lp1 :lf=log:af=acct :filter=/usr/local/libexec/filters/ifhp :mx=0 The printcap entry name is lp1. This information will be displayed when requesting status information using the lpq program. The printer alias. This allows a single spool queue to have multiple names. The :server option specifies this printcap entry is only used by lpd server. The :cm field supplies a information field for lpq (printer status) output. The :lp value specifies the destination file, device or remote spool queue to which data is sent. In this example it is the device /dev/lp1. By default, IO devices are opened for write-only operation. The :sd=/path specifies the spool directory where print job files are stored until they are printed. The :lf and :af options specify the names of the log and accounting files, respectively. These have the default values log and acct respectively. If not an absolute path, the file is relative to the spool queue directory. If these files don't exist, they will not be created, and no logging or accounting will be done. You will need to create them manually (e.g., by using touch) or by using the checkpc program. If you do not want a log or accounting file, then use The :filter=/path option specifies a filter program to be used to process job files. Filters and print formats are discussed in Filters. mx indicates the maximum file size for a print job. Specifying 0 means that there is no limit. Using :oh To Select Printcap Information When administering a large number of printers over a large area, it is sometimes desirable to have a default printer for each host. This default printer may be different for each host, and can be selected by using the oh entry. The oh value is a list of the following entries [!] IP/n - address + mask length 10.0.0.0/8 [!] IP/IP - address + mask 10.0.0.0/255.0.0.0 [!] vvv - glob for hostname pc*.org.com The &LPRng; software will determine the hostnames and IP addresses assigned to the host and then check to see if there is a match in the listed hostnames or IP addresses. The optional test inversion (!) causes the sense of the match to be inverted. The list of addresses or entries are tested in sequence until a match is found. If no match is found the printcap entry will not be used. For example: lp:oh=*.admin.org.com,10.0.0.5,10.2.0.0/16:lp=pr1@server1 lp:oh=*.eng.org.com:lp=hp@server2 color:oh=*.eng.org.com:lp=color@server3 color:oh=!*.eng.org.com:lp=color@server4 In the above example, if our host name is booster.admin.org.com, then we would use lp=pr1@server1, as the *.admin.org.com glob pattern would match our host name. if our host name is booster.dev.org.com and our IP address is 10.2.0.1, then we would use lp=pr1@server1, as the 10.2.0.0/16 ip address would be in the specified address range. Finally we have an example of the use of the match inversion operator (*.eng.org.com will use color@server3 and the others will use color@server4. Using the Wildcard Printcap Entry The wildcard printcap name * is used to select a default or printcap entry when a match is not found in the printcap database. # %P and %Q set to printer name *:lp=%P@server # %P set to 'printer', %Q set to printer name printer|*:lp=%P@server When searching for printcap information, the &LPRng; software will first search for an exact match for a printcap entry against the printcap names and aliases. If non is found, it then searches for a wildcard entry and uses the first one found with a wildcard name or alias. If the wildcard * is the printcap name, then the printer name (%P value) and queue (%Q value) are set to the name being searched for. If the wildcard is an alias, then then printer name is set to the printcap main entry name and the queue to the name being searched for. Enterprise Strength Printcap Example Most system administrators want to have a single printcap file that can be distributed or referenced from all the hosts under their administrative control. The following show how several large institutions have organized their printcap information. It uses the following principles: Many times it is necessary to partition groups of users into areas that have particular printing setups, but use the same queue name for different printers. This is done by using the :oh (on this host) option to select the printcap to be used. Provide as little information as possible to the systems that do not have print spoolers. This is because they will simply forward their requests to a print spooler and it is not necessary to have the information for a printer. As we will see in an example below, this can be very simple to do. Provide a default printer so that when the user does not have a printer explicitly selected then he will get well behaved results. For each family of printers, develop a standard method of interfacing to them. This means that the filter, options, and other things should be identical. For each server, provide a uniform set of standards for the spool queues for a printer. If the spool queue is simply used to store and forward jobs, then provide a standard default operation for this queue. If the spool queue directly feeds a printer, use the default options for the printer, and then refine them with printer specific information. # client setups; note brutality of this method that # assigns servers to clients based on their names # you could do this for IP addresses as well # lp1 is default printer for Engineering # Default client information .client=force_localhost@ # lp1:oh=*.eng.com:lp=%P@server1.eng.com:tc=.client *:oh=*.eng.com:lp=%P@server1.eng.com:tc=.client color:oh-*.admin.com:lp=%P@server2.admin.com:tc=.client *:oh=*.admin.com:lp=%P@server2.admin.com:tc=.client lowres:lp=%P@general.services.com:tc=.client *:lp=%P@general.services.com:tc=.client # Standard Printer Configurations # this is for the HP4simx, we use a standard filter setup .cf_hp4simx :ifhp=model=hp4simx :filter=/usr/local/libexec/filters/ifhp .cf_hplj5000 :ifhp=model=hp5000 :filter=/usr/local/libexec/filters/ifhp # now we define the printers that use them .pr_eng1:cm=Engineering's Printer 1 :tc=.cf_hplj5000:lp=10.0.0.2%9100 .pr_eng2:cm=Engineering's Printer 2 :tc=.cf_hplj5000:lp=10.0.0.23%9100 # now we define the server entries # We set up server entries and then forward # to a single server that sends the the printer .server :sd=/var/spool/lpd/%P :mx=0 pr1:oh=!10.0.0.5:lp=%P@10.0.0.5:server:tc=.server pr1:oh=10.0.0.5:tc=.server :tc=.pr_eng1 Remote Printer Using RFC1179 Options used: lp=destination rm=remote host (machine) rp=remote printer (machine) You can have the lpd server forward jobs to another server or print which supports the RFC1179 protocol by using the following printcap: # Simplest remote|Remote Printer :lp=raw@server # historical remote: rp=raw:rm=server # Sometimes you have to connect to a non-standard port special:lp=lp@server%2000 If the lp printer entry is present, it will override the rm and rp printer entries. The lp=pr@host format specifies that the output device is actually a remote spool queue, and jobs should be transferred using RFC1179 protocol. By default, &LPRng; will attempt to sanitize all jobs that it originates or forwards. This sanitization will result in an RFC1179 compliant control file, and will not modify any of the job information. Remote Printer Using Socket API If the spool queue destination is a remote printer supporting the Socket API, then you can have &LPRng; open a connection directly to the printer. These include the older Apple printers with TCP/IP support and the HP JetDirect supported printers. # Simplest remote :lp=10.24.2.3%9100 The lp=server%port or lp=IPaddr%port format specifies that lpd should open a TCP/IP connection to the remote host and simply transfer verbatum the files to be printed. The sh and sf will prevent lpd from trying to generate banner pages or put form feeds between jobs. While this is the simplest printcap, it is also the most dangerous as there is nothing to prevent a malformed job from being sent to the printer. The next printcap example is much more robust: # Simplest remote :lp=10.24.2.3%9100 :of=/usr/local/libexec/filters/ifhp :filter=/usr/local/libexec/filters/ifhp This version will use the ifhp filter to precondition the printer and to process jobs. See ifhp Filter for details. The ifhp filter will perform the appropriate printer resets, translate job information, and ensure correct printer operation in the presence of errors. It will also produce voluminous error messages and status information. Parallel Printer The parallel printer printcap is very simple. # parallel printer lp: :lp=/dev/lpr The lp=/dev/lpr specifies that lpd should open the device for APPEND and simply transfer job files to it. The sh and sf will prevent lpd from trying to generate banner pages or put form feeds between jobs. If you discover that UNIX print jobs result in a staircase appearance, then you need to force your printer to do LF (linefeed) to CR/LF (carriage return/line feed) translation, or do the translation yourself. # Simple parallel printer lp: :lp=/dev/lpr :filter=/usr/local/bin/lpf By using the if=...lpf filter, the job will be passed through the lpf filter, which will do the LF to CR/LF translation. If you have a more complex printer that handles PostScript, PCL, and PJL, then you will need to use the more powerful ifhp filter: # Simple parallel printer lp: :lp=/dev/lpr :ifhp=model=hp4,status@ :of=/usr/local/libexec/filters/ifhp :filter=/usr/local/libexec/filters/ifhp See ifhp Filter for details. This entry will specify that the printer is an HP4, and that no status information is available. This is usually the case with a parallel port. Serial Printer Options used: br=serial port bit rate rw FLAG device opened RW stty=stty options for serial port configuration The following is a typical printcap for a serial printer: # Local Serial ASCII printer lp2 :lp=/dev/ttya :rw :cm=Serial printer :sd=/var/spool/lpd/lp2 :stty=9600 -echo -crmod -raw -oddp -evenp pass8 cbreak ixon :filter=/usr/local/sbin/lpf :mx=0 Let's examine the new options: A serial port is usually bidirectional, and printers will report errors back to the host computer. The rw flag will cause the printer port to be opened read-write, and the lpd server will report status information. The stty option specifies the stty(1) flags and line speed needed to configure the serial line (See Serial Printers for details). The legacy br (bit rate) option can be used to specify the line speed as well. Bounce Queue Options used: lpd_bounce FLAG produces a single file for forwarding bq_format=format of filtered files When the destination of a spool queue is another spool queue the job is simply forwarded without any modifications. However, sometimes it is essential that the job be modified before forwarding, as when the remote spool queue is actually a printer, and jobs need to be converted to the format acceptable by the remote printer or banner pages added. The lpd_bounce flag marks a spool queue as a bounce queue. Lpd will perform all of the usually job processing steps, such as banner generation, filtering files, etc, but saves the output to a file. This file is then sent to the destination print queue for further processing. # Simple example of a bounce queue bounce:lp=bounce@bouncehost bounce:server :lp=lp@remote :lpd_bounce :sd=/var/spool/lpd/%P :filter=/usr/local/bin/lpf # uncomment ab if you want banner #:ab Some comments: The lpd_bounce option marks the job as a bounce queue, and the lpd server will process the job through the appropriate filter programs. The printcap has filter specifications for different job formats. These are the programs that will be used by &LPRng; to process the job. The bq_format specifies the job format for the output file sent to the remote spool queue. If not specified, it defaults to l (literal or binary). The ab (always print a banner) flag will force a banner to be added to the job. The banner generation is done as discussed in Banner Printing. Job Format Translation Options used: translate_format=change outgoing job file formats translate_incoming_format=change incoming job file formats A rarely encountered problem is when a job is printed with one format but for compatibility needs to be processed with another. The simple translate_format=vlxf option will rename format x files to f format. This can be used to resolve problems with PC based software, which spools jobs using the v format. Unfortunately, many RCF1179 print spoolers do not understand the v format and mishandle the job. A simple forwarding queue with the following entries will rename v format to l (binary) format. lp :sd=/var/spool/lpd/%P :translate_format=vl :lp=lp@printerserver The translate_incoming_format will do the same thing, but this time on incoming jobs. Dynamic Routing Options used: destinations=destinations for jobs router=router program &LPRng; has the ability to route a job to one or more destinations in a dynamic manner. This is not the same as load balancing, as the destinations are hard coded and not able to be changed. This is accomplished by having a router filter generate a set of destinations. Here is a sample printcap entry: t2|Test Printer 2 :sd=/var/spool/LPD/t2 :lf=log :destinations=t1@server1,t1@server2,t1@localhost :router=/usr/local/LPD/router When a job arrives at the lpd server, the 'router' filter is invoked with the standard filter options which include the user, host, and other information obtained from the control file. The routing filter exit status is used as follows: 0 (JSUCC) - normal processing 37 (JHOLD) - job is held any other value - job is deleted from queue The router filter writes to dest (destination queue) copies (number of copies to be made) priority (priority letter) X(controlfile modifications) end Example of router output: dest t1@localhost copies 2 CA priority B end dest t2@localhost CZ priority Z end In this example, two copies of the job will be sent to the t1 and t2 spool queue servers. The Class (C letter value) and job priority information will be rewritten with the indicated values. If routing information is specified by the router filter the job will be sent to the default destination. lpq will display job information in a slightly different format for multiple destination jobs. For example: Printer: t2@h4 'Test Printer 2' (routed/bounce queue to 't1@h2.private') Queue: 1 printable jobs in queue Rank Owner/ID Class Job Files Size Time active papowell@h4+707 A 707 /tmp/hi 3 10:04:49 - actv papowell@h4+707.1 A 707 ->t1@localhost <cpy 1/2> 3 10:04:49 - papowell@lprng2+707.2 A 707 ->t2@localhost 3 10:04:49 The routing information is displayed below the main job information. Each destination will have its transfer status displayed as it is transferred. By convention, the job identifier of the routed jobs will have a suffix of the form .N added; copies will have CN added as well. For example, papowell@lprng2+707.1C2 will be the job sent to the first destination, copy two. Routed jobs can be held, removed, etc., just as normal jobs. In addition, the individual destination jobs can be manipulated as well. The LPC functionality has been extended to recognize destination jobids as well as the main job id for control and/or selection operations. The optional destinations entry specifies the possible set of destinations that the job can be sent to, and is for informational purposes only. In order for destinations, and iterates over list looking for the job that you are interested in. Printer Load Balancing Options used: ss=queue served by printer sv=printers where jobs are sent (servers) In a large site, you could have several equivalent printers, which will be used by many people. The reason for this is, of course, to increase the printer output by enabling several jobs to be printed at once. A load balance print queue is one that feeds jobs to other queues and has a sv=q1,q2,... printcap entry that specifies the destination or server queues. These must be print queue entries and have spool directories on the server. The service queues have a ss=mainqueue printcap entry This informs the lpd server that the queue operates under the control of the mainqueue print queue, and is fed jobs from it. During normal operation, when the lpd server has a job to print in the mainqueue, it will check to see if there is an idle service queue. If there is, it will transfer the job to the service queue spooling directory and start the service queue printing activities. Even though the queues are not meant for direct use, people can print directly to individual queues. This allows a specific load sharing printer to be used. If you wanted to hide the load sharing printers, i.e. - not allow direct spooling to them, then you would simply remove the non-server entries from the printcap. Locations of Printcap Files Options used: printcap_path=printcap file locations lpd_printcap_path=lpd server printcap information The printcap_path and lpd_printcap_path configuration options (see lpd.conf(5) ) specify a set of paths for the printcap information. If the lpd_printcap_path is nonblank then the lpd server uses it, otherwise it uses the printcap_path information. Client programs use only printcap_path. The path names can be separated with whitespace, commas, semicolons, or colons. The default values are: printcap_path ${sysconfdir}/printcap lpd_printcap_path (blank or empty) Separate Server and Client Printcap Files Since only the lpd_printcap_path, you can place server specific information there. If this file is used it must also contain any required information in the <literal/all/ Printcap Entry The all printcap name and all option is reserved to provide a list of printers available for use by the spooling software. This is a desperation, last ditch, back to the wall option for administrators with systems that do not have ways to provide a list of printcap entries. The 'all' printcap entry has the form: all:all=pr1,pr2,... The value of the all option should be a list of printcap names whose values will then be extracted. Single Printcap File for Large Installation One of the major problems faced by administrators of large sites is how to distribute printcap information. They would like to have a single printcap file either distributed by a file server (NFS) or by some other method such as rdist. By using the server and oh tags, information for the specific sites can be separated out. For example: # printcap pr1:lp=pr1@serverhost1:oh=*.eng.site.com,130.191.12.0/24 pr2:lp=pr1@serverhost1:oh=*.eng.site.com,130.191.12.0/24 pr1:lp=pr2@serverhost2:oh=*.admin.site.com pr2:lp=pr2@serverhost2:oh=*.admin.site.com pr1:server:oh=serverhost1.eng.com:lp=/dev/lp:tc=.common pr2:server:oh=serverhost2.admin.com:lp=/dev/lp:tc=.common .common:sd=/usr/local/lpd/%P The above example has some interesting effects. The pattern is used as a glob pattern and is applied to the fully qualified domain name (FQDN) of the host reading the printcap file. For example, *.eng.site.com would match host h1.eng.site.com but would not match h1.admin.site.com. Thus, the effects of the first couple of entries would be to specify that the pr1 and pr2 printers on the eng hosts would be pr1@serverhost1, and on the admin hosts would be pr2@serverhost2, Also, the lpd daemons on serverhost1 and serverhost2 would extract the additional information for pr1 and pr2 respectively, overriding the common lp entries. Management Strategies for Large Installations One very effective way to organize print spooling is to have a small number of print servers running a lpd daemon, and to have all the other systems send their jobs directly to them. By using the above methods of specifying the printer and server host you eliminate the need for more complex management strategies. However, you still need to inform users of the names and existence of these printers, and how to contact them. One method is to use a common printcap file which is periodically updated and transfered to all sites. Another method is to distribute the information using the NIS or some other database. &LPRng; has provided a very flexible method of obtaining and distributing database information: see Using Programs To Get Printcap Information for details. Using Programs To Get Printcap Information In the lpd.conf file you can specify: printcap_path=|program This will cause the &LPRng; software to execute the specified program, which should then provide the printcap information. The program is invoked with the standard filter options, and has the name of the printcap entry provided on stdout and exit with a 0 (success) error code. By convention, the printcap name 'all' requests a printcap entry that lists all printers. This technique has been used to interface to the Sun Microsystem NIS and NIS+ databases with great success. By having the invoked program a simple shell script or front end to the nismatch or ypmatch programs, the complexity of incorporating vendor specific code is avoided. How to use NIS and &LPRng; This note is based on material sent to the lprng@lprng.com mailing list by Paul Haldane paul@ucs.ed.ac.uk. We generally don't use NIS for printcap files (we've moved to hesiod) but I can show you what we've done in the past. The input to NIS is a normal printcap file: # Classical printcap entry lp23a|lp23|lp|main printhost printer - KB, EUCS front Door:\ :lp=lp23a@printhost:\ :sd=/var/spool/lpr/lp23a: #lprng printcap entry lplabel|lpl|TEST - Labels printer: :lp=:rm=printhost:rp=lplabel: :sd=/var/spool/lpr/lplabel: :rg=lpadm:mx=1: To build the NIS printcap.byname map we add the following to the NIS makefile (along the other bits and pieces that the makefile needs to know about a new map). PRINTCAP=${sysconfdir}/printcap # warning : [ ] is actually [<space><tab>] in the script printcap.time: $(PRINTCAP) Makefile if [ -f $(PRINTCAP) ]; then \ sed < $(PRINTCAP) \ -e 's/[ ][ ]*$$//' -e '/\\$$/s/\\$$/ /' \ | awk '$$1 ~ /^#/{next;} $$1 ~ /^[:|]/ {printf "%s", $$0; next;} \ {printf "\n%s", $$0 }' \ | sed -e 's/[ ]*:[ ]*:/:/g' -e 's/[ ]*|[ ]*/|/g' \ -e '/^[ ]*$$/d' > .printcap.$$$$; \ cat .printcap.$$$$; \ if [ $$? = 0 -a -s .printcap.$$$$ ]; then \ awk <.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \ n = split($$1, names, "|"); \ for (i=1; i<=n; i++) \ if (length(names[i]) > 0 \ && names[i] !~ /[ \t]/) \ print names[i], $$0; \ }' | $(MAKEDBM) - $(YPDBDIR)/$(DOM)/printcap.byname; \ awk <.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \ n = split($$1, names, "|"); \ if (n && length(names[1]) > 0 && names[1] !~ /[ \t]/) \ print names[1], $$0; \ }' | $(MAKEDBM) - $(YPDBDIR)/$(DOM)/printcap.bykey; \ rm -f .printcap.$$$$; \ touch printcap.time; echo "updated printcap"; \ fi \ fi @if [ ! $(NOPUSH) -a -f $(PRINTCAP) ]; then \ $(YPPUSH) printcap.byname; \ $(YPPUSH) printcap.bykey; \ touch printcap.time; echo "pushed printcap"; \ fi To specify that you want YP database rather than file access, use the following entry in your /etc/lpd.conf file: printcap_path |/usr/local/libexec/pcfilter Put the following shell script in /usr/local/libexec/pcfilter #!/bin/sh #/usr/local/libexec/filters/pcfilter read key # specify the full pathname to the ypmatch program # the location depends on the version of Solaris or your # system install /full/pathname/to/ypmatch "$key" printcap.byname You can test this by using: h4: {314} # lpc client pr pr :lp=pr@server h4: {315} # lpc server pr pr :lp=pr@server How to use NIS and &LPRng; - Sven Rudolph Date: Wed, 11 Sep 1996 00:11:02 +0200 From: Sven Rudolph <sr1@os.inf.tu-dresden.de> To: lprng@lprng.com Subject: Using :oh=server: with NIS When I use a cluster-wide printcap, I want the entries for each printer to appear, e.g.: ---------- start of printcap snippet lp1 :lp=lp1@server lp2 :lp=lp2@server lp1 :server:oh=servername :sd=/var/spool/lpd/lp1 :lp=/dev/lp1 :mx=0 ---------- end of printcap snippet When I create a NIS map out of this the printer name is used as a key and must be unique. The NIS makedbm will drop all but the last entry for each printer. This makes the printer on the clients unavailable. I solved this by a hack where the second entry is called lp1.server and the NIS client script has to request the right entry. Assumptions Perl is available at the YP server in /usr/bin/perl. A Bourne Shell is available at all clients in /bin/sh The printcap that is to be exported is in /etc/printcap. The printcap is written in the new format. In the examples the printer is called lp1. Add the following to your YP Makefile (/var/yp/Makefile) on the YP server (these lines are for Debian GNU/Linux, other systems might require other modifications): ---------- start of /var/yp/Makefile snippet PRINTCAP = /etc/printcap printcap: $(PRINTCAP) @echo "Updating $@..." $(CAT) $(PRINTCAP) | \ /usr/lib/yp/normalize_printcap | $(DBLOAD) -i $(PRINTCAP) \ -o $(YPMAPDIR)/$@ - $@ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi ---------- end of /var/yp/Makefile snippet Install the programs match_printcap and normalize_printcap in the /usr/lib/yp directory; normalize_printcap is only required on the YP server. The normalize_printcap processes only the &LPRng; printcap format. ---------- start of /usr/lib/yp/normalize_printcap #! /usr/bin/perl $debug = 0; $line = ""; $new = ""; while (<>) { chomp; next if ( /^\s*\#.*/ ); s/^\s*$//; next if ( $_ eq '' ); print "new: " . $_ . "\n" if $debug;; if (/^\s/) { # continuation line $line = $line.$_; print "continued: $line\n" if $debug; next; } else { $line =~ s/\s+\:/:/g; $line =~ s/\:\s+/:/g; $line =~ s/\:\s*\:/:/g; print "line: $line\n" if $debug; push(@lines, $line) if $line; $line = $_; } } $line =~ s/\s+\:/:/g; $line =~ s/\:\s+/:/g; $line =~ s/\:\s*\:/:/g; push(@lines,$line) if $line; @lines = sort(@lines); foreach $line (@lines) { ($printers) = split(/\:/,$line); @printers = split(/\|/,$printers); foreach $printer (@printers) { $num{$printer}++; push(@allprinters,$printer); print "allprinters: @allprinters\n" if $debug; print $printer."_".$num{$printer}."\t$line\n"; } } @pr = keys %num; print "printers @pr\n" if $debug; if ($#allprinters >=0) { print "all_1\tall:all=".join(",",@pr)."\n"; } ---------- end of /usr/lib/yp/normalize_printcap The result of processing the sample printcap file is: lp1_1 lp1:lp=lp1@server lp1_2 lp1:server:oh=servername:sd=/var/spool/lpd/lp1:lp=/dev/lp1:mx=0 lp2_1 lp2:lp=lp2@server all_1 all:all=lp1,lp2 Observe that each of the real printer entries has a key consisting of the printer name with a numerical suffix. This leads to the following method of extracting the printcap information using ypmatch: ---------- start of /usr/lib/yp/match_printcap #!/bin/sh read p n=1 # specify the full pathname to ypmatch - this depends on your # OS version and installation while /full/pathname/to/ypmatch "${p}_${n}" printcap 2>/dev/null; do n=`expr $n + 1` done ---------- end of /usr/lib/yp/match_printcap Now test the YP arrangement: h4: {316} # cd /var/yp; make # this should create the printcap map h4: {317} # ypcat printcap # should provide the whole normalized printcap h4: {318} # echo lp1 |/usr/lib/yp/match_printcap # yields lp1 printcap Modify the printcap_path entry in the lpd.conf file: printcap_path=|/usr/lib/yp/match_printcap Test the use of the printcap path entry: h4: {319} # lpc client lp1 # shows the printcap for lp1 h4: {320} # lpc server lp1 # shows the printcap for lp1 Restart the lpd server and check to see that it accesses the right printcap information. Use the same lpq command, and then try lpc printcap lp1. Lexmark Printers Some Lexmark printers do not send end of job status back unless configured to do so. Here is what is needed to force this. (This capability has been incorporated into the ifhp filter.) From: Matt White <whitem@bofh.usask.ca> To: lprng@lprng.com Date: Wed, 21 Jan 1998 18:25:50 -0600 (CST) Subject: Re: [LPRng] ifhp with Lexmark Optra N printer On Wed, 21 Jan 1998, Simon Greaves wrote: > Apologies in advance if this is way off mark, but we've been > evaluating a commercial print charging package (Geomica) which > works by talking to the printer in what I think is a similar way > to the ifhp filters. Lexmarks are currently a big headache because > they seem to fail to return the message that they have finished > printing which screws things up somewhat. In our case, it is believed > to be a problem with the Lexmark firmware which they are looking > into. There is a fix for that...it is originally from the Lexmark 4039 series, but it still works on the Optra S 1650 machines that we have (and should work on the rest of the optra line). Just send this little chunk of postscript to the printer once: -----------snip---------- %! Postscript to set the 4039 printer into synchronous mode serverdict begin 0 exitserver statusdict begin true setenginesync end -----------snip---------- Basically, it causes the printer to wait until it is finished printing before actually reporting that it is done. I've got 3 Optra S printers running with ifhp right now with no extra options (just defaults). ----------------------------------------------------------- - Matt White whitem@arts.usask.ca - Network Technical Support http://arts.usask.ca/~whitem - College of Arts & Science University of Saskatchewan ----------------------------------------------------------- Tektronix Phaser Printers The ifhp filter supports the AppSocket protocol used by Tektronix. You will need a printcap similar to the following: phaser: :sd=/var/spool/lpd/%P # need a dummy device for output :lp=/dev/null # You need to specify the IP address of the printer's network interface # In this example it is 10.0.0.1 - replace with the correct value # The filter will actually open the connection. :filter=/usr/local/libexec/filters/ifhp :ifhp=dev=10.0.0.1%9100,model=tek Duplex Printing Duplex printing is when you print on both sides of a page. Some printers which do duplex printing require that you send them special commands to force this mode. This is usually done by the FILTERS. The ifhp filter makes a stab at sending the PJL or PostScript commands to the printer. Many people have reported problems doing duplex printing, so here is a check list. Make sure you have enough memory for the worst case print job. Usually the printer has to rasterize both pages before it can produce an impression. It may require much more memory than you expect. Check your printer manual to discover the EXACT form of the enter duplex mode command and make sure that either the command is part of the job (PJL language at the start of the job, postscript header, etc), or that the filter generates the correct form. Note there is a PostScript Printer Description file (PPD) for most printers that support PostScript, and they even have the PJL and PostScript code for this in the PPD file. It has been observed that even with what would apparently be sufficient memory, that many duplex jobs print 'oddly', that they are not aligned on the same side in the same way, etc etc. This may not be the fault of the software, but of the support for duplex operation. Read the ifhp documentation, and create a configuration section in the ifhp.conf file for your printer. I know this is painful, but until there is a uniform way to get the correct commands extracted from either PPD or some other database then this appears to be the only way to do it.
Patrick Powell
Solaris, Newsprint and FrameMaker The following is a guide to using &LPRng; and Sun Microsystems Newsprint by Christopher Hylands, Ptolemy Project Manager of the University of California. The Sun Newsprint printer is actually an OEM version of the Tektronix PhaserII; Sun Microsystems appears to have dropped support for Newsprint, and the recommended migration path is to buy a PostScript printer. If you want more information on using the Newsprint system, notes are available via http://ptolemy.eecs.berkeley.edu/~cxh/lprng.html. Looking through the mailing list logs, it looks like everyone was having a hard time getting lprng to work with Sun's brain-dead newsprinters. I tried using GhostScript, but the fonts were, IMHO, ugly, so I spent a little time getting the newsprint fonts to work. The key thing was to grab the file /usr/newsprint/lpd/if from a SunOS4.1.3 newsprint installation. If you cannot get this code, then the installation will be extremely difficult. To install lprng on a Solaris2.x machine, you need to first stop the existing print services and install the startup scripts for &LPRng;. Note that if there is a local printer, you may have to also fix the permissions of the device. Typical commands are: chown daemon /devices/sbus@1,f8000000/SUNW,lpvi@1,300000:lpvi0 We use the following simple :if script. #/bin/sh # extremely simple filter script /bin/cat The Sparcprinters use licensed fonts from NeWSprint. To use the licensed fonts, you must have the lprng spool directory for the sparcprinter in the same location as spool directory of the brain dead Solaris lp system. If your printer is named xsp524, then this directory would be /etc/lp/printers/xsp524. The printcap entry looks like: sp524|524: :mx=0 :lp=:rm=doppler:rp=xsp524: :sd=/var/spool/lpd/sp524d: :lf=/var/spool/lpd/sp524d/log: xsp524|Sun SPARCprinter NeWSprint printer: :mx=0:rs: :lp=/dev/lpvi0: :sd=/etc/lp/printers/xsp524: :lf=/etc/lp/printers/xsp524/log: :af=/var/spool/lpd/xsp524/acct: :filter=/usr/local/libexec/newsprint/if: The /usr/local/libexec/newsprint/if was copied from /usr/newsprint/lpd/if in a SunOS4.x installation of the newsprint software. Unfortunately, the newsprint engine is so brain dead that it needs many environment variables set, so it is fairly difficult to come up with a clean script to start the engine. I made the following changes to the file. First, set the path in the script. You may also need to change defaults to suit your preferences: PATH=/usr/ucb:/usr/bin:/etc:/usr/etc:\ /opt/NeWSprint/bin:/opt/NeWSprint/np/bin: PATH=$PATH:$NPHOME/pl.$ARCH/bin:$NPHOME/np/bin; export PATH You will also need a /etc/lp/printers/printername/.params file. If you are using the same spooler directory as the directory that the Solaris lp system uses, then the .param file should appear there. If you are using a different spooler directory, then you will need to copy the .param file from elsewhere and edit it accordingly. If you are going to move a license to a new printer, you should probably save the .param file in the old printer spooler directory. Run /opt/NeWSprint/bin/fp_install and remove the license from the old printer and assign it to the new printer. You could run /opt/NeWSprint/bin/rm_np_printer and remove the printer, but that will get rid of the .param file FrameMaker under Solaris2.x uses the lp command. The fix is to edit $FMHOME/fminit/FMlpr and comment out the lp line and add an lpr line sunxm.s5.sparc) lpr -P"$PRINTER" "$FILE" #lp -c -d"$PRINTER" "$FILE" Christopher Hylands, Ptolemy Project Manager University of California cxh@eecs.berkeley.edu US Mail: 558 Cory Hall #1770 ph: (510)643-9841 fax:(510)642-2739 Berkeley, CA 94720-1770 home: (510)526-4010 (if busy -4068) (Office: 493 Cory)
Spool Queues and Files When files are accepted by the lpd server for printing, they are stored in a spool queue directory, together with other files controlling the print operation. This section describes these files and how the &LPRng; software uses them. For descriptive purposes, we will use the following printcap entry as a guide: pr|alias :sd=/var/lpd/pr_public :cd=/var/lpd/pr Spool Queue sd=Spool queue directory name The option in the printcap entry specifies the spool queue directory. If there is no entry or value, then the printer can only be used by the clients such as lpq to locate the destination for a print job. All information, files, etc., for a print queue is stored in the spool directory. Queue Lock File spool_lock_filespool queue lock file - default %P When the lpd server starts printing, it will fork individual worker processes to service each queue. To prevent multiple processes from working on the same queue, a printer lock file specified by the queue_lock_file option (default %P - the %P is expanded to the print queue name) is used. In our example, the lock file would be: /var/lpd/pr/pr. The process ID of the currently active printer is stored in the lock file. By reading the lock file and testing to see if the process is still active, programs such as lpq can determine queue activity. Similarly, the worker process may need to create other processes to assist it. These in turn will create lock or temporary files in the spool directory as well. Spool Control File spool_control_filespool queue control file - default control.%P The spool control file is used to control the operations of the spooler, and is in the spool or control directory. The file name specified by the queue_control_file option (default control.%P - the %P is expanded to the print queue name); in our example, the control file would be: /var/lpd/pr/control.pr. The lpc program sends spool control requests to the lpd daemon, which updates the control file and then signals the appropriate spool server processes that an update has been performed. The control file contents have the form: key value The following keys and their values are currently supported. KeyValuePurpose printing_disabled 0 or 1 disable printing of jobs in queue spooling_disabled 0 or 1 disable placing jobs in queue holdall 0 or 1 hold jobs until released redirect printer transfer jobs to indicated printer class glob expression print only jobs whose class matches glob expression server_order printer name list preferred order of printer use debug debugging options debugging and tracing The printing_disabled and spooling_disabled are managed using the lpc start, lpc stop, lpc enable and lpc disable commands. Similarly, holdall is enabled and disabled by holdall and noholdall commands respectively. When holdall is enabled, jobs placed in the print queue will be held until they are explicitly released for printing by an lpc release command. The redirect entry is used to redirect or transfer jobs which are spooled to this queue to another queue, and is managed by the redirect command. The lpc redirect off removes the redirect entry from the control file. The class entry is similar in operation to the holdall, but allows jobs whose class identification matches the glob expression to be printed. This can be useful when you have special forms or paper required for a print job, and want to run only these jobs when the paper is in the printer. The server_order entry is created and updated for a multiple printer queue. It records the order in which printers should next be used for normal print operations. This allows round robin use of printers, rather than having all jobs printed to the first printer in the list of printers. The debug entry is set by the lpc debug command, and is used to enable or disable debugging and tracing information for a spool queue. This facility is for diagnostic purposes only. Log and Status Files create_filescreate log, accounting and status files lf=log file name (default: log) max_log_file_size#maximum log file size (Kbytes) min_log_file_size#minimum log file size (Kbytes) max_accounting_file_size#maximum accounting file size (Kbytes) min_accounting_file_size#minimum accounting file size (Kbytes) max_status_line#maximum status line length (characters) max_status_size#maximum status file size (Kbytes) min_status_size#minimum status file size (Kbytes) ps=filter status file name (default: status) queue_status_file=queue status file (default: status.%P) short_status_date=display short (hh:mm) timestamp (default: true) During operation, the lpd server records the current printing operations in the spool queue status file specified by the spool_status_file option (default status.%P - the %P is expanded to the print queue name); for our example, this would be /var/lpd/pr/status.pr. In order to prevent this file from growing too large, the server will periodically truncate the file. You can force creation of these files by setting the create_files option. The max_status_size configuration or printcap option sets the maximum size (in Kbytes) of the status file; if the file exceeds this, only the last min_status_size bytes or 25% of the maximum size (default if not specified) will be preserved. The server logs its operations in the log file specified by the lf (log file) option (default is lf=log). The max_log_file_size value (default 0) specifies the maximum length of the log file in Kbytes. If this value is non-zero, then the log file is truncated to min_log_file_size bytes or 25% of the maximum file size. Again, the last portion of the log file is preserved. If the max_log_file_size value is 0, then the log file grows without limit. The server records accounting information in the log file specified by the accounting file af (accounting file) option (default is af=acct). The max_accounting_file_size value (default 0) specifies the maximum length of the log file in Kbytes. If this value is non-zero, then the log file is truncated to min_accounting_file_size bytes or 25% of the maximum file size. Again, the last portion of the log file is preserved. If the max_accounting_file_size value is 0, then the log file grows without limit. See Open The Output Device and Accounting Printcap Options for more details. Some filters require an additional filter status file that they use for recording additional filter status or other operational information. The ps names this file, and it is passed to a print filter using the $s option (see Filter Command Line Options and Environment Variables ). The ps (Printer Status) can be used as well. This file is not truncated by the &LPRng; system. When reporting status information, the length of line returned can be a problem. The max_status_line#79 option restricts the status line to a maximum of 79 characters. The short_status_date (default is true) option causes short (hour:minute) timestamps to be displayed on status queries. Job Files longnumberlong job number default_priority=default job priority nline_after_fileN line after data file A print job consists of a control file and one or more data files. RFC1179 specifies the general format of these files and how they are to be transfered between servers. &LPRng; has extended the contents of the control files and the transfer protocol to provide a more powerful set of features, but has extensive provisions for backwards compatibility with non-&LPRng; software. A sample control file is shown below: Hh4.private J/tmp/file1 /tmp/file2 CA Lpapowell Ppapowell fdfA002230h4.private N/tmp/file1 UdfA002230h4.private fdfB002230h4.private N/tmp/file2 UdfB002230h4.private The first part of the control file contains general information generated by the lpr or other spooling program. The information lines start with an uppercase letter or digit. Some other spooling systems also start information lines with various punctuation marks such as underscores (_) or periods (.). Following this are a set of entries about each of the various files to be printed. These lines start with a lower case letter, followed by the print file name. The lower case letter is the format to be used to process the file. See print file formats for more information about its use. Control File Lines KeyMeaningGenerated ByKeyMeaningGenerated ByAidentifier *&LPRng; internalCclasslpr -C classDdatelprHoriginating hostlprIindentlpr -i indentJjobnamelpr -J jobname (default: list of files)Lbnrnamelpr -U usernameNfilename(see text)Mmailnamelpr -m mailnamePlognamelprQqueuenamelpr -QRaccntnamelpr -R accntnameSslinkdata *lprTprtitlelpr -T prtitleUunlnkfile(see text)Wwidthlpr -w widthZzopts *lpr -Z zopts1font1lpr -1 font12font2lpr -2 font23font3lpr -3 font34font4lpr -4 font4
The entries marked with * are used only by &LPRng;. N and U lines are associated with a print file. The N line is the original name of the print file. By default, &LPRng; places this line before the corresponding data file. You can use the nline_after_file option to have &LPRng; place the N line after the data file line. The U line originally was used to indicate that the named file was to be unlinked after printing. This information is now ignored by &LPRng;. These lines are always grouped with a print file entry. The names of control and data files follow a very strict pattern. Control files have the format cfXnumberhost, where X is an upper case letter, number is (usually) a 3 digit number, and host is the host name. RFC1179 restricted the total length of the control file name to 32 characters; &LPRng; has a much looser limit. Data file names must follow the same pattern as the control file name, and have the format dfXnumberhost. The X can be in the range A-Za-z, allowing at most 52 data files for a job. The number and host must be identical to the corresponding control file. By convention, &LPRng; uses the X of the control file name to set a priority for the job. A job with control file name cfA... will have lower format than a job with format cfB..., and so forth. The lpr program uses the first letter of the class name or an explicit priority value to set the letter value. If none of these are specified, then the default_priority value from the configuration or printcap entry is used. The job number is usually a 3 digit value. However, in systems where a large number of jobs are spooled and need to be kept for printing at scheduled times, this can lead to problems. The longnumber option will use 6 digit job numbers. This must be used with care when operating with non-&LPRng; software.
Job Hold File The information used to control the printing of a job is string in a hold file. The entries in this file have the form: key=[value] The following is an example of a hold file: server=0 A=papowell@astart.com J=jobname transfername=cfA001astart.com datafiles=N=file\002transfername=dfA001astart.com subserver=0 attempt=3 error=cannot open printer hold=0 priority=0 remove=0 routed=0 The contents of the control file are stored as X=<value> entries, where X is the upper case letter corresponding to the control file entry. The The server and subserver entry records the process ID of the server process and the subserver process that is printing the job. The attempt field records the total number of attempts to print the job. The error field records any error that would prevent the job from being printed. This information is reported by the lpq program. The hold field is non-zero when the lpc hold command is used to explicitly prevent the job from being printed; lpc release will clear the field and allow the job to be printed. The priority field is modified by the lpc topq command and is used to provide an overriding priority to printing the file. The remove field is non-zero when the file has been printed and should be removed. The routed field is used to indicate that there is routing information present in the hold file, and that special handling is needed. The routing information is provided by a routing filter. The information is recorded by information in the hold file. The following is an example of routing information. Normally this information is stored in a compressed format with one line per destination, but for clarity this has been broken out into plain text form: active 0 attempt 0 done 0 hold 0 priority 0 remove 0 routed 880892602 route 1 dest t1 ident papowell@h4+705.1 error copies 1 copy_done 0 status 0 active 0 attempt 0 done 0 hold 0 sequence 0 priority B CB end route 2 dest t1 ident papowell@h4+705.2 error copies 0 copy_done 0 status 0 active 0 attempt 0 done 0 hold 0 sequence 1 end Routing information lines start with route followed by individual routing entry information. The route dest, copies, priority, and Xnnnn entries are derived from the output of the router program; other fields are used during the printing process. The copy_done records the numbers of copies done, while the done records that the entry has been completed. The status is the process ID of the server process doing the printing. The output from route filter that generated the above file was: dest t1 copies 1 priority B CB end dest t1 end Job State Options used: ah FLAG Automatically hold jobs A job can be in the following state: Initial. This is the state during job submission. Jobs in the initial state do not have any status displayed for them. Held. Once a job is submitted, it can either be printed or held. The ah printcap option specifies that all jobs are automatically held on submission. The lpc release and lpc redo command will cause these jobs to be printed and the lprm command can remove these jobs. Active. The job is being processed for printing or transfer to another queue. Pending. Jobs which can be printed but are not active. This can be due to the printer being busy or the job class not being printed. Error. Jobs which have encountered an error during printing. The lpc release and lpc redo command will cause these jobs to be printed and the lprm command can remove these jobs. Done. Jobs which have completed printing, but which are not yet removed from the print queue. See the save_when_done flag for more information. The lprm command can remove these jobs. Normally the job sequences is initial, pending, active, and done. However, a job may be put in the error state by problems processing the job or by actions of the lpc command. Job Identifier For each job in a spool queue, the &LPRng; software creates a unique identifier. This identifier is recorded in the control file A line. It can be used by the various client programs for identifying jobs, and is displayed by the lpq program as status information.
Configuration File, Defaults and Overrides Options used: allow_getenv FLAG use GETENV environment variable The &LPRng; options are obtained as follows: The compile time defaults. These are in the LPRng/src/common/vars.c file. If the &LPRng; software has been compiled with the regression testing GETENV option enabled, the configuration information in the file specified by the LPD_CONF environment variable will be used. This can only be used if you are not setuid ROOT or as ROOT as it opens severe security loopholes. The file specified by the config_file compile time option, usually /etc/lpd.conf or /usr/local/etc/lpd.conf, and referred to a the lpd.conf file. If the config_file option value has the form |/pathname, then /pathname must be an executable program and will be run with the standard set of filter options. It must write configuration option values to its In order to protect system security, the lpd.conf (and the printcap) file should be read only. If the require_configfiles option is set in the compile time options, then the preceeding step must be successful, i.e. - there must be a configuration file or the program must execute and exit with a 0 status. If a printer or spooling operation is done, then the values in the printcap entry for the spooler are used to override the default and ifhp.conf file values. Configuration File Format The configuration file format is similar to a the fields of a printcap entry with the difference that the leading colon is optional and there can only be one option per line: # comment # set option value to 1 or ON ab :ab # set option value to 0 or OFF ab@ :ab # set option value to string str=name During system installation the &LPRng; software processes the default values in the LPRng/src/common/vars.c file and generates a sample lpd.conf file that has the format: # Purpose: always print banner, ignore lpr -h option # default ab@ (FLAG off) # Purpose: query accounting server when connected # default achk@ (FLAG off) # Purpose: accounting at end (see also af, la, ar, as) # default ae=jobend $H $n $P $k $b $t (STRING) # Purpose: name of accounting file (see also la, ar) # default af=acct (STRING) # change: # --- we change the af value to none, i.e. - no accounting # --- file by default af= You can change option values by editing the file as shown above then then to force the lpd server to use the new options, use the lpc reread command. Legacy Compatibility The following arguments have been provided for compatibility with legacy systems. Job Processing Much of the flexibility of the &LPRng; software is obtained from the ability to control the details of each step of job processing. The following section details each step in the processing of a job, and explains the printcap options used to control each operation. Assume the pr printcap entry has the form: pr :lp=/dev/lp OR :lp=rp@rm :sd=/var/spool/lpd/pr :lf=log :filter=/usr/local/bin/lpf Assume that we have used the following command to print a set of files. lpr -Ppr file1 file2 This will create a control file in the /var/spool/lpd/pr directory with the following contents (this is an example - in practice there may be minor differences between the example and an actual control file): Hh4.private J/tmp/file1 /tmp/file2 CA Lpapowell Ppapowell fdfA002230h4.private N/tmp/file1 UdfA002230h4.private fdfB002230h4.private N/tmp/file2 UdfB002230h4.private We will refer to this example throughout the following sections. Configuration and Setup Options Options used: ipv6 FLAG use IPV6 Network facilities default_tmp_dir=temporary directory lockfile=lpd server lock file report_server_as=server name for status reports spool_dir_perms=spool directory permissions spool_file_perms=spool file permissions The ipv6 specifies that the IPV6 protocol, rather than IPV4 will be used. The lockfile specifies the location of the lock file used by the lpd server. This file has the port number in the lpd_port value appended to form a unique lock file name. The spool_dir_perms and spool_file_perms (default 0700 and 0600 respectively) values are the (numeric) permissions for the spool directory and spool files. The report_server_as option allows an administrator to masquerade a server with another name. This could be useful if various load sharing activities are being carried out, or if there are problems reconfiguring DNS to cause the correct server name to be reported. The default_tmp_dir option specifies a temporary directory to be used to hold files or information temporarily if there is no spool directory available. Submitting Jobs and Service Requests Options used: lpd_port=Listening Port for unix_socket_path=Unix socket for After the [ipaddr%]port. If the ipaddr is specified then the lpd server binds to the interface with the specified address otherwise it binds to all interfaces. The port value can be a number or name the name of a service; The port corresponding to the service name is used. The The The main The An incoming connection request. If the maximum number of children has not been exceeded, then a new process will be forked to handle this connection. A child process exiting. The server will check to see if there is a pending request to start a server for a queue that could not be accommodated due to too many processes running. A request to start a service process for a queue. If the number of active processes is less than the maximum allowed a service process will be started, otherwise the request will be placed on a list for service when the number of processes active decreases. A timeout alarm for the queue rescanning operation. This is discussed in the next section in detail. When connection is accepted by the First, a timeout is established for the transfer of the information from client to the A single line is read into an internal buffer. This line must be terminated with a The input line is parsed and the actions required are determined. If the activity requires access to the spool queue information, then the current directory of the process is changed to the spool directory. This allows all file accesses to then be relative to this directory. If the processing requires starting a spool queue server process, a message is sent to the main After the processing of the original request has been completed, the process with then check to see if the Spool Queue for the printer should be processed. Job Reception longnumber FLAG Long job number (6 digits) fifo FLAG enforce FIFO order for reception lpd_listen_port= incoming_control_filter=filter to modify incoming job control file translate_incoming_format=change data file formats accounting_fixupname=change accounting name infomration When a print job is received, the The If an incoming control file filter is specified, then the incoming job's control file will be passed through the The majority of control file modifications are simple job file format changes. The translate_format for details. The accounting_namefixup=list[,list]* where list is: host(,host*)[=user(,user*)] The incoming job is checked to see if the originating host is in the list of hosts; the first matching one found is used. Each host list has the format: host,host... where host has the same format used for the When a host match is found, the name to be used for the user is determined from the user list; if none is specified then no changes are made. Each entry in the user list has the format The control file is then passed through the Dynamic Routing. Finally, the Spool Queue Processing Options used: lpd_force_poll=Force lpd_poll_time=Time between polls max_servers_active=Maximum number of active servers When the lpd server starts, it will fork a set of subserver processes, each which will handle an individual queue. If a system has a large number of queues, then this forking operation may result in the lpd server exhausting the process resources. To control this, the max_servers_active value restricts the number of active children to the specified value. If this value is 0, then 50% of the maximum system processes value will be used. Due to the limits on the number of processes, there may be times when a job is placed in a queue, but the lpd server is unable to start handling the job. When all of the children of the main lpd server have exited, the server starts a timer. After lpd_poll_time seconds, it will scan the queues, looking for jobs to process, and starts a process to service them. If it does not find any jobs it remains idle. The lpd_force_poll flag causes the server to periodically poll the queues. This is useful when there is a high possibility that jobs could fail to be printed due to high loads on the server. Opening the Output Device Options used: achk FLAG Accounting check at start af=Accounting File ar FLAG Remote printer accounting enabled as=Accounting at start connect_grace=Time between jobs connect_interval=Connection interval connect_timeout=Connection timeout control_filter=Control file filter ff=form feed fo FLAG form feed on open la FLAG Local printer accounting enabled ld=leader on open (initialization string) lk FLAG Lock IO device lp=IO device pathname nb FLAG Nonblocking device open network_connect_grace=Interval in secs between jobs of=of filter retry_econnrefused FLAG Retry if open failed retry_nolink FLAG Retry if open failed rm=the remote machine to send the job to rp=the remote print queue to send the job to rw FLAG device opened RW flag server_tmp_dir=temporary directory Sequence of Operations: During the server operations, it will try to create temporary files in the print queue spool directory. If this is not desirable, it will create them in the server_tmp_dir directory. If the accounting file specified by af exists, it is opened (af_fd) and the af_fd is passed as file descriptor 3 to all filters. If the af value has the form af=|/program then the program is started and the program af value has the form af=host%port, then a TCP/IP connection to the corresponding port on the remote host is made and the port used as af_fd. In the latter two cases, the filter Accounting Printcap Options for more information on the &LPRng; accounting support. If la (local accounting) is true and we are printing a job or ar (remote accounting) is true and we are transferring a job, the as value is examined. If it is a filter (program) specification, then the program is started with its /dev/null, errorcodes below). If the exit status is 0, (JSUCC) then the printing process will continue, if JHOLD the job will be held, if JREMOVE the job will be removed, if JFAIL the job processing will terminate with a JFAIL indication, otherwise the job processing will terminate with a JABORT indication. If the accounting filter exited with a JSUCC (no error code) and the achk (accounting check) flag is set, the line read from the accounting filter accept, hold, fail, remove, otherwise the job processing terminates with a JABORT indication. An accept will allow the job to be printed, hold will hold the job, fail will cause the job to fail, remove will cause the job to be removed. If the connect_grace value is non-zero and the server is opening a device or network_connect_grace is non-zero and a network connection is being made, the server will pause the specified time. This is to accommodate devices which need a recovery time between jobs. The lp option is checked to determine the type of IO device. FormatMeaning /pathnameAbsolute pathname of IO device pr@hosttransfer to pr on remote host host%portopen a TCP/IP connection to port on host. host can be name or IP address |filterrun the filter program; it The IO device specified by lp is opened write-only or read-write if the rw flag is true, and the resulting file descriptor is io_fd. If the nb flag is set, a non-blocking open will be done as well. If the lk (lock device) flag is true, the device will be locked against use by other If a host%port combination, a TCP/IP connection will be opened to the remote port and the connection will be used as io_fd. If a filter program is specified, the filter program will be run and the If a rp@rm combination, or none of the above combinations are true and the rm and rp values are non-zero, then the job will be transferred to a remote printer. The type of operation will be a job transfer, rather than printing operation. If the connect_timeout value is non-zero, a timeout is setup for the device or socket open. If the device or connection open does not succeed within the timeout, then the open operation fails. If a connection is to a network address (i.e. - connect() system call) and the connection attempt fails with an ECONNREFUSED error, if the retry_econnrefused flag is set then the connection attempt is retried, but this time using an alternative port number. See RFC1179 for details. This is repeated until all of the possible originating port numbers are exhausted. If the open or connect operation fails, and the retry_nolink flag is set, then the server will pause for a minimum of connect_grace plus a multiple of connect_interval seconds based on the number of attempts before retrying the open operation. Note that the interval may increase as the number of attempts increases. If printing a job and the :of filter is specified, it is created with its :of filter, then the of_fd value will be the io_fd descriptor. If transferring a job and the control_filter option is specified, then the program specified by the control_filter value will be run. It will have its Filter Command Line Options and Environment Variables for details of options passed to the control filter, and errorcodes for the exit codes of the filter. If the operation is a job transfer, the operation proceeds as outlined in RFC1179, and then the Normal Termination operations are carried out. If the operation is a print operation and the ld (leader on open) value is provided, the string is translated (escapes removed) and written to the of_fd file descriptor. If the fo (form feed on open) flag is true, then the ff (form feed) string is translated (escapes removed) and written to the of_fd file descriptor. Printing Banners Options used: ab FLAG Always print banner (default FALSE) be=End banner generator program bl=Short banner line format bp=Banner generator program bs=Start banner generator generate_banner FLAG Generate banner for forwarded jobs hl FLAG Banner (header) Last of=Banner and File Separator Filter sb FLAG Short banner (default FALSE) sh FLAG Suppress header (banners) (default FALSE) Banner printing is one of the more complicated configuration options of &LPRng;. This is due mainly to historical evolution of the software, as well as a lack of a well defined standard for filter responsibilities. In the original BSD print spoolers, the philosophy was that banner printing should be delegated to the filters, as they were the most aware of the capabilities of the printers. This required an out of band method to convey banner printing information to the filter, and resulted in a complicated interface. The original interface was: The filter doing banner printing was invoked as a special :of filter, or passed a special flag. The print spooling software would send a special single line of information telling it what the banner information should be. Note that this line was never documented except for the source code, and was inconsistent from version to version. Also, there was no indication of what to do with additional lines, if any. The filter would generate the banner, discard the line, and then pass other lines to the output device. Adding to the confusion, the original print spoolers had a :sh (suppress header or banner) flag, which was supposed to suppress banner printing. It did this by having the print spooler not generate the magic banner information line. A more sophisticated banner printing system would allow the print spooler software to generate the banner, and would then have the :of filter act as a pass through. Thus, we need configure the :of filter NOT to use the first line as banner printing information, and to pass through all information to the device. Complicating this whole mess is the ld (leader option) and tr (trailer option) which is a string sent to the output device (:of filter) when the device (filter) is initialized or terminated. This can sometimes be interpreted as the banner line, leading to unexpected results. Sequence of Operations: If the sh (suppress header) flag is true, no banner is printed, and the actions in this section are skipped. No banner information line is generated for the :of filter, and no banner printing program is invoked. If there is an :of filter and it is expecting such a line and you have ld or tr information then you may get unexpected results (actually, catastrophic failure is a better term, but I digress). If the hl (header last) flag is true the banner is printed at the end of the job and the actions in this section are done at the end of the job. If the user does not want banner pages she can use the lpr -h option. This will cause the lpr program to delete the L (banner name) line in the control file. If there is no L line in the control file and ab (always print a banner) is false (the default), then no banner is printed and the other actions in this section are skipped. If ab is true and the L line is missing then the N (user login name) is used; if it is missing as well, then ANONYMOUS is used for the user name. If a banner printing program is specified by bp, bs, or be options, then &LPRng; will invoke the program to generate a banner and then send the generated banner to the printer via the :of filter. The banner printing program will be invoked using the standard filter command line flags (see Filter Command Line Options and Environment Variables for details), with is If no banner printing program is specified and the sb (short banner) option is TRUE (default is true), then the bl=... (banner line) option value is expanded and sent to the of_fd (:of filter or device. The default bl value is: bl=$-'C:$-'n Job: $-'J Date: $-'t. Using our example, this will get translated to: papowell:A Job: file1 file2 Date: Thu Nov 27 23:02:04 PST 1997 If no banner printing program is specified and we have sb@ (no short banner) then we skip banner generation, i.e. - we do not send a banner generation line to the output (:of filter). If the queue is a normal forwarding queue, then the generate_banner option will invoke the bp, bs or be program as appropriate to create a banner page file which is then made the first (default) or last (hl flag or be=... present) file in a job. This option has no effect in other types of queues. See the translate_format option as well. Printing Job Files Options used: Xf=Format Filter sf FLAG Suppress Form Feed Separators if=Default F Format Filter pr=pr formatting program send_job_rw_timeout= print job read/write timeout send_query_rw_timeout= status query operation read/write timeout sf FLAG Suppress form feed between job files Sequence of Operations: for each job in listed in the control file, the following operations are done in turn. If there is an :of filter present, the suspend string \031\001 is written to of_fd and the no further action is taken until the of filter is suspended. The control file line for the job is examined, and the first letter of the data file specification is used as the format. If the format is p, the job is first processed by the program specified by the pr program, and the program output used as the print file. If the format is f, l, or p then the :if filter is used, otherwise the keyword Xf is used. Note that certain formats such as p, a, l, may not be used as formats. The filter program is started with an appropriate set of command line options (see Filter Command Line Options and Environment Variables ), and with its When doing a read/write operation to a device or remote system, a timeout can be specified. When doing a print or job transfer operation, the send_job_rw_timeout value is used. When doing a status or query operation, the send_query_rw_timeout value is used. If a write or write operation does not complete within the specified timeout seconds, then we have an error condition and job processing or the query operation is terminated with JFAIL status. If the timeout value is 0, then no timeout is done. lpd will then wait for the filter to exit. The following exit codes are used by lpd: Key Value Meaning JSUCC 0 Successful JFAIL 1, 32 Failed - retry later JABORT 2, 33 Abort - terminate queue processing JREMOVE 3, 34 Failed - remove job (Unused) 4, 35 (Unused) 5, 36 JHOLD 6, 37 Hold this job - reprint later JNOSPOOL 7, 38 No spooling to this queue JNOPRINT 8, 39 No printing from this queue JSIGNAL 9, 40 Killed by unrecognized signal JFAILNORETRY 10, 41 Failed, no retry Other Abort - terminate queue processing If the filter exit status was JSUCC (0), or no error indicated, then processing will continue otherwise the job termination takes (see Abnormal Termination ). If the :of filter is present, then it is reactivated with a kill -CONT signal. The the sf (suppress FF print file separators ) is turned off a form feed is sent between each file of a job. Printing Banner At End of Job Options used: hl FLAG Header (Banner) Last The actions taken in this step are identical to those for the Printing Banner, with the exception that the be (end banner program) is used to select the banner generation program rather than the bs (start banner program). If we have hl true, then we print a banner at the end of the job, rather than start. Normal Termination Options used: fq FLAG Form Feed on Close la FLAG Local Printer Accounting tr=Trailer on Close ae=Accounting at end save_when_done FLAG Save when done save_on_error FLAG Do not delete on error done_jobs=NSave status of last N jobs wait_for_eof FLAG Wait for EOF before closing device socket_lingersocket linger timeout half_close FLAG use shutdown() and not close() Sequence of Operations: If we are printing and the fq flag is set and the sf (suppress interfile FF) flag is set, then the ff (form feed) string will be interpreted and sent to the of_fd. If we are printing, the tr (trailer) string will be interpreted and sent to the of_fd. If printing and the la (local printer accounting) flag is set or transferring a job and the ar (remote accounting) flag is set, the ae is examined and accounting is done as described for the as field. If the :of filter is present, its lpd server waits for it to exit. The exit status is used as described above. If the device is a socket or network connection, the socket linger time is set to socket_linger value if nonzero. If the If the wait_for_eof option is true (default) then a read is done on the connection until an EOF is found. The device (io_fd) is then closed. The job is marked as completed in the spool queue. If the save_when_done flag is clear and the done_jobs and done_jobs_max_age values are zero (0), the job is removed. If the done_jobs or done_jobs_max_age values are nonzero, the spool queue is periodically checked and for an excess number of jobs or jobs with old status. This action is suppressed if either the Abnormal Termination Options used: mail_from=Mail from user name mail_operator_on_error=Mail to operator on error send_try=Maximum printing or transfer attempts save_on_error FLAG Do not delete on error done_jobs=NSave status of last N jobs done_jobs_max_age=NRemove status when older than N seconds send_failure_action=Action on Failure sendmail=sendmail path name and options stop_on_abort FLAG Stop processing queue on filter abort If the job processing terminates abnormally, the following sequence of events occurs: The job is marked as having an error during processing. The kill -INT and kill -CONT) to them. If there is a mail_operator_on_error value, the specified operator will be mailed an error indication. The sendmail option specifies the pathname of the sendmail program and the options needed to have it read mail addresses from its standard input. For example, sendmail=/usr/sbin/sendmail -oi -t is a commonly used set of options. The mail_from value specifies the user name used for mail origination. If not specified, the default is to use the print spool queue or printer name. If there is a send_failure_action specified, then it is decoded and the corresponding action taken. If the value is remove, hold, abort, or retry, then the job is removed, held, aborted, or retried. If the value is |/program, the program is executed and the number of attempts are written to the filter If the status is ABORT and the stop_on_abort flag is set, then further processing of jobs is terminated. The job is not removed from the queue. If the error status indicates removal, the save_on_error flag is clear, and the done_jobs and done_jobs_max_age values are zero (0), then the job is removed from the spool queue. If the error status indicates that no further operations should be performed on the queue, then the lpd server will stop processing jobs. If the error code indicated that the job should be retried, and the send_try value is 0 or the number of attempts is less than the send_try value, then the job is retried. Between each attempt to transfer a job to a remote site. This pause will double after each attempt, reaching a maximum of max_connect_interval seconds. If max_connect_interval is 0, there is no limit on the interval value. Forwarding Jobs Options: bkBerkeley compatible control file bq_format=format of filtered output lpd_bouncefilter job and transfer output control_filter=Control file filter control_file_line_order=Control file line order nline_after_fileN line after data file send_data_firstsend data files first If a spool queue is doing store and forward operations, then rather than printing a job the control files and data files are sent to the remote printer. In order to do this, the following items must be arranged. If necessary, the job must be processed by filters on the local host. The control file must be prepared and updated according to the requirements of the remote site. A connection must be established to the remote site. The data files and control files must be transferred to the remote site. One of the more serious problems is when a print spooler (LPR) program does not generate print jobs in a manner compatible with a remote system. While &LPRng; performs checks for improper implementations of RFC1179, it will try to accept a job even under the most severe abuse of the protocol. However, other spoolers are not so forgiving. Some spoolers require that the contents of the control file be in exactly the order that the original 1988 BSD The bk (Berkeley control_file_line_order option to specify an even more restricted set, and use the nline_after_file option to have the file information line (N value) come after the data file entry. However, there are some very odd commercial implementations that require more information than is present. To assist with this, the control_filter option can be used. This specifies a program that will process the control file before it is sent to a remote destination. The control_filter program is run with the standard set of filter options. The exit code of the control_filter is used to determine whether to proceed in processing. See Errorcodes for details. Sequence of Operations: A copy of the control file information is made and the copy will be modified during processing, rather than the original. If the lpd_bounce option is specified then a temporary file is created and the job is printed using the procedures for printing to a device, but to the file. This includes all of the filter operations, banners, and so forth. The working copy of the control file is set to have the temporary file as the data file to be sent to the remote destination, and the data file format is set to the bq_format value. The control file is rewritten according to the requirements of the routing information, if any. For each destination in the routing information and each copy, a new job identifier value will be generated. The control file is rewritten according to the bk and control_file_line_order options. If a control filter is specified, the control filter program is run and the output of the program is used as the new control file. A connection is made to the remote host, and the data and control files are transferred to the remote host using the RFC1179 protocol. If the send_data_first option is specified the data files are sent first. If the job was sent successfully, the job status is updated in the same manner as for a printed job. Debugging Options used: debugging=debugging options full_time FLAG full or extended time format ms_time_resolution FLAG millisecond time resolution syslog_device=syslog alternative device use_info_cache FLAG cache printcap and other information The &LPRng; software has a very powerful debugging capability. Since most printing problems occur on remote systems where it is impossible to run debuggers, and since most systems do not do core dumps of SETUID ROOT programs, the &LPRng; software provides a very verbose set of log file trace messages. First, serious errors or other information are logged using the syslog() facilities. If these are not present on a system, then the messages are logged to the device specified by syslog_device. For client programs, the debugging options are specified on the command line and output is directed to lpd server, debugging commands can be specified on the command line OR as the db=options printcap value. Output is directed to the log file (lf option value, default log). A typical debug entry has the format 2,network+1,database. This sets the general debugging level to 2, network debugging to 1 and the database debugging level to the default. The following debugging options and levels are supported. nnn - general purpose debugging level network - network debugging database - database debugging receive - job or command reception debugging print - detailed job printing debugging The full_time flag forces the logging and other information which has timestamps to have a full (year, month, day, etc.) timestamp. The ms_time_resolution flag forces millisecond time resolution in the time stamp. The use_info_cache (default ON) causes lpd to cache printcap and configuration information. This is desirable except when trying to change values in printcap files and test the results. By using use_info_cache@ in the configuration information, you can get immediate responses. Also, see lpc reread for another method. Filters This section gives an overview of how &LPRng; uses filter programs, and gives a detailed discussion of how the printcap options and filters interact. Filter Functions Print filters are one of the most powerful tools in BSD-style printer systems. In general UNIX terms, a filter is a program that takes its input file(s), does something with it, and sends the result to its standard output. Most UNIX utilities are designed as filters. (But since you are a system manager, you should already know that :)) In the context of a BSD-style print spooler (and also &LPRng;), the term filter refers to a program that processes file while it is being transferred to a printer. The filter is executed with A filter can be as simple as a LF to CR/LF translator, or it can incorporate a complete accounting system, automatic file type translations, or even redirect the job to another printing system. The lpf filter supplied as part of the &LPRng; distribution is a a very simple CR to CR/LF conversion filter. The ifhp filter provides support for more complex PostScript, PCL, and text printers. Filter Exit Codes When a filter exits, the exit code value is used by the parent process to determine what actions to take. Since filters are used in several places in the printing process, not just to do format conversion, there is a large number of recognized exit values. Key Value Meaning JSUCC 0 Successful JFAIL 1, 32 Failed - retry later JABORT 2, 33 Abort - terminate queue processing JREMOVE 3, 34 Failed - remove job (Unused) 4, 35 (Unused) (Unused) 5, 36 (Unused) JHOLD 6, 37 Hold this job - reprint later JNOSPOOL 7, 38 No spooling to this queue JNOPRINT 8, 39 No printing from this queue JSIGNAL 9, 40 Killed by unrecognized signal JFAILNORETRY 10, 41 Failed, no retry Other Abort - terminate queue processing JSUCC A zero or JSUCC exit value always indicates success; a non-zero exit value indicates failure or a problem condition and requires special handling by the parent process. JFAIL When printing or performing some action that can be repeated, such as connecting to a remote printer, a 1 or JFAIL status indicates a transient failure condition. Depending on various configuration options, the printing or other operation can be retried. JABORT The 2 or JABORT is a more serious error, and indicates that there is no expectation that the operation would succeed if retried. It may also indicate that no other similar operation should be performed. Jobs whose print filters exit with JABORT are usually unprintable, and by default are removed from the print queue. JREMOVE The JREMOVE status indicates that the job should be removed from the print queue. This is a refinement of the JFAIL and JABORT status. The job is usually unconditionally removed from the print queue, even if it is normally kept in the queue for reprinting. This status is usually returned by filters which are responsible for permission checking and is returned when the user has no permission to print. JHOLD The JREMOVE status indicates that the job should be held and reprinted at a later time. This status is returned by various filters during the processing of a job, and usually indicates that the resources needed for a job are not available. Held jobs need to be explicitly released by the administrator. JNOSPOOL and JNOPRINT The JNOSPOOL and JNOPRINT are used as part of the management of load balancing queues and the check idle filter. &LPRng; has the ability to run a program to check to see if a spool queue is available for printing on a dynamic basis. If the filter that does this checking exits with JNOSPOOL or JNOPRINT then jobs should not be sent to the spool queue. JSIGNAL This status is usually returned when the exiting process is terminated by a signal or abort, and does not exit using the exit facility. It is usually handled like a JABORT exit status, and is the indication of a severe and possibly non-restartable system failure. JFAILNORETRY This code is used under an extremely odd set of circumstances and was used to support a sophisticated print retry system. Normally when a print filter or other filter returns this code, it is treated as JFAIL. The job is marked as having an error condition and is not immediately retried. Other jobs can then be tried for printing in the queue. It is not removed from the print queue, but marked as unprintable. When a round-robin retry print scheduling algorithm is used, if there are no other jobs available for printing then the jobs that failed with JFAILNORETRY are retried. Thus, jobs that are submitted go to the head of the queue for printing, and jobs that are pending for repeat are printed after them. This algorithm is deprecated, and that the details of this algorithm are undocumented. Other Values If a filter exits with other than the indicated value, or a value inappropriate for its purpose, then the result is treated like JABORT. Print Job Formats Options used: :if, cf, df, gf, nf, :of, rf, tf, vf, Xf, Filter programs &LPRng; has inherited a set of so-called `print formats' from its BSD ancestor. The format was used to specify the type of file that was being printed. The lpd server used the print format to select the filter for processing the file. The default format is f. The user can specify the format (i.e., the file type) by giving the appropriate option to lpr: -b or -l: Binary (literal) file. No processing should be done. The l format is recorded as the file format. -c: cifplot(1) output. -d: TeX DVI file. -g: Output from the plot(3X) routines. -n or -t: (di)troff output. -p: Text file that should be pre-processed by the pr command, and then by the standard text filter. -v: Benson Varian raster image. Alternatively, one can also use -Fx, where x is the format specifier. (E.g., -Fc instead of -c.) This last form also allows you to use other (non-standard) format specifiers. The filter for format X is the value for the Xf printcap option, with some minor exceptions. The following Xf options have a pre-defined meaning. :if The f format filter, i.e. - for the default f format. All print jobs are passed through this one, unless another format is selected. cf Cifplot data filter (for -c format). df Filter for DVI files (-d). gf Graph data filter (-g). nf Ditroff data filter (-n). :of This filter is used for processing the (optional) banner at the start and/or end of the print job, and also for the interjob separators. See OF Filter for details. rf Filter for Fortran style files (-r). tf Troff filter (-t). vf (Versatek) raster image filter (-v). OF Filter The :of filter is used to process banners and job separators. The :of filter is responsible for performing appropriate processing of this information and sending to the printer for action. While the various file filters are invoked on a once per print file basis, the :of filter is invoked on a once per print job basis. This filter is the first one to be started, and should perform whatever specialized device initialization is needed. It should also do whatever accounting procedure is desired for start of job accounting. The :of filter will be given any banner printing or job separation information for a job. As part of its operation, it can detect a specific string, corresponding to a banner print request, and generate a banner. (See the Job Processing Steps and Printcap Options for details.) During operation, the lpd server will send the special stop sequence of \031\001 to the :of filter. The filter must then suspend itself using a kill -STOP operation. The lpd server will detect that the :of filter has suspended itself and then will perform other printing operations. After the other printing operations have been completed, the :of will then be sent a kill -CONT signal. This sequence will continue until all information has been printed, and then the :of filter's <application>lpr</application> -p format Options used: pr=pr program for p format The -p format is requires filtering the the input files by the pr utility and then passing the result through the :if filter. This is widely regarded as a kludge and may not be supported on your print spooler. <application>lpr</application> binary (-l) format The binary (or literal) format is -l. The :if filter is used to process the file, and is invoked with the -c (cancel processing?) flag. The filter will not modify the file when sending it to the printer, but may apply various setups to the printer. Chaining Filters If a filter command has a pipe (|) or IO redirection indicator (< or >) in it, or starts with an open parenthesis (shell configuration option. This allows a wide variety of options and operations to be carried out. The $* value should be used to have the filter options passed to the correct entry in the filter chain. For example, lp: :filter=( echo "starting `date`" >/var/log/status; /usr/local/ifhp $* ) Filter Command Line Options and Environment Variables Options used: bk_filter_options=Backwards Compatible Filter options bk_of_filter_options=Backwards Compatible OF Filter options bkf FLAG Backwards Compatible Filters filter_ld_path=Filter LD_LIBRARY_PATH environment filter_options=Filter options filter_path=Filter PATH environment of_filter_options=OF Filter options pass_env=Environment variables to copy to Filter environment pl=line count for page pw=column count for page px=pixel width for page py=pixel length for page A filter (or program) specification in the &LPRng; printcap database has the form: :option=| [flags] /path [argument | "argument" | 'argument' ]* :option=[flags] /path [argument | "argument" | 'argument' ]* The first case is used where the option value can be a string or filter, and the second where a program is always expected. The following procedure is used to run a filter program. Arguments in single or double quotes are passed as a single value, as for a shell. The sequence of operations to run a filter is as follows: The program must be specified with an absolute path name. By default, the program is run as the user if invoked from a client program such as lpr, lpc, etc. If invoked from lpd, it is run as the user ID specified by the :user (default daemon) configuration entry. The /bin:/usr/bin:/usr/local/bin, and /lib:/usr/lib:/usr/local/lib, configuration options specifies the value of the The /bin:/usr/bin:/usr/local/bin, and /lib:/usr/lib:/usr/local/lib, configuration option specifies the value of the &LPRng; ftp mirror sites Filter Environment Variables ROOT Flag. If the ROOT flag is specified the filter is executed with Userid and Effective Userid ROOT (User ID 0). By default it is executed with the user and group configuration option user and group ids. Running a filter as ROOT is extremely dangerous, and should only be used for programs that require root permissions to open files or make network connections from privileged ports. $- or -$ flag is not present, the :filter_options or :of_filter_options for the filter values are appended to the filter command line. If the :bkf (Berkeley :bk_filter_options and :bk_of_filter_options values are used instead of the :filter_options and :of_filter_options values. Print Filter Command Line Options OptionDefaultValueOptionDefaultValuefilter_options$C $F $H $J $L $P $Q $R $Z $a $c $d $e $f $h $i $j $k $l $n $p$r $s $w $x $y $-aof_filter_options(same as filter_options)bk_filter_options$P $w $l $x $y $F $c $L $i $J $C $0n $0h $-abk_of_filter_options$w $l $x $y
By default, for programs that are not being invoked as print job file filters, the filter_options arguments are added. For print job filters, if the :bkf flag is set, then the bk_filter_options and bk_of_filter_options entries are used. The default bk filter options are the same as originally used with the BSD :of filter, either the of_filter_options or bk_of_filter_options arguments will be added. The program arguments will then be scanned and interpreted. Arguments of the form $letter will be translated into values from the print job control file and/or printcap entry. The letters have the following meaning: Filter Command Line Options and Values OptionPurpose or Valuea printcap af (accounting file name)b job size (in K bytes)c binary file (l format for print file)d printcap cd or sd entrye print job data file name (currently being processed)f print job original name when spooled for printing (N info from control file)h print job originating host (H info from control file)i indent request (I info from control file)j job number in spool queuek print job control file namel printcap pl (page length)m printcap con user name (L info from control file)p remote printer (when processing for bounce queue)r remote host (when processing for bounce queue)s printcap sf (status file)t time in common UNIX formatw printcap pw (page width)x printcap px (page x dimension)y printcap py (page y dimension)F print file formatP printer nameS printcap cm (comment field)Capital letterCorresponding line from control file{key}printcap value for key
If there is no value for the specified argument, then the argument is removed from the list. If there is a value, the actual form of the substitution is controlled by additional flags as follows. Filter Command Line Option Format FormTranslatedValue $x $-x $0x $'x
Each entry in quotes is treated as a single value, as in /bin/sh. The $'x does not quote the value. Combinations of the various flags are allowed. For example, $-x would simply substitute the value for x, and then pass the whitespace separated components as individual arguments. This last form is useful for adding in additional flags on the command line.
The command line is parsed, metacharacters are ruthlessly stripped from all arguments and pathnames and replaced by _ (underscores), and an argument list suitable for the execve system call is formed. A sanitized environment is set up for the program execution, with the following environment variables. Filter Environment Variables Variable NameMeaning CONTROL control file image HF hold file image DATAFILES list of data file names HOME Home directory (client only) IFS " \t" LD_LIBRARY_PATH :filter_ld_path configuration information LOGDIR Home directory (client only) LOGNAME PATH filter_path configuration information PRINTCAP_ENTRY printcap information SHELL SPOOL_DIR :sd printcap information TZ Time zone USER User name (client only)
If the filter is to be run by a client program such as lpr, then the environment variables specified by the pass_env configuration or printcap option will be extracted from the environment, have any metacharacters removed, and then placed in the environment variable list. Commonly, the PGPPASS, PGPPASSFD, and PGPPATH are specified. The program is started, with /dev/null.
&LPRng; Supported Filters There already exists a large library of ready-to-use filters. Some of them have &LPRng;-specific versions, which can be found at the &LPRng; ftp mirror sites. Filter Support Conventions By convention, most filters are either totally standalone (very rare) or require a set of support files or configuration files. There are two types of configuration files: per print queue configuration information and global configuration information. Since a print filter executes with the spool queue directory as the current directory, most filters put per print queue configuration information in a file in the spool directory. Some vintage filters insist on having these files hidden with names such as .setup. This can make it difficult for administrators to determine where the configuration files are. Global configuration files are usually placed in commonly accessible directories such as /usr/local/libexec/filters and its subdirectories. This allows the &LPRng; administrator to set the privileges on these directories such that only the lpd process can access them. When a filter is invoked, it is passed a large number of options, many of which are totally ignored in filter operation. However, for many purposes it is necessary to provide options to the filters to tailor their operation to the particular spool queue needs. An alternative to using information in a file is to place options in the printcap entry and have the filter extract them from the lpf Source code: &LPRng; Distribution This filter is distributed as part of the &LPRng; source code, and has a very limited functionality. By default, it only translates \n to \r\n sequences, and detects the OF Filter Stop sequence when invoked as an OF filter. Options: -Tcrlf - suppress \n to \r\n translation <application>ifhp</application> Filter Source code: &LPRng; Distribution, ifhp-<em>version</em>.tgz The ifhp filter supports a wide variety of smart printers, or to be more specific, printers which support PostScript, PCL or PJL languages. For details on using the ifhp filter see the ifhp filter documentation for details. The following is a quick set of examples of printcap entries: # network connection to jet direct box, # no banners, HP compatible lp :lp=ipaddr%9100 :filter=/usr/local/libexec/filters/ifhp # # banner added, model information added # lp :lp=ipaddr%9100 :ifhp=model=hp4 :bp=/usr/local/libexec/filters/pclbanner :of=/usr/local/libexec/filters/ifhp :filter=/usr/local/libexec/filters/ifhp # # for a parallel port printer or when you want VERY fast # throughput, no pagecounts, error messages, etc. The # lp :lp=/dev/lp0 :ifhp=model=hp4,status@ :filter=/usr/local/libexec/filters/ifhp
Permissions and Authentication The contents of the /etc/lpd.perms file are used to control access to the lpd server facilities. The model used for permission granting is similar to packet filters. An incoming request is tested against a list of rules, and the first match found determines the action to be taken. The action is either ACCEPT or the request is granted, or REJECT and the request is denied. You can also establish a default action. The following is a sample lpd.perms file. # allow root on server to control jobs ACCEPT SERVICE=C SERVER REMOTEUSER=root REJECT SERVICE=C # # allow same user on originating host to remove a job ACCEPT SERVICE=M SAMEHOST SAMEUSER # allow root on server to remove a job ACCEPT SERVICE=M SERVER REMOTEUSER=root REJECT SERVICE=M # all other operations allowed DEFAULT ACCEPT Each line of the permissions file is a rule. A rule will ACCEPT or REJECT a request if all of the patterns specified in the rule match. If there is a match failure, the next rule in sequence will be applied. If all of the rules are exhausted, then the last specified default authorization will be used. The sense of a pattern match can be inverted using the NOT keyword. For example, the rules with ACCEPT NOT REMOTEUSER=john,bill succeeds only if the REMOTEUSER value is defined and is not john or bill. Each entry in a rule is a keyword which has is assigned a value or list of values followed by an optional set of patterns that are matched against these values. The following table is a summary of the available keywords. Permission Keywords and Purpose KeywordMatchDEFAULTdefault resultSERVICEChecking lpC, lpR, lprM, lpQ, and PrintingUSERP (logname) field name in print job control file.REMOTEUSERuser name in request from remote host.HOSTDNS and IP address information for the H (host) field name in print job control fileREMOTEHOSTDNS and IP address information for the connection from the remote host making the requestIPAlias for HOSTREMOTEIPAlias for REMOTEHOSTREMOTEPORTOriginating TCP/IP port for the connection from the remote host making the requestPORTAlias for PORTUNIXSOCKETConnection is on a UNIX socket, i.e. from localhostSAMEUSERUSER and REMOTEUSER matchesSERVERrequest originates on lpd serverFORWARDdestination of job is not hostREMOTEGROUPREMOTEUSER is in the specified group or netgroup in the lpd server group database.GROUPUSER is in the specified group or netgroup in the lpd server group database.LPCLPC command in the LPC request.CONTROLLINEmatch a line in control fileAUTHauthentication typeAUTHUSERauthenticated userAUTHSAMEUSERsame authenticated userAUTHFROMauthenticated forwarderAUTHJOBauthenticated job in queueAUTHCASSL signing certificates for job
Permission Checking Algorithm Options used: default_permission=Default Permission (accept) The lpd server uses the following algorithm to do permission checks. The configuration information initially establishes a default permission using the default_permission configuration value. This is used if an explicit permission is not determined by the other steps in this algorithm. Each line of the permissions file is a lists of tests (patterns) and a permission value that is used if all of the tests (patterns) on the line are successful. A DEFAULT line sets the default result if all lines fail. Each line is executed in sequence until a match is found. The first matching line terminates the permission checking and the corresponding permission value is used. Each keyword has a value (or set of values) that are matched against a set of patterns. If the keyword does not have a value (or the null value) then the match will fail. Initially, all the keywords have a null value. When a connection is received by the lpd server, REMOTEHOST and REMOTEPORT are set to the the IP addresses and hostnames, and the TCP/IP port of the host originating the IP address respectively. REMOTEIP and IFHP are aliases for REMOTEPORT and PORT is an alias for REMOTEPORT and are provided for backwards compatibility with older versions of &LPRng;. If the connection was on a UNIX socket, then the UNIXSOCKET flag is set. For example, a request originating from 10.0.0.2, port 1011 would set REMOTEIP to 10.0.0.2 and PORT to 1011. The REMOTEHOST value is set to the result of doing a reverse DNS lookup on the REMOTEIP address. This value is the list of names and ip addresses in standard IP notation (nnn.nnn.nnn.nnn) that are returned by the lookup. If the DNS lookup fails then the REMOTEHOST value is set to the REMOTEIP value. For example, lookup of 10.0.0.2 would result in the names h2.private and patrick.private, and the only IP address assigned to it was 10.0.0.2. The REMOTEHOST value would then be the list h2.private,patrick.private,10.0.0.2. The SERVICE value is set to X and then the permissions database is scanned for a matching entry. The result is the permission value of the first matching line or the default permission. If the result is REJECT then the connection is closed. Next, a single line is read from the connection. This line contains the request type, the print queue name, and depending on the request type an optional user name and options. The SERVICE value is set to R, Q, M, and C, for a lpR, lpQ, lprM, and lpc request respectively and PRINTER to the print queue name. If the request is for an lpc operation, the LPC value is set to the name of the operation. For example, and lpc lpd operation If the request contains a user name then REMOTEUSER is assigned the user name. If the request originates from the lpd server as determined by the connection arriving from the localhost address or an address assigned to one of the network interfaces for this host then the SERVER value is set to true (or matches). If the request is for an authenticated transfer, (see Authentication and Encryption ), then the authentication procedures are carried out. After they have been performed, the AUTH value is set to true, AUTHTYPE is set to the name of the authentication method, AUTHUSER to the authenticated identifier of the originator of the request, and AUTHFROM to the authenticated identifier of the originator of the connection. Other matching keywords such as REMOTEGROUP use values set at this time. These are discussed in the next section. The permission database is rescanned, this time to see if there is permission to operate on the specified spool queue. The permission database is first checked to see if the requesting user has control (SERVICE=C) permission. If they do, then they can perform any operation on the spool queue. The scan is then repeated for the actual request. If there is no permission to perform the operation then an error code and messages is returned on the requesting connection. If the operation is for a spool queue or server, no other permissions checking is done. This includes the lpq command, and most of the lpc commands control queue operations. If the operation is for for individual jobs in a spool queue, then the queue is scanned and job information is extracted for each job in the queue. The USER value is set to the job control file P line. The value of the H line in the control file is used to perform a DNS lookup, and the HOST value is set to the results of this lookup. IP is an alias for HOST, and is retained for backwards compatibility. The SAMEUSER value is set to true (or match) if the REMOTEUSER value is identical to the USER value. Similarly, SAMEHOST is set to true if the REMOTEHOST value matches the HOST value. See the following sections for other keywords such as GROUP. The permission checking is done for each individual job in a spool queue, and if it succeeds the action is carried out on the job. These checks are applied on the arrival of a job from an external connection. Unfortunately, there are a set of print spooler implementations that do not handle job rejection due to lack of permissions. These printers will continually and repeatedly attempt to send a job for which there is no printing permission until the job is removed by administrative action. To accommodate these printers, we must accept jobs for printing and then dispose of them. This is done by using the SERVICE=P (printing) checks. These checks are performed When a print spool is active and is printing or forwarding jobs, before it processes a job it will read the job control file and set the USER and HOST values as discussed in the previous sections. It will also set the AUTH, AUTHUSER, and AUTHJOB values as well, if the job was spooled by using an authenticated method. The permissions database will be scanned and the resulting permission determined. Note that the values of the REMOTE keys are undefined, and tests using them will have unpredictable effects. If the job does not have permission to be printed, it will normally be removed from the spool queue. While this model is very simple it can handle a wide range of situations. However, it is really based on the simple trust that users will not impersonate other users or hosts. If this is not the case, then more elaborate procedures based on encryption and authentication are called for. There is a problem with permissions checking for lpq (SERVICE=Q) requests. Since the user name is not passed as part of the request, it is impossible to use the REMOTEUSER clause to restrict lpq operations. The SERVICE=R and SERVICE=P facilities are provided to handle problems with print spoolers that do not recognize a lack of permission error code, and will indefinitely retry sending a job to the lpd server. If this is the case, then the SERVICE=R clause can be used to accept jobs, and then the SERVICE=P clause will cause the lpd server to remove of the job when it is scheduled for printing. Rule Matching Procedures [not] key assigned value [not] key=pattern substring match [not] key=pattern1,pattern2,pattern3,... glob and exact [not] key=IP1/mask1,IP2/mask2,... IP address Each of the indicated values is matched against a list of patterns. The following types of matches are used: assigned value. The keyword has an assigned value which is true (match) or false (no match). Examples are SAMEHOST and SERVER. substring match. The indicated entry is present as a substring in the pattern. GLOB matches. The pattern is interpreted as a GLOB style pattern, where * matches 0 or more characters, and ? matches a single character, and [L-H] specifies a range of characters from L to H, in ASCII order. IP address match. The address must be specified in the standard nn.nn.nn.nn format. The mask must be either an integer number corresponding to the number of significant bits, or in the standard nn.nn.nn.nn format. Addresses are compared by doing ( IPaddr XOR IP ) AND mask If the result is 0, then a match results. Note that there may be one or more addresses being checked for; this can occur when a host may have multiple IP addresses assigned to it. integer range match. The pattern has the form low-high, where low and high are integer numbers. The match succeeds if the value is in the specified range. Same IP Address Match. This compares two lists of IP addresses; a match is found when there is one or more common addresses. DEFAULT DEFAULT ACCEPT DEFAULT REJECT The DEFAULT rule specifies the default if no rule matches. Normally, there is one DEFAULT entry in a permissions file. Example: DEFAULT ACCEPT SERVICE Match type: substring The SERVICE key is based on the type of request. KeyRequest KeyRequest CLPC Control Request Mlprm Removal Request PPrinting Qlpq Status Request Rlpr Job Transfer XConnection Request Each of the above codes corresponds either directly to the user command, or a set of subcommands. If you have an LPC request, you can add an LPC=xxx clause to refine the permissions checking to allow or disallow lpc commands such as lpc status, printcap, active, . Example: # control only from root on server ACCEPT SERVICE=C SERVER USER=root REJECT SERVICE=C # accept all others ACCEPT SERVICE=* USER Match type: GLOB The USER information is taken from the P (person or logname) information in the print job control file. Example: # we allow jobs to be spooled ACCEPT SERVICE=R # now we do the checking at print time ACCEPT SERVICE=P USER=root REJECT SERVICE=P REMOTEUSER Match type: GLOB The REMOTEUSER information is taken from the user information sent with a service request. Note that one of the flaws of RFC1179 is that an Example: ACCEPT SERVICE=C REMOTEUSER=root,papowell,admin SERVER ACCEPT SERVICE=C LPC=status,lpd REMOTEUSER=admin REJECT SERVICE=C HOST Match type: GLOB The H (host) information in the print job control file is used to do a DNS lookup, and the resulting list of names and addresses is used for matching purposes. Example: # we allow jobs to be spooled ACCEPT SERVICE=R # now we do the checking at print time # allow from our private subnet ACCEPT SERVICE=P HOST=10.0.0.0/8,*.othernet.com REJECT SERVICE=P REMOTEHOST Match type: GLOB The REMOTEHOST information is obtained by doing a reverse IP name lookup on the remote host IP address and the resulting list of names and addresses is used for matching purposes. If there is no FQDN available, then the IP address in text form will be used. Example: # allow from our private subnet ACCEPT SERVICE=R REMOTEHOST=10.0.0.0/8,*.othernet.com REJECT SERVICE=R REMOTEPORT Match type: integer range The REMOTEPORT value is the originating port of the TCP/IP connection. The match succeeds if it is in the specified range. Example: # require connections to originate from privileged port ACCEPT SERVICE=X REMOTEPORT=1-1023 REJECT SERVICE=X PORT Alias for REMOTEPORT. IP Alias for HOST. REMOTEIP Alias for REMOTEHOST. LPC Match type: GLOB The requested lpc command. This allows the following permissions line to be used: Example: #allow remoteuser admin on server to use LPC topq and hold ACCEPT SERVICE=C SERVER REMOTEUSER=root ACCEPT LPC=topq,hold SERVER REMOTEUSER=papowell REJECT SERVICE=C SAMEUSER Match type: exact string match Both the REMOTEUSER and USER information must be present and identical. Example: # LPC users can do anything ACCEPT SERVICE=C SERVER REMOTEUSER=root REJECT SERVICE=C # allow users who sent jobs from the same host to remove them ACCEPT SERVICE=M SAMEUSER SAMEHOST REJECT SERVICE=M SAMEHOST Match type: Same IP Address The REMOTEHOST and HOST address lists are checked; if there is a common value the match succeeds. Example: # allow root on the same host as user # to remove files ACCEPT SERVICE=M SAMEHOST REMOTEUSER=root REJECT SERVICE=M SERVER Match type: Matching IP Address One of the REMOTEHOST addresses must be the same as one of the addresses of the lpd server host, or must be one of the addresses found by looking up the localhost name using gethostbyname(). Example: # allow root on the server full LPC permissions ACCEPT SERVICE=C SERVER REMOTEUSER=root REJECT SERVICE=C FORWARD Match type: Address Match The list of REMOTEHOST and HOST addresses must not have a common entry. This is usually the case when a remote lpd server is forwarding jobs to the lpd server. Example: # do not accept forwarded jobs or requests REJECT SERVICE=* FORWARD GROUP Match type: modified GLOB The USER must be present in one of the groups in /etc/group or whatever permissions mechanism is used to determine group ownership which matches the GLOB pattern. If the pattern has the form @name, then a check to see if the user is in the named netgroup is done. Example: ACCEPT SERVICE=P GROUP=admin,@netgroup REJECT SERVICE=P REMOTEGROUP The same rules as for GROUP, but using the REMOTEUSER value. Example: ACCEPT SERVICE=R REMOTEGROUP=admin,@netgroup REJECT SERVICE=R CONTROLLINE Match type: GLOB A CONTROLLINE pattern has the form X=pattern1,pattern2,... X is a single upper case letter. The corresponding line must be present in a control file, and the pattern is applied to the line contents. This pattern can be used to select only files with specific control file information for printing. AUTH Match type: value If the current transfer or the transfer used to send a job was authenticated, then AUTH is true or matches. Example: # reject all non-authenticated transfers REJECT NOT AUTH AUTHTYPE Match type: glob If the current transfer or the transfer used to send a job was authenticated, then AUTHTYPE is set to the name of the authentication method. Example: # require kerberos, pgp, or md5 authentication REJECT NOT AUTHTYPE=kerberos*,pgp,md5 AUTHUSER Match type: GLOB The AUTHUSER rule will check to see if the authenticated user identification matches the pattern. Example: ACCEPT SERVICE=C AUTHTYPE=kerberos* AUTHUSER=admin@ASTART.COM IFIP Match type: IPmatch, but for IPV6 as well as IPV4 There is a subtle problem with names and IP addresses which are obtained for 'multi-homed hosts', i.e. - those with multiple ethernet interfaces, and for IPV6 (IP Version 6), in which a host can have multiple addresses, and for the normal host which can have both a short name and a fully qualified domain name. The IFIP (interface IP) field can be used to check the IP address of the interface that accepted the network connection, as reported by the information returned by the accept() system call. Note that this information may be IPV4 or IPV6 information, depending on the origination of the system. This information is used by gethostbyaddr() to obtain the originating host fully qualified domain name (FQDN) and set of IP addresses. Note that this FQDN will be for the originating interface, and may not be the canonical host name. Some systems which use the Domain Name Server (DNS) system may add the canonical system name as an alias. This entry is deprecated and may not be supported in future releases. Permission File Location Options used: perms_path= path The perms_path= configuration variable specifies the location of the default permissions file. The default value is: perms_path=${sysconfdir}/lpd.perms The lpd.perms file can be obtained by running a program, in a similar manner to the printcap file. See Filters for details on how the program would be invoked. For example, assume the configuration information specified: perms_path=|/usr/local/libexec/get_perms The lpd server will write either a blank line for connection (SERVICE=X) and global lpc permissions (SERVICE=C and LPC=reread,lpd,default) or the name of the spool queue to the get_perms X) and control (C service values. Example Permission File # allow root on server to control jobs ACCEPT SERVICE=C SERVER REMOTEUSER=root ACCEPT SERVICE=C LPC=lpd REJECT SERVICE=C # # allow same user on originating host to remove a job ACCEPT SERVICE=M SAMEHOST SAMEUSER # allow root on server to remove a job ACCEPT SERVICE=M SERVER REMOTEUSER=root REJECT SERVICE=M # all other operations allowed DEFAULT ACCEPT In the above sample, we first specify that lpC commands from user root on the lpd server will be accepted. This is traditionally the way that most lpc commands operate. We also allow anybody to use the lpc lpd command. We reject any other lpc requests. We accept lprM requests from the host and user that submitted the job, as well as from root on the server, and reject any others. Finally, all other types of commands (lpq, lpr) are allowed by default. Complex Permission Checking One of the more useful types of permission checking is to restrict access to your printers from users outside your networks. The IP pattern can specify a list of IP addresses and netmasks to apply to them. For example IP=10.3.4.0/24 would match all hosts with the IP addresses IP=10.3.4.0 to IP=10.3.4.255. Similarly, the HOST pattern can specify a set of hostnames or patterns to match against based on the GLOB notation. For example REMOTEHOST=*.private would match all hosts with a DNS entry which ended with private. The NOT keyword reverses the match sense. For example REJECT NOT REMOTEHOST=*.private,*.murphy.com would reject all requests from hosts which did not have a DNS entry ending in private or murphy.com. More Examples The following is a more complex lpd.perms file. # All operations allowed except those specifically forbidden DEFAULT ACCEPT #Reject connections which do not originate from hosts with an # address on 130.191.0.0 or from localhost, # or name is not assigned to Engineering pc's REJECT SERVICE=X NOT IFIP=130.191.0.0/16,127.0.0.1/32 REJECT SERVICE=X NOT REMOTEHOST=engpc* #Do not allow anybody but root or papowell on #astart1.private or the server to use control #facilities. ACCEPT SERVICE=C SERVER REMOTEUSER=root ACCEPT SERVICE=C REMOTEHOST=astart1.private REMOTEUSER=papowell #Allow root on talker.private to control printer hpjet ACCEPT SERVICE=C HOST=talker.private PRINTER=hpjet REMOTEUSER=root #Reject all others REJECT SERVICE=C #Do not allow forwarded jobs or requests REJECT SERVICE=R,C,M FORWARD # allow same user on originating host to remove a job ACCEPT SERVICE=M SAMEHOST SAMEUSER # allow root on server to remove a job ACCEPT SERVICE=M SERVER REMOTEUSER=root Authentication and Encryption One of the major problems in a print spooler system is providing privacy and authentication services for users. One method is to construct a specific set of protocols which will be used for providing the privacy or authentication; another is to provide a simple interface to a set of tools that will do the authentication and/or encryption. &LPRng; provides native support for the MIT Kerberos 4 extensions and Kerberos 5 authentication. &LPRng; uses the OpenSSL libraries to support SSL authentication and encrypted data transfers. &LPRng; has native support for the PGP (Pretty Good Privacy) program and can sign and optionally encrypt command and responses between servers and clients. Due to legal restrictions, an external PGP program must be used for this purpose. A simple MD5 hash based authentication scheme is also provided as an example to illustrate how new or different authentication methods can be adddd. Finally, &LPRng; provide a general purpose interface allowing users to insert their own authentication methods, either at the program level or at the code level. A careful study of the authentication problem shows that it should be done during reception of commands and/or jobs from a remote user and/or spooler. At this time the following must be done: The received command must be checked for consistency, and the remote user and host must be determined. The remote user and host must be authenticated. The command and/or spooling operation must be carried out. The results must be returned to the remote system. To accomplish these goals, the following printcap entries are used: auth= AUTHTYPE - pgp, kerberos, etc AUTHTYPE_path= pathname - the pathname of a program to be used to support this authentication type AUTHTYPE_id= identification - the identification of the server for the authentication method. For example, the kerberos principal for the server, the PGP key id for the server, and so forth. AUTHTYPE_server_key= identification - location of a file on the server where a key used to unlock or encrypt a message is kept. AUTHTYPE_forward_id= identification - the identification of the remote destination, This is used by the server when forwarding a job to a remote destination. By default, the AUTHTYPE_id value is used by the server as its identification. AUTHTYPE_default_client= identification - when forwarding a job and the job arrived via an unauthenticated method, use this as the default client identification. User Identification When a user logs into a system, they are assigned a user name and a corresponding UserID. This user name is used by the &LPRng; software when transferring jobs to identify the user. When we look into the problem of authentication, we will possibly have a more global user identification to deal with, the authentication identifier (AuthID). One way to deal with this problem is to give &LPRng; intimate knowledge of the UserID and AuthID relationship. While this is possible it is difficult to deal with in a simple and extensible manner. An alternate solution is to provide a mapping service, where the authentication procedure provides a map between the UserID and AuthID. RFC1179 Protocol Extensions The RFC1179 protocol specifies that a \nnn[additional fields]\n \nnn is a one octet (byte) value with the following meaning: REQ_START 1 start printer REQ_RECV 2 transfer a printer job REQ_DSHORT 3 print short form of queue status REQ_DLONG 4 print long form of queue status REQ_REMOVE 5 remove jobs The &LPRng; system extends the protocol with the following additional types: REQ_CONTROL 6 do control operation REQ_BLOCK 7 transfer a block format print job REQ_SECURE 8 do operation with authentication The REQ_CONTROL allows a remote user to send LPC commands to the server. The REQ_BLOCK provides an alternate method to transfer a job. Rather than transferring the control and data files individually, this format transfers one file. The REQ_AUTH provides a mechanism for providing an authentication mechanism and is described in this document. Authentication Operations Options used: auth=client to server authentication type auth_forward=server to server authentication type XX_id=server identification XX_forward_id=Server identification A &LPRng; client lpd server authenticated transfer proceeds as follows. If an authenticated transfer is specified by the auth=protocol entry in the printcap or configuration information, the client sends a request for an authenticated transfer to the server. Part of the authentication request is the authentication type. If authentication type XX is requested the server will examine the information in the printcap and configuration entries for an XX_id value. If this value is present then the server supports authentication of this type. Further permission checks are carried out and finally the server will accept or reject the authentication request. If the request is accepted the server returns a positive acknowledgment (single 0 byte) to the requester, otherwise it returns a nonzero value and an error message. If the request is accepted then an authentication specific protocol exchange is carried out between client and server. The commands and/or data files are encrypted and/or signed and transferred to the server. The protocol specific software on the server will then decrypt and/or check signatures, perform the requested actions, and in turn generate a status information. The status information is encrypted and/or signed by the server and sent to the client, where the client decrypts and/or checked for correct signature. The auth authentication type is used as a prefix to look up authentication specific information in the printcap or configuration information. For example, if auth=kerberos, then kerberos_id, kerberos_forward_id, and other information would be extracted from the printcap or configuration information. A lpd server to lpd server authenticated transfer proceeds as follows. If an authenticated transfer is specified by the auth_forward=protocol entry in the printcap or configuration information, the originating server sends a request for an authenticated transfer to the destination server. The originating server plays the part of the client and performs the same set of actions. The following printcap or user level information needs to be provided for an authenticated exchange. The auth option specifies the authentication type to be used for client to server transfers. For example, auth=kerberos or auth=kerberos5 or would specify Kerberos 5 authentication, auth=kerberos4 would specify Kerberos 4 authentication, auth=pgp would specify PGP authentication, auth=md5 would specify MD5 authentication, etc. The special form auth@ specifies no authentication. The auth_forward option specifies the authentication type to be used for server to server transfers. For example, auth_forward=kerberos5 would specify Kerberos 5 authentication, etc. The special form auth@ specifies no authentication. The AUTHTYPE_id option specifies the identification to be used for the destination in client to server transfers. For example, kerberos_id would specify the principal name of the When forwarding a job to a remote server, the AUTHTYPE_forward_id option specifies the identification to be used for the destination in server to server transfers. For example, kerberos_id would specify the principal name of the originating kerberos_forward_id option specifies the destination server kerberos id. When forwarding a job to a remote server, if the original job does not have an authenticated user name or identification, then the AUTHTYPE_default_client_id option specifies the name to be used to identify the job. As discussed in later sections, this will be the The authenticated transfer request sent to a server has one of the following forms, depending on the originator: \008printer C user_id authtype \n - for commands (lpq, lpc, etc.) \008printer C user_id authtype size\n - for print jobs (lpr) \008printer F server_id authtype \n - forwarded commands (lpq, lpc, etc.) \008printer F server_id authtype size\n - forwarded print jobs (lpr) The single character with the \008 value signals that this is an authentication request the printer is the name of a print queue, and the C (client) or F indicates that the request is from a client program or is a forwarded request from a server. The user_id or server_id field is an identifier supplied by the originator and is discussed below. If the size value is present then the request is for a job transfer and this value represents the job size. It is used to determine if there is sufficient space in the spool queue for the job. The user_id or server_id fields in the authentication request are obtained as follows. If the request originates from a client, then the user_id is the user name of the originator obtained from password information. If the request originates from a server, then the server_id is the printcap or configuration xx_id=server_id value, where xx is the value of the auth_forward=xx entry. When the authenticated transfer request is received, the destination will either return a single zero byte, or a non-zero byte value followed by additional refusal information. A refusal terminates the protocol exchange. Further exchanges are then determined by the authentication protocol specific requirements. Once the initial exchanges have been completed a user file and/or command will be transferred to the destination server. An authentication protocol specific AUTHFROM and AUTHUSER strings will be supplied to the lpd server for purposes of permission checking. The lpd server then carries out the requested operation, and will write error and status information into a file. After the requested activity has finished, protocol specific module transfer the status information in the file to the requesting system and terminate the protocol exchange. Permission Checking When an authenticated transfer has been performed, the following permission information will be provided. AUTH This value is true or match if an authenticated request was received. AUTHTYPE=authtype This has the value of the authtype field in the authentication request. AUTHUSER=userinfo This is the AUTHUSER information provided by the authentication protocol, and is usually the originating user's identification. AUTHFROM=frominfo This is the AUTHUSER information provided by the authentication protocol, and is usually the originating system (user or lpd server) identification. AUTHSAMEUSER This item has effect only when checking jobs in a spool queue. The AUTHUSER information from the request is compared to the AUTHUSER information from the request that created a job. If they are identical, the match succeeds. AUTHJOB This item has effect only when checking jobs in a spool queue. If the job was transfered using an authentication protocol the match succeeds. For example, to reject non-authenticated operations, the following line could be put in the permissions file. REJECT NOT AUTH If a remote server has id information FFEDBEEFDEAF, then the following will accept only forwarded jobs from this server. ACCEPT AUTH AUTHFROM=FFEDBEEFDEAF REJECT AUTH REJECT NOT AUTH To allow only authenticated users to remove jobs you can use: ACCEPT AUTH SERVICE=R,M,L,P AUTHSAMEUSER REJECT AUTH REJECT NOT AUTH PGP Authentication Support PGP is a well known encryption and authentication program. For more details see the web site http://www.pgp.net or the ftp site ftp://ftp.pgp.net. &LPRng; has greatly simplified the use of PGP for authentication by building in support as follows. The user and group configuration entry (defaults daemon and daemon respectively) specify the user and group id used by the lpd server for file and program execution. PGP uses the current user id of the PGP process to determine the locations of various configuration files and information. In this discussion we will assume that lpd runs as uid daemon. By default, the PGP program expects the public and secret key rings to be in the $HOME/.pgp/ directory to be readable only by the user. In order to set up PGP authentication, make sure that the daemon account has a home directory. The daemon user should not allow logins or have its login password disabled. Each PGP key has an associated identifier. It is recommended that the lpd key be lpr@hostname, where hostname is the fully qualified domain name of the server. Create the public and private keys for the server. For security reasons the #> su /bin/sh # start root shell %> HOME=/tmp %> export HOME %> mkdir /tmp/.pgp %> pgp -kg # select 1024 or longer keys # set the user id to be lpr@hostname as discussed above # set the pass phrase %> ls /tmp/.pgp pubring.bak pubring.pgp randseed.bin secring.pgp %> cd /tmp/.pgp %> pgp -kxa lpr@hostname serverkey pubring.pgp # creates serverkey.asc # you will want to give serverkey.asc to users to add to their # public key ring %> mkdir ~daemon/.pgp %> cp * ~daemon/.pgp %> chown daemon ~daemon/.pgp ~daemon/.pgp/* %> chmod 700 ~daemon/.pgp %> chmod 644 ~daemon/.pgp/* Next, place the passphrase for the daemon user in ~daemon/.pgp/serverkey, and make sure it has owner daemon and 600 permissions (read/write only by daemon). This is extremely important. If other users can read this file then security will be severely compromised. Next, distribute the servername.asc file to users. &LPRng; server. This is usually done by placing the key file in a well known file location or making it available to users by some form of Public Key Distribution system (PKD). Users add the serverkey.asc key to their public key using: pgp -ka serverkey.asc Finally, the administrator will need to add the users public keys to the daemonpublic key ring file pubkey.pgp. This can most easily be done by copying all of the users public keys (in ASCII text format) to a single file (/tmp/keyfile)and using: su daemon pgp -ka /tmp/keyfile ~daemon/.pgp/pubring.pgp If the lpd server is using PGP to forward jobs or requests, the destination server's public key must be put in the originating servers public keyring. For example: su daemon pgp -ka /tmp/lpd.keyfile ~daemon/.pgp/pubring.pgp Printcap Configuration Options used: pgp_path=path to PGP program pgp_id=destination server key used by clients pgp_forward_id=destination server used by server pgp_server_key=path to server passphrase file Example printcap entry: pr: :lp=pr@wayoff :auth=pgp :pgp_id=lpr@wayoff.com :pgp_path=/usr/local/bin/pgp pr:server :lp=pr@faroff :auth_forward=pgp :pgp_id=lpr@wayoff.com :pgp_path=/usr/bin/pgp :pgp_forward_id=lpr@faroff.com The pgp_path value is the path to the PGP program. The program must be executable by all users. The pgp_id value is the id used by PGP to look extract keys from key rings. When doing a client to server transfer this will be supplied as the id to be used for the destination, and the user's public keyring will be checked for a key corresponding to this id. When a request arrives at the server, the server will use this value as the id of a key in its private key ring. Finally, when a server is forwarding a request to a remote server, it will use this value as the id of the key in its private key ring to be used to sign or encode the destination information. The pgp_forward_id value is used by the lpd server as the id to use to find a key for the destination. The pgp_server_key is the path to the file containing the server passphrase. This file will be read by lpd to get the passphrase to unlock the server's keyring. User Files and Environment Variables Options used: PGPPASSFILE=File to read PGP passphrase from PGPPASSFD=File descriptor to read PGP passphrase from PGPPASS=PGP passphrase One problem with using PGP is the need to have users input their passphrases. The following methods can be used. Put the passphrase in a file, say $(HOME)/.pgp/.hidden, and set the PGPPASSFILE environment variable to the file name. This file will be opened and read by PGP to get the passphrase. This file should be owned by the user and have 0600 or read/write only by user permissions. A more subtle solution is to use the PGPPASSFD environment variable facility. This causes PGP to read the passphrase from a file descriptor. If the user puts his passphrase in a file, say $(HOME)/.pgp/.hidden, then the following shell script can be used: #!/bin/sh # /usr/local/bin/pgplpr script - passphrase in $(HOME)/.pgp/.hidden # PGPASSFD=3 3<$(HOME)/.pgp/.hidden lpr "$@" The least desirable method is to put the passphrase in the PGPPASS environment variable. Since the ps command can be used to list the environment variables of processes, this is highly undesirable and should not be used under any circumstances. Using Kerberos 5 for Authentication &LPRng; Kerberos 5 authentication is based on the Kerberos5-1.2.5 release as of 3 June 2002. The distribution was obtained from MIT from the http://web.mit.edu/kerberos/www/ Website. The following sections briefly describes how to set up and test the Kerberos software and then how to configure &LPRng; to use Kerberos. &LPRng; Configuration The following --enable-kerberos enable Kerberos V support --enable-mit_kerberos4 enable MIT Kerberos 4 support --disable-kerberos_checks disable Kerberos sanity checks The krb5.h and the krb5 support libraries. libraries. If it finds these, then Kerberos authentication will be included. The Kerberos Installation Procedure Get the Kerberos 5 distribution. Compile and install the distribution. Create the /etc/krb5.conf and /usr/local/var/krb5kdc/kdc.conf, files using templates from the files in the Kerberos distribution's src/config-files directory. See the Kerberos Installation Guide and the Kerberos System Administrators Guide for details. Start up the KDC and KADMIN servers - you might want to put the following in your rc.local or equivalent system startup files: if [ -f /etc/krb5.conf -a -f /usr/local/var/krb5kdc/kdc.conf ]; then echo -n ' krb5kdc '; /usr/local/sbin/krb5kdc; echo -n ' kadmind '; /usr/local/sbin/kadmind; fi Use kadmin (or kadmin.local) to create principals for your users. Use kadmin (or kadmin.local) to create principals for the lpd servers. The recommended method is to use lpr/hostname@REALM as a template for the principal name, i.e. - lpr/astart1.private@ASTART.COM for an example. You should use fully qualified domain names for the principals. Do not assign the principal a password. Example: #> kadmin OR #> kadmin.local kadmin: addprinc -randkey lpr/wayoff.private@ASTART.COM quit Extract the keytab for each server: Example: #> kadmin OR #> kadmin.local ktadd -k /etc/lpr.wayoff.private lpr/wayoff.private@ASTART.COM quit The /etc/lpr.wayoff.private file contains the keytab information which is the equivalent of a password for a server program. You should create these files and then copy the appropriate keytab file to /etc/lpd.keytab file on each server. See the warnings about of keytab files in the Kerberos Installation and Kerberos Administration manuals. You should copy the file using an encrypted connection, set the permissions to read only by the owner (400), and set the owner to daemon or the user that lpd will run as. #> chmod 400 lpr.wayoff.com #> scp lpr.wayoff.com root@wayoff.com:/etc/lpd.keytab #> ssh -l root wayoff.com # wayoff > chmod 400 /etc/lpd.keytab # wayoff > chown daemon /etc/lpd.keytab # wayoff > ls -l /etc/lpd.keytab -rw------- 1 daemon wheel 128 Jan 16 11:06 /etc/lpd.keytab If you want to have MIT Kerberos4 printing compatibility then you will need to set up Kerberos 4 servertabs instead of Kerberos 5 keytabs. Assuming that you have put the Kerberos 5 keytab in /etc/lpd.keytab, then you extract the Kerberos 4 srvtab version of the Kerberos 5 keytab using the following commands. You must put the key in the /etc/srvtab file in order to be compatible with the Kerberos 4 support. h4: {321} # ktuil rkt /etc/lpd.keytab wst /etc/srvtab &LPRng; Configuration The &LPRng; software needs to be configured so that it can find the Kerberos libraries and include files. By default, the include files are installed in /usr/local/include and the libraries in /usr/local/lib. Use the following steps to configure &LPRng; so that it uses these directories during configuration and installation: cd .../LPRng rm -f config.cache CPPFLAGS="-I/usr/local/include -I/usr/include/kerberosIV" \ LDFLAGS="-L/usr/local/lib -L/usr/lib/kerberosIV" \ ./configure make clean all su make install Printcap Entries Options used: auth=kerberos5use Kerberos5 authentication kerberos_id=server prinicpal name (for client use) kerberos_server_principal=alias for kerberos_id kerberos_forward_id=destination server used by server kerberos_forward_principal=alias for kerberos_forward_id kerberos_keytab=location of the lpd server keytab file kerberos_service=service to be used kerberos_life=lpd server ticket lifetime kerberos_renew=lpd server ticket renew Example printcap entry: pr:client :lp=pr@wayoff :auth=kerberos5 :kerberos_id=lpr/wayoff.private@ASTART.COM pr:server :lp=pr@faroff.private :auth_forward=kerberos5 :kerberos_id=lpr/wayoff.private@ASTART.COM :kerberos_forward_id=lpr/faroff.private@ASTART.COM :kerberos_keytab=/etc/lpd.keytab OR If you want to use Kerberos 4 authentication to the server pr:client :lp=pr@wayoff :auth=kerberos4 :kerberos_id=lpr/wayoff.private@ASTART.COM # support both Kerberos 4 and 5 on server pr:server :lp=pr@faroff.private :auth_forward=kerberos5 :kerberos_id=lpr/wayoff.private@ASTART.COM :kerberos_forward_id=lpr/faroff.private@ASTART.COM :kerberos_keytab=/etc/lpd.keytab The printcap configuration for Kerberos authentication is very simple. The kerberos_id is the principal name of the lpd server that clients will connect to. For backwards compatibility, kerberos_server_principal can also be used. This values is used to obtain a ticket for the lpd server, and is the only entry required for client to server authentication. The other entries are used by the lpd server. kerberos_keytab entry is the location of the keytab file to be used by the server. This contains the passphrase used by the server to authenticate itself and get a ticket from the ticket server. The kerberos_id value is also used by the server during the authentication process to make sure that the correct principal name was used by the request originator. This check has saved many hours of pain in trying to determine why authentication is failing. The kerberos_life and kerberos_renew set the lifetime and renewability of the lpd server Kerberos tickets. These values should not be modified unless you are familiar with the Kerberos system. There are extensive notes in the &LPRng; source code concerning these values. The kerberos_service value supplies the name of the service to be used when generating a ticket. It is stronly recommended that the kerberos_id entry be used instead. User Environment Variables and Files In order to use kerberos authentication, the user will need to obtain a ticket from the Kerberos ticket server. This is done using kinit. No other actions are required by the user. Using Kerberos 4 for Authentication &LPRng; has built-in support for the Project Athena extensions to the RFC1179 protocol. These provide an extremely simple authentication protocol using an initial credential exchange. After the initial exchange the usual RFC1179 protocol is used. During configuration, if the krb.h (Kerberos 4) include file is found, then this is enabled by default. Printcap Entries Options used: auth=kerberos4use Kerberos4 authentication kerberos_id=destination server key used by clients kerberos_server_principal=alias for kerberos_id Example printcap entry: pr: :lp=pr@wayoff :auth=kerberos4 :kerberos_id=lpr/wayoff.private@ASTART.COM The configuration information for Kerberos4 and Kerberos5 is identical and differ only in the authentication type. Note that only client to server authentication is supported. Using SSL for Authentication &LPRng; has built-in support for using SSL as an authentication method. The implementation is based on OpenSSL 0.9.6c and the associated libraries as of of 3 June 2002. The distribution was obtained from the OpenSSL group from the http://www.openssl.org Website. SSL authentication is based a private key/secret key technology, where the various keys are placed in files (or data structures) called A top level or root certificates and a set of signing certificates. By convention, these are stored in the /etc/lpd/ssl.ca directory; the root certificate is usually the ca.crt file. Each server has a certificate and private key file which are used to identify the server and sign the SSL messages. The private key file is usually stored in an encrypted form and a password is required unlock the file. By convention, the server files are stored in the /etc/lpd/ssl.server directory; the server.crt file contains the server certificate and (encrypted) private key; the server.pwd file contains the password to decrypt the private key. Each user has a certificate and private key file which are used to identify the user and sign the SSL messages. The private key file is usually stored in an encrypted form and a password is required unlock the file. By convention, the user files are stored in the ${HOME}/.lpr directory; the client.crt file contains the client certificate and (encrypted) private key; the client.pwd file contains the password to decrypt the private key. A utility to create and manage the SSL certificate files. The locations of the SSL files can be specified by various options to Certificate Management The h110: {111} % lprng_certs lprng_certs -- LPRng SSL Certificate Management Copyright (c) 2002 Patrick Powell Based on CCA by Ralf S. Engelschall (Copyright (c) 1998-2001 Ralf S. Engelschall, All Rights Reserved.) usage: lprng_certs option init - make directory structure newca - make new root CA and default values for certs defaults - set new default values for certs gen - generate user, server, or signing cert verify [cert] - verify cert file index [dir] - make certificate index files in directory dir encrypt keyfile - set or change password on private key file The Creating Root Certificate The h110: {112} #> lprng_certs newca lprng_certs -- LPRng SSL Certificate Management Copyright (c) 2002 Patrick Powell Based on CCA by Ralf S. Engelschall (Copyright (c) 1998-2001 Ralf S. Engelschall, All Rights Reserved.) INITIALIZATION - SET DEFAULTS ... ______________________________________________________________________ STEP 1: Generating RSA private key for CA (1024 bit) ______________________________________________________________________ STEP 2: Generating X.509 certificate signing request for CA ______________________________________________________________________ STEP 3: Generating X.509 certificate for CA signed by itself ______________________________________________________________________ RESULT: /etc/lpd/ssl.ca/ca.crt: /C=US/ST=California/L=San Diego/O=Astart/OU=Certificate Authority/\ CN=Astart CA/Email=id@astart.com error 18 at 0 depth lookup:self signed certificate OK ______________________________________________________________________ STEP 4. Encrypting RSA private key with a pass phrase for security The contents of the certificate key file (the generated private key) should be echo kept secret, especially so if it is used to sign Certificates or for User authentication. SSL experts strongly recommend you to encrypt the key file with a Triple-DES cipher and a Pass Phrase. When using LPRng, you provide the password via a file specified by the LPR_SSL_PASSWORD environent variable, or in the ${HOME}/.lpr/client.pwd file. The LPD server uses the ssl_server_password_file option to specify the location of a file containing the password. See the LPRng Reference Manual for details, or the printcap(5) man page. key file is /etc/lpd/ssl.ca/ca.key Encrypt the private key now? [Y/n]: y Fine, you're using an encrypted private key to sign CERTS. Creating Client and Server Certificates The h110: {112} #> lprng_certs gen lprng_certs -- LPRng SSL Certificate Management Copyright (c) 2002 Patrick Powell Based on CCA by Ralf S. Engelschall (Copyright (c) 1998-2001 Ralf S. Engelschall, All Rights Reserved.) CERTIFICATE GENERATION What type of certificate? User/Server/Signing Authority/Help? [u/s/a/H] Create in '/etc/lpd/ssl.certs' [return for yes, or specify directory] CERT name 'user-10'? [return for yes, or specify name] papowell CERT name 'papowell'? [return for yes, or specify name] Creating papowell in /etc/lpd/ssl.certs Sign with Certificate '/etc/lpd/ssl.ca/ca.crt' \ [return for yes, ? for list, or specify cert file] ? Possible CERTS in directory '/etc/lpd/ssl.ca' are: /etc/lpd/ssl.ca/ca.crt /etc/lpd/ssl.ca/signer1.crt /etc/lpd/ssl.ca/tsign.crt Sign with Certificate '/etc/lpd/ssl.ca/ca.crt' \ [return for yes, ? for list, or specify cert file] signer1 Match Found /etc/lpd/ssl.ca/signer1.crt Sign with Certificate '/etc/lpd/ssl.ca/signer1.crt' \ [return for yes, ? for list, or specify cert file] Private key in /etc/lpd/ssl.ca/signer1.crt Generating user Certificate [papowell] STEP 1: Generating RSA private key for user (1024 bit) STEP 2: Generating X.509 certificate signing request for user .... STEP 3: Generating X.509 certificate signed by /etc/lpd/ssl.ca/signer1.crt ... RESULT: /etc/lpd/ssl.certs/papowell.crt: OK STEP 4. Enrypting RSA private key /etc/lpd/ssl.certs/papowell.key with a pass phrase for security Encrypt the private key now? [Y/n]: Fine, you're using an encrypted private key to sign CERTS. STEP 5: Combine CERT and KEY file Generate single CERT and KEY file? [Y/n] Use the following commands to examine the CERT and KEY files: openssl x509 -text -in /etc/lpd/ssl.certs/papowell.crt openssl rsa -text -in /etc/lpd/ssl.certs/papowell.crt After the certificate file has been created, then it should be copied to the appropriate location: /etc/lpd/ssl.server/server.crt and the password in /etc/lpd/ssl.server/server.pwd, for a server or ${HOME}/.lpr/client.crt and the password in ${HOME}/.lpr/client.pwd for a user. Creating Signing Certificates Having only one signing certificate, i.e. - the root certificate, may make it difficult to delegate authority for the creation of user certificates and/or server certificates. The Permissions and Certificate Revocation The certificate revocation facility is not implemented in &LPRng;, due to various technical and management issues. Instead, the Using MD5 for Authentication &LPRng; has built-in support for using MD5 digests as an authentication method. The implementation is provided as an example of how to add user level authentication into the &LPRng; system. The method used to do authentication is very simple. Each user has a file containing a set of keys that are used to salt an md5 hash. The information being transferred has its md5 checksum calculated using this salt, and is then transferred to the destination, along with the md5 hash result. At the destination the server will get the user id, obtain the salt value from a key file, and then calculate the md5 hash value. If the two are in agreement, authentication is successful. The keyfile used for md5 authentication contains an id followed by a text string whose binary value is used as a hash key: id1=key id2=key Example: lpr@h2=tadf79asd%^1asdf lpr@h1=fdfa%$^&^%$ Printcap Entries Options used: auth=md5use MD5 authentication auth_forward=md5forward using MD5 authentication md5_id=id for server md5_forward_id=id for server md5_server_keyfile=server keyfile Example printcap entry: pr: :lp=pr@wayoff :auth=md5 :md5_id=lpr@wayoff.com pr:server :auth_forward=md5 :md5_id=lpr@wayoff.com :md5_server_keyfile :md5_forward_id=lpr@faroff.com The md5_id value is used by the client to obtain a hash key that is used to salt the md5 calculation for client to server transfers. The md5_forward_id value is used by the server to obtain a hash key that is used to salt the md5 calculation for server to server transfers. The md5_server_keyfile contains the keys of users; the id sent as the connection information is used to obtain the key from the file. To set up md5 authentication, all that is needed is the following. For each user generate a key and place it in the server keyfile. This file should have the form: user1@host1=asdfasdfadf user2@host2=a8789087asddasdf Assign a key to the server, and set its printcap entry to this key. pr: :lp=pr@wayoff :auth=md5 :md5_id=lpr@wayoff.com For each user, create a user key file with the following format: lpr@wayoff = user1@host1 asdfasdfadf The first entry corresponds to the md5_id value in the printcap. The second field is the AUTHUSER value supplied to the server and which will be used to look up the key in the servers key file. Finally, the last field is the salt value for the md5 calculation. User Environment Variables and Files Options used: MD5KEYFILE=5location of user keyfile The MD5KEYFILE environment variable contains the path to the user keytab file. Adding Authentication Support Additional types of authentication support can be added very easily to &LPRng; by using the following conventions and guidelines. First, the authentication method can be connection based or transfer based. Connection based authentication involves the &LPRng; client or server opening a connection to the remote server, having the authentication protocol provide authentication information, and then having no further interaction with the system. This is the easiest to implement and understand method. Code needs to be provided to do a simple authentication exchange between the two ends of the connection, after which no other action needs to be taken. Transfer based authentication is more complex, but allows encrypted transfers of information between the two systems. A connection is established between client and server (or server and server), and an initial protocol exchange is performed. Then the authentication module transfers the command or job information to the destination, where is it unpacked and/or decrypted. The internal Printcap Support By convention, printcap entries auth=XXX and auth_forward=XXX specifies that authentication protocol XXX is to be used for client to server and for server to server transfers respectively. Similarly, the server receiving an authentication request must have a XXX_id=name entry in the printcap or configuration information. This allows several different authentication protocols to be accepted by a server. By convention, printcap and configuration entries of the form XXX_key contain configuration information for the XXX authentication protocol. As part of the authentication support process the XXX_key values are extracted from the printcap and configuration files and placed in a simple database for the authentication support module. If you are using a routing filter, then you can also place XXX_key information in the routing entry for each file, and this will be used for sending the job to the specified destination. Code Support The LPRng/src/common/sendauth.c file has the following entries at the end. #define SENDING #include "user_auth.stub" struct security SendSecuritySupported[] = { /* name, config_tag, connect, send, receive */ { "kerberos4", "kerberos", Send_krb4_auth, 0, 0 }, { "kerberos*", "kerberos", 0, Krb5_send }, { "pgp", "pgp", 0, Pgp_send }, #if defined(USER_SEND) USER_SEND #endif {0} }; This is an example of how to add user level authentication support. The user_auth.stub file contains the source code for the various modules authentication modules. You can replace this file with your own version if desired. The following fields are used. name The authentication name. The auth=XXX printcap or configuration value will cause the name fields to be searched using a glob match. config_tag When a match is found, the config_tag value is used to search the printcap and configuration entries for information. If the config_tag field has value XXX, then entries with keys XXX_key will be extracted for use by the authentication code. connect Routine to call to support connection level authentication. This routine is responsible for connection establishment and protocol handshake. If the value is 0, then the send field value will be used. send Routine to call to support transfer level authentication. The send routine is provided a file and a connection to the remote server, and is responsible for the transferring files. The LPRng/src/common/lpd_secure.c file has the following information at the end: #define RECEIVE 1 #include "user_auth.stub" struct security ReceiveSecuritySupported[] = { /* name, config_tag, connect, send, receive */ #if defined(HAVE_KRB_H) && defined(MIT_KERBEROS4) { "kerberos4", "kerberos", 0, 0, 0 }, #endif #if defined(HAVE_KRB5_H) { "kerberos*", "kerberos", 0, 0, Krb5_receive }, #endif { "pgp", "pgp", 0, 0, Pgp_receive, }, #if defined(USER_RECEIVE) /* this should have the form of the entries above */ USER_RECEIVE #endif {0} }; This information matches the same information in the sendauth.c file. When the authentication request arrives at the server, the name field values are searched for a match, and then the config_tag value is used to get extract configuration information from the database for the protocol. The receive routine is then called and is expected to handle the remaining steps of the authentication protocol. If the routine exits with a 0 value then the lpd server expects connection level authentication has been done and proceeds to simply transfer information using the standard RFC1179 protocol steps. A non-zero return value indicates an error and an error is reported to the other end of the connection. If the receive module is to perform transfer level authentication, then the module carries out the necessary steps to transfer the command and/or job information. It then calls the necessary internal &LPRng; routine to implement the desired services. After finishing the requested work, these routines return to the calling authentication module, which then will transfer data, close the connection to the remote system, and return to the calling system. The combination of 0 return value and closed connection indicates successful transfer level authentication to the server. The user_auth.stub file contains the following code that sets the USER_SEND variable: #if defined(SENDING) extern int md5_send(); # define USER_SEND \ { "md5", "md5", md5_send, 0, md5_receive }, #endif If the SENDING value has been defined, this causes the prototype for md5_send() to be place in the file and the USER_SEND value to be defined. This will cause the md5 authentication information to be placed in the correct table. Connection and Transfer Authentication Rather than go into a detailed description of the code, the user_auth.stub file contains extremely detailed examples as well as several working versions of authentication information. It is recommended that the user start with one of these and then modify it to suit themselves.
Accounting In Academic institutions, avoiding printing accounting has been regarded as a challenge, an ongoing game of fat cat and poor starving mouse, between the brutal and corrupt Administration and the poor, downtrodden, over-charged student. We will disregard the fact that if most students put as much effort into their studies as in finding ways to avoid accounting procedures then they would be Rhodes Scholar material, but I digress... There are two approaches to printing accounting: use software determine the number of pages that should be printed or use a hardware pagecounter facility on the printer to accurately determine the number of pages used. While the software method works well in a relatively error and security compromise free environment and where print jobs do not jam, for accurate account a hardware level pagecounter or some other method must be used. LPRng provides facilities to use either method to determine page counts. There are also two methods available to store the actual accounting information: the simple Accounting Printcap Options The accounting facilities are controlled and enabled by the following entries in the printcap file. TagDefault ValuePurpose afNULLaccounting file name max_accounting_file_size100max accounting file size (in Kbytes) min_accounting_file_size10min accounting file size (in Kbytes) as"jobstart $H $n $P $k $b $t"accounting info for job start ae"jobend $H $n $P $k $b $t"accounting info for job end achkFALSE laTRUEdo accounting for 'local' printer arFALSEdo accounting for 'remote' transfers Accounting Information The :as=jobstart $H $n $P $k $b $t jobstart '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \ '-b1093' '-tNov 5 19:39:25' :ae=jobend $H $n $P $k $b $t jobend '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \ '-b1093' '-tNov 5 19:39:59' If the options have the form :as=|/path ..., then the specified program is run to record the information. By convention the accounting information is passed as command line values to the program. The programs are run in the same manner as a print filter. When the :as=|/usr/local/libexec/jobstart $H $n $P $k $b $t /usr/local/libexec/jobstart '-Hh4.private' '-nroot' \ '-Pps' '-kcfA938h4.private' '-b1093' '-tNov 5 19:39:25' :ae=jobend $H $n $P $k $b $t jobend '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \ '-b1093' '-tNov 5 19:39:59' Accounting File The Accounting File (:as=| ... , then the value is assumed to be a program to be run to record start and end of job information. The program is run in the same manner as a print filter. The values of the If the originate_port option. Finally, if the By convention the The following is an example of information written to the accounting file: jobstart '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \ '-b1093' '-tNov 5 19:39:25' start '-p12942' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \ '-c0' '-Fo' '-tSun Nov 5 19:39:25 1995' filestart '-p12944' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \ '-c0' '-Ff' '-tSun Nov 5 19:39:27 1995' fileend '-p12944' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \ '-b3' '-c0' '-Ff' '-tSun Nov 5 19:39:58 1995' end '-p12942' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \ '-b2' '-c0' '-Fo' '-tSun Nov 5 19:39:59 1995' jobend '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \ '-b1093' '-tNov 5 19:39:59' The jobstart and jobend lines are written by start and end line are added by the The Authorization and Quotas In addition to simply recording accounting information the accounting procedures can be used to check print quotas or update databases. This is done by using the Accounting Check If the If the information was read from a program, then the exit code of the program is checked: Exit Code Action JSUCC (0) process data read JFAIL retry with JFAIL status JHOLD hold job JREMOVE remove job JABORT abort processing jobs other abort processing jobs If the information was read from a network connection or the program exited with JSUCC (0) then the start of the first line of the information read is used. If this line starts with the following case insensitive words then the following actions are taken: Word Action (blank) process job ACCEPT process job FAIL retry with JFAIL status HOLD hold job REMOVE remove job (other) abort processing jobs These facilities can be used to implement a wide variety of quota mechanisms. The most simple method is to create a script or program that can be run as the Accessing Printer Hardware Pagecounters The following is from Hewlett-Packard documentation, http://www.hp.com/cposupport/printers/support_doc/bpl02119.html All HP LaserJet 4/5/6 family printers have a page count feature built into the firmware. However, this feature works differently depending on which HP LaserJet printer is being used. The following is a description of how the page count feature works for each printer within the HP LaserJet 4/5/6 printer families. HP LaserJet 4/4M printers HP LaserJet 4 Plus/4M Plus printers HP LaserJet 4P/4MP printers HP LaserJet 4Si/4Si MX printers HP LaserJet 4ML printers HP LaserJet 5P/5MP printers HP LaserJet 6P/6MP printers All of the above printers use the same method for keeping track of the number of copies. There are really two different page count values: Primary and Secondary values. Every time a page is printed, whether it is an internal job (such as a self-test) or a standard print job, the Secondary page count increases by one. This value is stored in standard RAM. Once the Secondary page count value reaches 10, the Primary page count will increase by 10. The Primary page count value is stored in a type of memory called NVRAM (Non-Volatile RAM). This is important, since NVRAM is not cleared when the printer is powered off. Standard RAM, on the other hand, is cleared when the printer is turned off or reset. Thus, the Primary page count only increases in increments of 10. Example You have a brand new HP LaserJet 6P printer and you print a self-test page. When you look on the test page for the Page Count value, you will see that it says 1. Next, you decide to print a two page letter and, after that, another self-test. The page count value now says 4. Internally, the printers Secondary page count (stored in RAM) has the value of 4 while the Primary page count (stored in NVRAM) still has the value of 0. Now, you turn the printer off, then back on, and print another self-test. The page count value again says 1 since the previous value of 4, stored in RAM, was cleared when the printer was powered off. Finally, print a ten page document and then turn the printer off. Upon turning the printer back on and printing out another self test, you see that the page count value is 11. Internally, the Secondary page count value is back at 1 while the Primary page count value (stored in NVRAM) is 10. Added together, you end up with the resulting value seen on the self-test page. The HP LaserJet 4L/5L/6L printers differ from that of the other printers in that they do not have any NVRAM available for storing page count values. Thus, no way exists for the printer to retain a page count value once the printer is powered off. The HP LaserJet 4L/5L/6L printers have only a single page count value that increases in increments of one until the printer is powered off. Reliable Accounting In order to do reliable accounting, the printer must be queueried for the current value of the pagecounter at the start and end of jobs and this information stored in the accounting file. The ifhp filter can be configured to obtain the pagecounter values and to record them at the start and end of each part of a print job. We can use the Example: lpd generates: jobstart - from the lpd.conf 'as=' option jobend - from the lpd.conf 'ae=' option -H - host name -n - user name -P - printer -k - control file name -b - byte count of job/file -t - current printing time ifhp filter generates: start/end - of filter, for entire job filestart/fileend - if or other filter, for each file (options above are same) -A - identifier information -q - process id of filter -p - current value of page counter, 0 indicates no page counter on printer or it is not readable jobstart '-Hh110.private' '-nroot' '-Plp' \ '-kcfA129h110.private' '-b48780' '-t2001-10-19-09:36:36.000' ^^^ bytes in file start '-q26130' '-p105340' '-t2001-10-19-09:36:38.330' \ ^^^^^^^ starting page counter value for job '-Aroot@h110+129' '-nroot' '-Plp' filestart '-q26132' '-p105340' '-t2001-10-19-09:36:38.350' \ ^^^^^^^ starting page counter value for file '-Aroot@h110+129' '-nroot' '-Plp' fileend '-b19' '-T435' '-q26132' '-p105359' '-t2001-10-19-09:43:51.504' ^^^^^^^ ending page countvalue for file ^^^ number of pages printed for this file '-Aroot@h110+129' '-nroot' '-Plp' end '-b19' '-T435' '-q26130' '-p105359' '-t2001-10-19-09:43:51.504' ^^^^^^^ ending page countvalue for job ^^^ number of pages printed for this job '-Aroot@h110+129' '-nroot' '-Plp' jobend '-Hh110.private' '-nroot' '-Plp' \ '-kcfA129h110.private' '-b48780' '-t2001-10-19-09:43:51.000' ^^^ bytes in file If for some reason the job is killed or terminates due to error conditions, the start '-p100' '-q20005' '-Fo' '-kcfA100taco' '-uuser' '-hhost' '-R... filestart '-p101' '-q20005' '-Ff' '-kcfA100taco' '-uuser' '-hhost' '-R... start '-p110' '-q20005' '-Fo' '-kcfA101taco' '-uuser' '-hhost' '-R... filestart '-p112' '-q20010' '-Fo' '-kcfA101taco' '-uuser' '-hhost' '-R... end '-p112' '-q20010' '-Fo' '-kcfA101taco' '-uuser' '-hhost' '-R... The missing &LPRng; accounting.pl Utility The &LPRng; accounting.pl utility provides the basic framework for using the The utility maintains the accounting file by inserting a The following shows the printcap entry for using the printer :af=acct :as=|/usr/local/libexec/filters/accounting.pl start :ae=|/usr/local/libexec/filters/accounting.pl end At the start of each job the utilty writes a At the end of the job, the utilitity will read the accounting file and use the recorded information to update the accounting information. In order to make this reliable, the following steps are taken. The accounting file is read and scanned for the last The file is scanned for If the last line in the accounting file does not indicate successful completetion of the job and contain pagecounting information, then the accounting procedure is abandoned until the next job completes successfully. If the last line in the accounting file indicates successful completion, then its pagecounter value is used as the last page counter value. Job information is updated by finding the start and end pagecounter values for each job. It is possible that a job will not have a pagecounter value recorded at its start; in this case the page usage will be 0, as it did not even get initialized. After determining the accounting information, the procedure will then update and databases and the accounting file. During this update, interrupts should be disabled and the amount of time taken to update the accounting information and/or file should be minimized. Administrators can use this script as a starting point for more advanced accounting. For example, rather than just recording the information, at the job start the script can query either a local database or a remote server to see if the user has permissions to access the printer. At the end of the job or when an END line is written to the accounting file, the local database or remote accounting server can be updated. RFC 1179 - Line Printer Daemon Protocol RFC1179 can be obtained from the &LPRng; distribution, in the LPRng_DOC/rfc1179 directory, or from one of many sites which mirror the RFCs. This RFC is an informational RFC, which means that the information in it is meant as a guide to users, and not as a fixed standard. In addition, the RFC tried to document the behavior of the BSD In this section, I will try to explain what RFC1179 specifies as a protocol, and many of the problems encountered in trying to use it. Ports and Connections Options used: lpd_port=Port for lpd_listen_port=Port for originate_port=Ports to originate connections on reuse_addr FLAG Set SO_REUSEADDR flag on connection retry_econnrefused FLAG Retry on connect ECONNREFUSED error retry_nolink FLAG Retry on device open or connection ffailure unix_socket_path PATH UNIX FIFO pathname for local connections socket_linger=socket linger timeout RFC1179 requires that the lpd server listen for TCP/IP connections on port 515. This port is registered with the Internet Naming Authority, and the /etc/services file or TCP/IP services database usually has an entry: printer 515/tcp spooler # line printer spooler RFC1179 explicitly states that all connections to port 515 must originate from ports 721-731. The reason for this restriction is due to the UNIX concept of reserved and privileged ports. By convention, ports in the range 1-1023 can only bound by processes whose Effective User ID (EUID) is 0 (root). This, ordinary users could not originate a connection from the reserved or privileged port range. In a UNIX environment, this means that the user programs lpr, lpq, lprm, and lpc would have to be SETUID root. As experience has shown, for security purposes, the fewer programs that need to have privileged status, the better. &LPRng; uses the lpd_port=printer configuration option to set the port for the connections to a lpd_listen_port=printer configuration option can be used to specify a port for the lpd_listen_port value is specified the lpd_port value will be used as the The The restriction of originating ports to 721-731 causes another set of problems. Part of the TCP/IP protocol is concerned with avoiding communications problems resulting from the arrival of old or stale packets. When a connection between sourcehost, sourceport and desthost, destport is made, a set of sequence numbers is established and used for sending and acknowledgement of data. When the connection terminates, the TCP/IP protocol restricts the establishment of a new connection between sourcehost, sourceport and desthost, destport for a period long enough for all stale packets to be removed from the system. This is approximately 10 minutes long. In order to simplify assignments of ports, timing out connections, and other matters, many TCP/IP packages do keep track of explicit connections originating from a port, but simply prevent the port from being reused for either origination or reception of a connection. They do, however, keep track of the active connections to a port, and perform timeouts on these. This is usually much simpler to implement, as it can be done with a list attached to the port. This implementation method creates some problems when a large number of connections must be originated from a relatively small number of port numbers. Observe what happens when host 1 tries to send a large number of jobs to a server 2. The following connections are established and terminated: host 1, port 721 and host 2, port 515 host 1, port 722 and host 2, port 515 host 1, port 723 and host 2, port 515 host 1, port 724 and host 2, port 515 host 1, port 725 and host 2, port 515 host 1, port 726 and host 2, port 515 host 1, port 727 and host 2, port 515 host 1, port 728 and host 2, port 515 host 1, port 729 and host 2, port 515 host 1, port 730 and host 2, port 515 host 1, port 731 and host 2, port 515 Now according to the RFC1179 rules and the TCP/IP protocol, we will have to wait until one of these connections terminates before we can make another. On the originating system, if the TCP/IP implementation does timeouts on the originating port, we will have to wait for the timeout to elapse before we can make a new connection. Unfortunately, there is no way to find out what the status of the port is, so we will have to try them each in turn until we get a successful connection. The &LPRng; code has tried to provide several methods to deal with these problems. Firstly, the originate_port=512 1023 option specifies the range of ports used to originate connections when the software is running either as ROOT or SETUID root. By strict RFC1179 rules, this should be originate_port=721 731, but it turns out that most BSD reserved originating port. By using 512 ports we get a greatly reduced rate of errors due to lack of ports due to pending timeouts. However, on some systems which are acting as servers for a large number of printers even increasing this port range is insufficient, and steps need to be taken use the originating port numbers more efficiently. The Berkeley TCP/IP implementation getsockopt() and setsockopt() allows the user to manipulate some of the underlying timeouts and options of the TCP/IP network. When a TCP/IP connection is established, the setsockopt() facility can be used to set the SO_REUSEADDR flag on the connection. This flag effectively sets the timeout value on the ports and connections to 0, allowing immediate reuse of the ports. When done on an originating end of a connection, this will allow the originating port number to be reused immediately. It would appear that by setting SO_REUSEADDR on the originating end that we have solved our problems. However, unless the destination end of the connection sets its SO_REUSEADDR flag on the connection, it will still do a timeout. Thus when we try to make a connection from a port that was active within a short period of time to the same host, then it will reject the connection until the timeout is over. The reuse_addr flag (default off) forces the &LPRng; software to set the SO_REUSEADDR flag on originating connections. As indicated, this will allow ports to be reused immediately for outgoing connections, rather than waiting for a timeout. While the reuse_addr flag usually allows us to reuse ports, there is still the problem of dealing with connections failing due to the remote site rejecting the connection due to a pending timeout from a previous connection. A careful study of the original BSD TCP/IP network code and of some others indicates that when a connection fails due to a pending timeout, an ECONNREFUSED error code is returned to a connect() system call. If this happens and we suspect that the remote site is rejecting the connection due to a timeout problem, then we should retry making the connection but from a new port, and continue retrying until all possible ports are used. The retry_econnrefused (default on) flag is used to specify that we retry connections in this manner. When this is set, a connection refused error causes the connection to be retried using a new port. This will be repeated until all available ports have been tried. When printing a job and the lpd server connection to a remote site or device open fails, the retry_nolink (default on) will cause the attempt to be retried indefinitely. The combination of retry_econnrefused and retry_nolink will provide robust connection attempts to remote systems. While the above problems cause difficulties when making connections, there are also problems when terminating connections. After closing a socket, the TCP/IP software will try to flush any pending data to the destination. Unfortunately, on some systems it will only do this while the process is active. This has caused problems on systems which terminate a process it has received an abnormal (signal caused) termination. The setsockopt() SO_LINGER option allows the user to specify that when a socket is closed normally, that the process should block until pending data is flushed or for the socket_linger period. If socket_linger is 0, then no SO_LINGER operation is done. In summary, if you experience problems with connection failures due to port exhaustion, first try setting the reuse_port flag, and you should see a reduction. Check to ensure that the retry_econnrefused and retry_nolink flags are set, and the error code in the log and status files. If the failures continue, then the problem is caused by the remote end having timeout limitations and there is little you can do except to set a very long connect_retry interval, say connect_retry=120 (2 minutes). Protocol Requests and Replies Options used: remote_support=Remote operations supported After a connection has been established, a request can be sent to the lpd server. The request consists of a single octet indicating the request type, followed by the printer (or print queue) name, followed by a set of options for the request, followed by a LF (line feed) character. RFC1179 Commands NNNRFC1179Operationprogram1yesstart printlpc2yestransfer a printer joblpr3yesprint short form of queue statuslpr4yesprint long form of queue statuslpr5yesremove jobslprm6&LPRng;do control operationlpc7&LPRng;transfer a block format print joblpr8&LPRng;secure command transferlpc9&LPRng;verbose status informationlpr
After the request has been sent, then a reply will be returned. In general the reply has the following form: \000\n Success \NNN\n Failure (NNN is error code) text\n Text or status information As can be seen, this protocol is extremely simple, but there are a set of problems due to the loosely written language of RFC1179. Firstly, while RFC1179 sets limits on the lengths of commands, it does not strictly set limits on the characters set used in the commands. This can result in problems when trying to print status information, headers on banners, and other details. The original RFC1179 protocol did not provide any way to do remote control of queues or lpc to control a non-&LPRng; printer, it will not work. You can specify that a network printer is non-&LPRng; by using the remote_support=RQVMC option and specify the operations supported by the printer. The letters R, Q, M, and C stand for lpr, lpq, lprm, and lpc operations respectively, and indicate that these are supported. If remote_support does not allow a particular operation, then the &LPRng; software will not send a corresponding request to the printer. For example, remote_support=R would restrict operations to spooling jobs only, and the &LPRng; software would not query the printer for status.
Job Transfer Options used: longnumber FLAG Long job number (6 digits) send_data_first FLAG Send data files first use_shorthostUse short hostname A job transfer operation starts with a job transfer request, followed by several file transfer operations. At the end of the file transfers, the connection should be closed. A file transfer request has the form: CommandPurpose \001\nabort \002nnnn cfnamecontrol file transfer \003nnnn dfnamedata file transfer The abort operation is used to terminate job transfer and indicate that the job should not be processed for printing. The connection will be closed and the partly transferred job will be discarded. The control file and data file transfer commands have a length (in bytes) of the file and the name of the file to be transferred. When the command is received, the server will reply with a status line: StatusPurpose \000Accepted, proceed \nnnRejected with error code The reply is only a single octet. Some defective implementations of RFC1179 send a LF after the octet, which makes life very difficult. &LPRng; makes an effort to detect these non-conforming RFC1179 systems and will accept jobs from them. However, it will not send jobs to them. If &LPRng; sends a reject code, as an extension to RFC1179 it also sends an error message. Note that the values for error codes are not defined, nor are their causes. &LPRng; uses the following values for error codes, which appear to be compatible with many, but not all, of the BSD CodeError \000Accepted, proceed \001Queue not accepting jobs \002Queue temporarily full, retry later \003Bad job format, do not retry When the sender gets the reply indicating success, it sends the nnnn bytes of the control or data file, followed by a \000 octet. The receiver will then reply as above; a single \000 octet indicating success. The above procedure is carried out until all data files and the control file of a job are transferred. RFC1179 is silent on the following issues: When sending a job, do you send the control file first, followed by the data file(s), or the data files first? When sending multiple jobs, can you send them on a single connection, or do you have to establish a new connection for each job? &LPRng; will accept jobs whether they are sent control or data files first. By default, it sends the control file first, followed by the data file. If the destination system requires that the data files be sent first, the send_data_first printcap option can be used to force data files to be sent first. RFC1179 states that:
The name of the control file ... should start with ASCII "cfA", followed by a three digit job number, followed by the host name which has constructed the control file.
The should in this wording indicates that this is simply a guideline, and that other formats are possible. Some of the major problems with this format are as follows: The restriction to 3 digits means that at most 1000 jobs can be in a queue. Strangely, some systems generate far more than 1000 jobs a day, and need to archive them on a regular basis. The longnumber option will allow &LPRng; to use a 6 digit job number for files in the print queue. The host name format is not specified. Some implementations consider that this is the short host name, while others think it is the fully qualified domain name (FQDN). &LPRng;, by default, will use the FQDN host name. However, the use_shorthost option will force it to use short host names in control and data files. The cfA control file name was modified to allow the job priority to be used as the A letter of the control file. By default, this is A (lowest, i.e. cfA) and but can range to Z (highest, i.e. cfZ). All known spoolers except &LPRng; seem to ignore the actual value of the letter.
Data File Transfer As mentioned before a data file is transferred using the command below. CommandPurpose \003nnnn dfnamedata file transfer From RFC1179:
The data file may contain any 8 bit values at all. The total number of bytes in the stream may be sent as the first operand, otherwise the field should be cleared to 0. The name of the data file should start with ASCII "dfA". This should be followed by a three digit job number. The job number should be followed by the host name which has constructed the data file. Interpretation of the contents of the data file is determined by the contents of the corresponding control file.
There are several surprises in RFC1179. Apparently a job should only consist of a single data file. This is a severe limitation, and in fact the BSD dfA, dfB, ... dfZ, dfa, dfz. The RFC does not specify that the control file and data file job numbers must be identical. Most implementations follow this convention, which simplifies life tremendously. The RFC does not specify that the control file and data file job host names must be identical. Most implementations follow this convention, which simplifies life tremendously. A zero length data file does not cause a data transfer to take place. &LPRng; modifies this action to be slightly different. When a zero length data file transfer is indicated, all of the input until the connection is closed is used as the contents of the data file. When lpr
program, this can be very useful as it eliminates the need to create temporary files on the local host. Note that some print spoolers do not use this interpretation, and this option should be used carefully.
Control File Contents The control file consists of a set of lines which either provide printing information or specify data files to be printed. The information lines start with upper case letters or digits, while the data files lines start with lower case letters. Here is a sample control file: Hh4.private J(stdin) CA Lpapowell Apapowell@h4+955 Ppapowell fdfA955h4.private N(stdin) UdfA955h4.private The following are the letters and their meanings in the control file. Control File Lines and Purpose LetterDefinedPurposeA&LPRng;Identifier for jobCRFC1179Class for banner pageHRFC1179Host nameIRFC1179Indent PrintingJRFC1179Job name for banner pageLRFC1179Print banner pageMRFC1179Mail When PrintedNRFC1179Name of source filePRFC1179User identificationQ&LPRng;Queue nameR&LPRng;Accounting infoSRFC1179Symbolic link dataTRFC1179Title for prURFC1179Unlink data fileWRFC1179Width of outputZ&LPRng;Filter options1RFC1179troff R font2RFC1179troff I font3RFC1179troff B font4RFC1179troff S fontcRFC1179Plot CIF filedRFC1179Print DVI filefRFC1179Print formatted filegRFC1179Plot filekRFC1179Reserved for use by Kerberized &LPRng; clients and servers.lRFC1179Print file leaving control charactersnRFC1179Print ditroff output fileoRFC1179Print Postscript output filepRFC1179Print file with 'pr' formattRFC1179Print troff output filevRFC1179Print raster filezRFC1179Reserved for future use with the Palladium print system.
The A (Identifier) line was introduced to record a unique system wide job identifier for &LPRng; submitted jobs. This is basically formed from the user name, job number, and host at the time of submission. For example: papowell@h4+955 is job number 995 submitted by papowell from host h4. The C (Class) line is set by the lpr -C class option, and the value can be used to control printing. For example, the lpc class zone command would restrict job printing to only jobs with class zone. The H (hostname), P (username), and J (jobname) fields are used to identify the host and user which sent the job, and to provide information to be displayed by lpq when reporting job status. The L (print banner page) field is one that has caused many problems for users. RFC1179 indicates that its presence causes the banner page to be printed, and its absence suppresses banner pages. The lpr -h option suppresses putting this line into the control file. Usually the L field is a duplicate of the P field. The M (mail information) field supplies a mail address for &LPRng; to send mail to when a job is completed. See Job Completion Notification Requested for more details. The N (file name) field is usually provided to identify the file name corresponding to the data file. This can be used to print names on page separators, etc. &LPRng; largely ignores this line. The I (indent) and W (width) fields are supposed to specify a page indent and width for printing. These fields are passed to filters if they are present. The Q (queue name) field is an &LPRng; extension, and contains the name of the print queue the job was originally sent to. See qq printcap option for details. The R (accounting info) field was added by &LPRng; to allow a specified account to be billed for job printing. The lpr -Rname option can be used to specify the accounting name. The S (symbolic link) and U (unlink after printing) lines were used by the original BSD S lines and force the U lines to refer only to job data files. This closes a nasty security loophole on non-&LPRng; print spoolers. The T (pr job title) is used with the lpr -p operation to supply a banner to the pr program. The Z (filter options) value is specified with lpr -Zoption and is passed to the data file filters during the printing operation. See Filters for details on how the this is used during the printing process. All of the lower case letters are reserved for format specifications for data files. In the control file, these are followed by the name of the data file to which they correspond. While in principle different data files in the control file can have different formats, this has not been implemented in any known spooling system. See Filters for details on how the data file formats are used during the printing process.
<application>lpq</application> Requests The RFC1179 protocol specifies that lpq print status requests can be sent to the lpd server. The lpq requests have the format: \003printer [id]* \n short \004printer [id]* \n long \011printer [id]* \n &LPRng; extension- verbose The lpd print server will then return queue status and close the data connection. RFC1179 does not state in any manner what the format of the queue status should be. Thus, implementors have been free to augment or change the status as they like. Even the BSD See for information on the formats returned. The id values are used to select the jobs to be displayed. &LPRng; displays any job whose ID, hostname, or user name information from the control file A, H, or P fields match any of the id values. Note that since there is no identification of the information requestor, then restriction of information is almost impossible. <application>lprm</application> Requests The RFC1179 protocol specifies that lprm job removal requests can be sent to the lpd server. The lpq requests have the format: \005printer user [id]* \n The lpd print server will search the specified print queue and remove any job whose ID, hostname, or user name information from the control file A, H, or P fields match any of the id values and for which the user has permission to perform a removal operation. See Permissions and Authentication for details. Most RFC1179 compatible spoolers use the user information in the request as the name of the user which spooled the job. However, in a network environment this is extremely easy to fabricate, and is at best a weak type of authentication. LPC Requests &LPRng; has extended the RFC1179 protocol to allow queue and printer control commands to be sent to the \006printer user key [options] The following commands are supported. LPC Commands CommandOperationCommandOperation active [printer[@host]]check to see if server accepting connections abort (printer[@host] | all) terminate server process printing job disable (printer[@host] | all) disable queueing debug (printer[@host] | all) debugparms set debug level for printer enable (printer[@host] | all) enable queueing hold (printer[@host] | all) (name[@host] | job | all)* hold job holdall (printer[@host] | all) hold all jobs on kill (printer[@host] | all) stop and restart server lpd [printer[@host]] get lpq (printer[@host] | all) (name[@host] | job | all)* invoke lprm (printer[@host] | all) (name[@host]|host|job| all)* invoke move printer (user|jobid)* target move jobs to new queue noholdall (printer[@host] | all) hold all jobs off printcap (printer[@host] | all) report printcap values quit exit LPC redirect (printer[@host] | all) (printer@host | off )* redirect jobs release (printer[@host] | all) (name[@host] | job | all)* release job reread [printer[@host]] start (printer[@host] | all) start printing status (printer[@host] | all) status of printers stop (printer[@host] | all) stop printing topq (printer[@host] | all) (name[@host] | job | all)* reorder job defaultq default queue for local (printer | all) client printcap and configuration information server (printer | all) server printcap and configuration information
Many of these commands support extremely specialized operations for print queue management, However, the following are the most commonly used and are supported by the BSD start, stop, enable, disable Start and stop will start and stop printing for a specified queue. Enable and disable enable and disable sending and/or accepting jobs for the queue. abort, kill Abort will cause the process doing the actual job printing to be terminated. Kill does an abort, and then restarts the printing process. These commands are used to restart a queue printing after some disaster. topq Places selected jobs at the top of the print queue. status Shows a status display of the print spools on the server. The following commands are extensions to the basic set provided by the BSD lpq, lprm Invokes the lpq or lprm program from lpc. Useful when in the interactive mode. hold, holdall, release The hold command will cause the selected jobs to be held until released. The holdall jobs sets all jobs submitted to the queue to be held until released. The release command releases jobs for printing. If a job has had an error and is in the error state, the release command will cause it to be reprinted. move, redirect The move command will move selected jobs to the specified spool queue. The redirect command sends all jobs submitted to the queue to be sent to the specified queue. active, lpd, reread The active command will connect to the server for the printer. This is used to check to see if non-&LPRng; print servers are active. The lpd command will connect to the server and get the process id (PID) of the lpd server. The reread command causes a SIGHUP signal to be sent to the lpd process, causing it to reread the lpd.conf, printcap, and lpd.perms files. This is done when configuration information has been modified and the administrator wants to have the server use the new information. debug This is a desperation facility for developers that allows dynamic enabling of debug information generation. Not normally used in general operation. local, server These commands will print out the configuration information in the local lpd.conf file, as well as the printcap information for the specified printers; client prints what the &LPRng; clients (lpr, lpq, ...) would use while server prints what the &LPRng; server (lpd) would use if running on this host. This is an extremely useful diagnostic tool for administrators. Not normally used in general operation.
Block Job Transfer Options used: send_block_format FLAG Transfer job as a block In normal job transfer operations, the sender and receiver have a handshake interaction in order to transfer a print job. Each file is sent individually. The send_block_format option forces a Block Job Transfer operation. This causes the sender to transfer a single file containing all the job printing information, including control file and data files. The transfer command line has the form: \007printer size\n The receiver will return any acknowledgement of a single 0 octet, and then the size bytes of the job will be transferred by the sender. At the end of the transfer a single 0 octet is added, and the receiver will indicate success by returning a single 0 octet. Any other value returned by the receiver indicates an error condition. The file transferred by the sender is simply the command lines that it would have normally sent for job transfer, followed by the control or data file values. Authenticated Transfer RFC1179 does not provide any authentication or encryption mechanism for the transfer of jobs or commands to the lpd print server. The Authenticated Transfer operation was added to allow an encrypted or authenticated transfer of print jobs or commands. Since there are various restrictions on the incorporation of authentication facilities into programs, &LPRng; supports authentication by providing a simple interface to encryption programs. The idea is that when authentication is required when sending a job, &LPRng; will generate a block transfer job as described for the Block Job Transfer operation, and then invoke a set of programs to encryt and transfer the file, and encrypt and transfer the returned status. Similarly, when sending a command, the command information will be placed in a file and the encrypted file will be transferred. This technique means that the programs and support to do encryption are external to &LPRng;, and can use any type of method that they choose to implement the secure and/or authenticated transfer. See Authentication and Encryption for details on the authentication interface.
The Most Frequently Asked Questions In this section, the Most Frequently Asked Questions have been placed, together with their answers. You may notice that some questions have the same answer, but the symptoms appear differently. Some of these answers will reference other material in this FAQ, or the &LPRng; man pages. Why do I get malformed from address errors? This is the number one question asked by most &LPRng; users who try to use &LPRng; with network printers or other systems supporting RFC1179 printing. For details about &LPRng; and RFC1179, see RFC1179 and &LPRng;. The malformed from address error is usually reported when trying to send a print job from &LPRng; to other BSD Servers originate a connection from ports in the range 721-731. WHY? These are a subset of the 'reserved' ports in UNIX, and normal users cannot open connections from them. This provides a small amount of security from UNIX users on the host 'spoofing' a server. IMPLICATION: in order to do use a reserved port, the program must have root privileges. This means the LPR, WHAT TO DO: When installing &LPRng; you will need to install the executables SUID root. In the src/Makefile, you can remove the comment from the line PERMS=SUID_ROOT_PERMS and then do make install. This will install the executables SUID, and owned by root. It was working normally, then I get connection refused errors This message usually appears when you have been sending a large number of jobs to a network printer or a remote system. The reason for this is a combination the above port 721-731 restriction and the TCP/IP timeouts. For details, see RFC1179 and &LPRng;, but here is a quick explanation. A TCP/IP connection is usually specified as between srchost:srcport, desthost:destport, although in practice the order of source (src) and destination (dest) is not important. When a connection is established, each end of the connection exchanges the necessary flow control and error control information. When a connection is terminated, each end of the connection will not accept another connection from the same host:port that was previously active for a specified timeout period, usually 10 minutes. Some TCP/IP implementations go further: they will not allow ANY connection to be originated (via the bind() system call or API) from a port that was active, or accepted on a port that was active for this timeout period. Now let us see what happens when we have a client program, which must originate a connection on port 721-731, connect to the server, which waits for a connection on port 515. We first try to make a connection from host:port 1.1.1.1:721 to 1.1.1.2:515. The first time that we make the connection (or the first connection) we succeed. We can transfer a file, etc., and then close the connection. When we try to reconnect from 1.1.1.1:721 to 1.1.1.2:515 we get an error such as "address already in use" or "connection refused". Luckily, we can use port 722 to originate a connection, and we can connect from 1.1.1.1:722 to 1.1.1.2:515. We continue on, until we come to port 731, and then we need to wait for our timeouts. SOLUTION: It appears that most RFC1179 implementations do not check for the exact port range 721-731, but only that the connection originates from a reserved port, i.e. - in the range 1-1023. You can extend the range of ports used by &LPRng; by changing the originate_port=721 731 value in the defaults (LPRng/src/common/defaults.c) file or in the lpd.conf file. I recommend the following: originate_port=512 1022 This is, in fact, now the default in &LPRng; software. If you get the infamous malformed from address error message from your spooler, then you will have to set originate_port=721 731, and live with a delayed throughput. Job is not in print queue, but it gets printed! In the original BSD This type of operation required spool directory space, special SETUID programs, and a slew of headaches in system security and management. The LPR, However, some users require or want their jobs to be spooled on the local host system, and then transferred to the remote printer. This is usually the case when some type of processing (filtering) is needed in order to print the job correctly. There are several methods that can be used to force this. Method 1: Explicit Printer Address You can force a job to be sent directly to the pr serviced by the host by using the form: lpr -Ppr@host file You can also set the PRINTER environment variable to a similar form, and get the same effect: PRINTER=pr@host; export PRINTER; lpr file Method 2: User and Server Printcap Entries If you want to have the benefits of a printcap file, i.e. - you can use aliases or abbreviations for the names of printers, then here is a couple of hints. First, the &LPRng; software scans the printcap file for printcap entries, combining information for the same printer into a single entry. Information found later in the printcap file will override earlier information. In addition, you can tag entries as either being used for all utilities or just for the # for all utilities pr:lp=pr@host # just for lpd pr:server :lp=/dev/lp # more information pr:check_for_nonprintable@ # --- final result for LPR pr:lp=pr@host:check_for_nonprintable@ # --- final result for lpd pr:lp=/dev/lp:check_for_nonprintable@ As you can see, the server keyword indicates that the printcap entry is only for the server. The /dev/lp. Note that the lp=... information overrides the :rp: (remote printer) and :rm: (remote machine) fields if they are present. Method 3: Force sending to server on localhost The force_localhost printcap or configuration flag forces non- This method is similar to the previous one, but has the benefit that it can be configured as a global (i.e. - applies to all printers) rather than printer specific. You can put this in the lpd.conf file for general application, or have a printcap entry of the following form: # for all utilities pr:lp=pr@host:force_localhost The force_localhost flag, and send jobs to the pr queue on the host machine. However, the LPR, Job disappears and is never printed, but lpr works This is a rather disconcerting problem, and usually occurs when sending jobs to either a network printer or a nonconforming RFC1179 print spooler. For details about &LPRng; and RFC1179, see RFC1179 and &LPRng;, but here is a quick explanation. An SOLUTION: Set the send_data_first flag in the printcap for the particular printer, or in the lpd.conf configuration file. This is: :send_data_first: (printcap) send_data_first (lpd.conf) Note that some printers/servers INSIST on the control file first; You can clear the flag using send_job_first@ if you need to. I get messages about bad control file format RFC1179 describes a set of fields that MAY appear in the control file. It is silent if other ones can appear as well. Unfortunately, some implementations will reject jobs unless they contain ONLY fields from a very small set. In addition, RFC1179 is silent about the ORDER the fields can appear. &LPRng; quite happily will accept jobs from poor or nonconforming RFC1179 spooler programs, and fix them up to be conformant. If you are sending jobs to one of a non-conforming spooler, you can force &LPRng; to send jobs with only the fields described in RFC1179 by setting the the :bk: (BacKwards compatible) flag in the printcap for your printer. What is RFC 1179, the Line Printer Daemon Protocol? RFC1179 defines a standard method by which print jobs can be transferred using the TCP/IP protocol between hosts. The standard was developed by simply detailing the way that a version of the BSD From the RFC Introduction:
RFC 1179 describes a print server protocol widely used on the Internet for communicating between line printer daemons (both clients and servers). RFC1179 is for informational purposes only, and does not specify an Internet standard.
Having said this, the RFC then goes on to describe the protocol used by a particular implementation of Comment by Patrick Powell <papowell@lprng.com> :
Since 1988, there have been a large number of print spooling systems developed which claim RFC1179 conformance, but which are mutually incompatible. Rather than live with the limited capabilities of the RFC1179 standard, &LPRng; has extended them by adding capabilities to perform remote control of print spoolers, encrypted and authenticated data transfers, and other operations missing from the RFC1179 specification. However, great effort was made to be backwards compatible with older and other &LPRng; was developed in order to be able to both accept and provide interactions with these systems. It does so by allowing various options to be used to tune how print jobs would be exchanged. Currently, &LPRng; can be configured to send and receive print jobs between a vast number of the existing spooling systems. It is flexible enough to act as a gateway between non-compatible systems, and has provisions to transform jobs from one format to another in a dynamic manner.
For a detailed explanation about &LPRng; and RFC1179, see RFC1179 and &LPRng;.
I want to replace lp, lpstat, etc, but my programs need them &LPRng; was designed as a replacement the BSD printing system. As such, it inherited its command names and options from the latter. As you might know, SystemV uses a totally different set of commands, incompatible with the BSD ones. The good news is that the &LPRng; binaries include an emulation for the SystemV commands. (See lp Simulation for details. Briefly, you create links to the appropriate programs, and invoke them by the link names. Actually, these links are installed by default in recent versions. ln -s lpr lp ln -s lpq lpstat ln -s lprm cancel If you make these links, calling lp, lpstat and cancel will give you a (partial) SVR4 emulation. They have their own man pages, which you should read if you need the emulation. Since it is a partial emulation, you shouldn't expect everything to work. In particular, I would guess that any script which relies on the output format of one of your system binaries will break. Again, see lp Simulation for more details or additional suggestions.
Remote Logger Operation Several sites have wanted a way to provide central logging of job status and/or information. In order to do this, the following functionality is implemented in &LPRng;. Logger Network Communication Options used: logger_destination=logger information destination logger_pathname=pathname of temp file for log information logger_max_size=max size in K of temp file for log information logger_timeout=time between connection attempts The printcap/configuration variable logger_destination specifies a destination in the standard host%port notation used by &LPRng;. Host is the destination host, and can be a name or IP address. Port is the port on the destination host. A TCP/IP connection is made to the indicated port. Log information is save in a temporary file specified by logger_path, and up to logger_max_size K bytes of data will be saved. If a connection cannot be made to the logger_destination, then every logger_timeout seconds a new connection attempt will be made. If logger_timeout is 0, then a connection attempt will be made every time new data arrives to be logged. Logger Messages Log messages consist of a single line terminated with a newline (\n) character. Each log message reports a system event or status change of the LPD server. When the connection is first established, a complete dump of the status of the LPD server is sent. After this, only status update messages are sent. The remote monitor can force a status dump by simply closing and reopening the connection. Message Format Each message is encoded as a URI escaped string. That is, non-alphanumeric characters are encoded as the 3 character sequence %xx, where xx is the hexadecimal value of the character. The message has the format key=value, where key indicates the message type. For example: dump=host=h4.private%0aprinter=t1%0aprocess=1613%0a update_time=1999-03-23-20:32:17.148%0a\ value=queue=holdall 0%25250aprinting_aborted=0x0%25250a\ printing_disabled=0x0%25250aspooling_disabled=\ 0x0%25250a%250a%0a The following keys are used: dump A status dump of the current contents of a print queue. Each message has a set of headers and a value. For example, the decoded dump message from the previous section would be: host=h4.private printer=t1 process=1613 update_time=1999-03-23-20:32:17.148 value=queue=holdall 0%250aprinting_aborted=0x0%250a\ printing_disabled=0x0%250aspooling_disabled=0x0%250a%0a Each line consists of a key and a value. The host key indicates the host name, printer is the print queue, process is the process which generated the report or action, update_time is the time at which the report was generated, and value is the value of the report. The decoded value of the above report is: queue='holdall 0%0aprinting_aborted=0x0%0aprinting_disabled=0x0%0a\ spooling_disabled=0x0%0a The queue key provides the current value of the queue control file. Dump Messages Dump messages are generated at the start of operations, and consist of a list of queue status messages. LPD Messages These are used to indicate LPD startup or change in operation. Decode: lpd=host=h4.private%0aprocess=1672%0aupdate_time=1999-03-23-20:5 1:10.507%0avalue=Starting%0a host=h4.private process=1672 update_time=1999-03-23-20:51:10.507 value=Starting lpd: 'Starting' Job Status Messages - UPDATE Update messages are used to report changes in the queue contents, such as job arrival. Decode: update=host=h4.private%0aidentifier=papowell@h4+676%0anumber= ... host=h4.private identifier=papowell@h4+676 number=676 printer=t1 process=1677 update_time=1999-03-23-20:51:17.197 value=bnrname=papowell%0acf_esc_image=Apapowell@h4+676%250aCA%250aD1999-03- ... This update message reports the arrival of a new job at the queue. The value field reports the control file contents: class=A date=1999-03-23-20:51:17.151 file_hostname=h4.private fromhost=h4.private held=0x0 hf_name=/var/tmp/LPD/t1/hfA676 hold_class=0x0 hold_time=0x0 identifier=papowell@h4+676 job_time=0x36f86f45 jobname=/tmp/hi logname=papowell number=676 priority=A queuename=t1 size=3 transfername=cfA676h4.private update_time=1999-03-23-20:51:17.187 The update_time field in the section above is the time that the job information was last updated. The cf_esc_image value is the URL escaped control file information. Printer Status Messages - PRSTATUS These messages report printing or other activity related to a job. Decode: prstatus=host=h4.private%0aidentifier=papowell@h4+676%0anumber= 676%0aprinter=t1%0aprocess=1692%0aupdate_time=1999-03-23-21:02:04.855%0avalue= finished 'papowell@h4+676'%252c status 'JSUCC'%0a host=h4.private identifier=papowell@h4+676 number=676 printer=t1 process=1692 update_time=1999-03-23-21:02:04.855 value=finished 'papowell@h4+676'%2c status 'JSUCC' PRSTATUS: 'finished 'papowell@h4+676', status 'JSUCC'' Index To All The Configuration and Printcap Options &LPRng; Options Option Purpose or Value ab always print banner, ignore lpr -h option accounting_namefixup update accounting name information achk query accounting server when connected ae accounting at end (see also af, la, ar, as) af name of accounting file (see also la, ar) ah automatically hold all jobs allow_getenv Allow use of LPD_CONF allow_user_logging allow users to request logging info using lpr -mhost%port allow_user_setting allow privileged user to impersonate other users ar enable remote transfer accounting (if af is set) as accounting at start (see also af, la, ar) use_auth authentication type to use auth_forward authentication type for forwarding be Banner at End Generation Program bk Berkeley --> bk_filter_options Berkeley --> bk_of_filter_options Berkeley --> bkf backwards-compatible filters: use simple parameters bl short banner line sent to banner printer bp Banner Generation Program (see bs, be) bq_format Format of bounce queue output br Serial port bit rate (see ty) bs Banner at Start Generation Program check_for_nonprintable --> class_in_status Show job class name in lpq status information client Mark printcap entry for client programs only cm comment identifying printer ( --> config_file configuration file connect_grace connection control for remote printers connect_interval connection control for remote printers connect_timeout connection control for remote printers connect_try connection control for remote printers create_files create spool queue files control_filter control file filter db debug options for queue default_format default job format default_permission default permission for files default_printer default printer default_priority default job priority default_remote_host default remote host default_tmp_dir default directory for temp files destinations printers that a route filter may return and we should query done_jobs save status for last N jobs done_jobs_max_age remove status older than N seconds fifo enforce FIFO job ordering ff string to send for a form feed filter_ld_path filter LD_LIBRARY_PATH value filter_options filter options filter_path filter PATH environment variable fo send form feed when device is opened force_fqdn_hostname force FQDN hostname value in control file force_localhost force clients to send all requests to localhost force_queuename force use of this queuename if none provided fq send form feed when device is closed full_time use extended time format generate_banner generate banner page for forwarded jobs group Effective Group ID (EGID) for SUID ROOT programs half_close Use shutdown(fd,1) when sending job to remote printer instead of close(fd) hl Header (banner) last, at end of job ignore_requested_user_priority Ignore requested user priority incoming_control_filter incoming job control filter if default (f, l) filter program ipv6 using IPV6 conventions kerberos_keytab kerberos keytab file location kerberos_life kerberos key lifetime kerberos_renew kerberos key renewal time kerberos_forward_principal kerberos remote principle name for forwarding kerberos_server_principal kerberos remote server principle name kerberos_service kerberos default service la enable local printer accounting (if af is set) ld leader string sent on printer open lf error log file for spool queue lk lock the IO device lockfile lpd lock file logger_destination destination for logging information logger_timeout intervals between connection attempts logger_pathname temp file for log information logger_max_size max size in Kbytes of temp file for log information longnumber use long job number when a job is submitted lp printer device name or specification lpd_bounce force lpd to filter job before forwarding lpd_force_poll force lpd to poll idle printers lpd_poll_time interval between lpd printer polls lpd_port lpd listening port lpd_printcap_path lpd printcap path lpr_bounce lpr does filtering as in bounce queue lpr_bsd lpr does filtering as in bounce queue mail_from mail user from user name mail_operator_on_error mail to this operator on error max_accounting_file_size maximum size (in K) of accounting file max_connect_interval maximum time between connection attempts max_log_file_size maximum size (in K) of spool queue log file max_servers_active maximum number of lpd queue servers that can be active max_status_line maximum length of status line max_status_size maximum size (in K) of status file mc maximum copies allowed min_accounting_file_size minimum size (in K) of accounting file min_log_file_size minimum size (in K) of spool queue log file min_status_size minimum size to reduce status file to minfree minimum amount of free space needed ml minimum number of printable characters for printable check ms_time_resolution millisecond time resolution mx maximum job size (1Kb blocks, 0 = unlimited) nb use nonblocking device open network_connect_grace pause between transferring jobs to remote printer nline_after_file N line after file name of banner output filter of_filter_options OF filter options oh Printcap entry valid only on these hosts originate_port originate connections from these ports pass_env clients pass these environment variables to filters perms_path lpd.perms files pl page length (in lines) pr pr program for p format printcap_path printcap file ps printer status file name pw page width (in characters) px page width in pixels (horizontal) py page length in pixels (vertical) queue_lock_file queue lock file name queue_control_file queue control file name queue_status_file queue status file name qq put queue name in control file remote_support operations allowed to remote host report_server_as server name for status reports retry_econnrefused Retry on connect ECONNREFUSED errors retry_nolink Retry device open or connect failures return_short_status return short lpq status when request arrives from specified host reuse_addr set SO_REUSEADDR on outgoing ports reverse_lpq_format reverse lpq format when request arrives from specified host rg clients allow only users in this group access to printer rm remote machine (hostname) (with rp) router routing filter, returns destinations rp remote printer name (with rm) rw open printer for reading and writing safe_chars additional safe characters in control file lines save_on_error save job when an error save_when_done save job when done sb short banner (one line only) sd spool directory pathname send_block_format send block of data, rather than individual files send_data_first send data files first in job transfer send_failure_action failure action to take after send_try attempts failed send_job_rw_timeout print job read/write timeout send_query_rw_timeout status query operation read/write timeout send_try maximum number of times to try sending job sendmail sendmail program server Mark printcap entry for lpd server program only server_tmp_dir server temporary file directory sf suppress form feeds separating data files in job sh suppress header (banner) pages short_status_date short (hh:mm) timestamp format for status short_status_length short lpq status length in lines socket_linger set the SO_LINGER socket option spool_dir_perms spool directory permissions spool_file_perms spool file permissions ss name of queue that server serves (with sv) stalled_time time after which to report active job stalled stop_on_abort stop processing queue on filter abort stty stty commands to set output line characteristics sv names of servers for queue (with ss) syslog_device name of syslog device tc Include indicated printcap entries in current entry tr trailer string to send before closing printer translate_format translate data format in control file unix_socket_path FIFO path for local process connections use_info_cache read and cache information use_shorthost Use short hostname for lpr control and data file names user Effective User ID (EUID) for SUID ROOT programs wait_for_eof Wait for EOF on connection
License &license; Testing and Diagnostic Facilities The &LPRng; code has the ability to run as non-setuid software, and to use the non-default TCP/IP ports for communication. This facility allows a Test Version to be run in parallel with the normal &LPRng; software. To simplify testing and portability issues, a simple test version of the spool queues and jobs has been supplied with the &LPRng; distribution. These queues can be placed in a suitable location (/tmp is common) and the &LPRng; software tested. The test version of the software will use the LPD_CONF environment variable to specify the location of the configuration file. It will read this configuration file on startup and use the values to override the normal defaults. Since a user could maliciously set up their own configuration files with values that could compromise system security, it is strongly recommended that the test version is not made SETUID root. In fact, the &LPRng; code will chatter messages when the LPD_CONF ability is enabled and it is run as root. Compiling the Test Version Edit src/Makefile, and uncomment the indicated line. Then run make to regenerate the distribution. #### ****** TESTING AND SECURITY LOOPHOLE ****************************** # Define GETENV to allow the LPD_CONFIG environment # variable to be used as the name of a configuration file. In non-testing # systems, this is a security loophole. #CF := $(CF) -DGETENV Setting Up The Test Version Spool Queues The &LPRng; TESTSUPPORT directory contains a set of shell scripts and files that need to be installed in the appropriate directory. The following steps are used. First, you need to set up your HOST environment variable to the fully qualified domain name of your host and your USER environment variable to your user name. This is done in order to get values to put into the Test Version configuration files. In the TESTSUPPORT directory, edit the Makefile, and specify the location of the Test Version spool queues. The default location is /tmp; since on most systems these files are deleted or are available to everybody, a more secure location should most likely be used. DO NOT USE THE RAW TESTFILE DIRECTORY. These files need to be copied and placed in another directory. The LPD_CONF environment variable should be set to the location of the installed lpd.conf file. In the TESTSUPPORT directory, run make. This will copy and install the necessary files. Example: CSH: setenv HOST {fully qualified domain name}; setenv USER `whoami` setenv LPD_CONF /tmp/LPD/lpd.conf set path=( /tmp/LPD $path ) unsetenv PRINTER Example: setenv HOST h4.private setenv USER papowell setenv LPD_CONF /tmp/LPD/lpd.conf set path=( /tmp/LPD $path ) unsetenv PRINTER Bourne Shell: HOST={fully qualified domain name}; export HOST; USER='whoami'; export USER LPD_CONF=/tmp/LPD/lpd.conf.$HOST; export LPD_CONF PATH=/tmp/LPD:$PATH; export PATH PRINTER=; export PRINTER Example: HOST=h4.private; export HOST USER=papowell; export USER LPD_CONF=/tmp/LPD/lpd.conf.$HOST; export LPD_CONF PATH=/tmp/LPD:$PATH; export PATH PRINTER=; export PRINTER cd TESTSUPPORT make Running the Test Version Software Set your current directory to the location of the compiled Test Version executables. Execute the various executables using ./cmd, or set . as the first entry in the PATH . If it is not the first entry, then the standard system executables will be used. Run ./checkpc. this will print out the various values for the spool queues in the Test Version setup. If the t1, t2,... spool queues are not displayed, make sure that the LPD_CONF environment variable is set correctly and that you are using the Test Version executable. Run ./checkpc -f. This will fix up the (deliberately introduced) problems in the spool queues. Next, run ./lpd -F in one window, and then run ./lpq -a in another window. This will check that the server is working. You can now amuse yourself by sending jobs, setting up permissions checking, and other chores. When everything appears to be working correctly, you can then remove the Test Version flag from the src/Makefile, recompile, and install the &LPRng; software. &genindex.sgml;
lprng-doc-3.8.Arc2/LPRng-Reference.dsl0000644000175000017500000001456010554776205014363 00000000000000 ]]> ]]> ]> (define %gentext-nav-use-tables% ;; Use tables to build the navigation headers and footers? #t) (define %html-ext% ;; Default extension for HTML output files ".htm") (define %shade-verbatim% ;; Should verbatim environments be shaded? #f) (define %use-id-as-filename% ;; Use ID attributes as name for component HTML files? #t) (define %root-filename% ;; Name for the root HTML document "index") (define html-manifest ;; Write a manifest? #f) (element segmentedlist (make element gi: "TABLE" (process-children))) (element seglistitem (make element gi: "TR" (process-children))) (element seg (make element gi: "TD" attributes: '(("VALIGN" "TOP")) (process-children))) (define ($email-footer$) (empty-sosofo)) (define ($html-body-end$) (if (equal? $email-footer$ (normalize "")) (empty-sosofo) (make sequence (if nochunks (make empty-element gi: "hr") (empty-sosofo)) (make element gi: "p" attributes: (list (list "align" "center")) (make element gi: "small" ($email-footer$)))))) ]]> (define %section-autolabel% #t) (define %may-format-variablelist-as-table% #f) (define (toc-depth nd) (if (string=? (gi nd) (normalize "book")) 7 1)) (define (article-titlepage-recto-elements) (list (normalize "title") (normalize "subtitle") (normalize "corpauthor") (normalize "authorgroup") (normalize "author") (normalize "releaseinfo") (normalize "copyright") (normalize "pubdate") (normalize "revhistory") (normalize "legalnotice") (normalize "abstract"))) (element sgmltag ($mono-seq$ (make sequence (literal "<") (process-children) (literal ">")))) (element command ($mono-seq$)) (element application ($bold-seq$)) (element warning ($admonition$)) (element (warning title) (empty-sosofo)) (element (warning para) ($admonpara$)) (element (warning simpara) ($admonpara$)) (element caution ($admonition$)) (element (caution title) (empty-sosofo)) (element (caution para) ($admonpara$)) (element (caution simpara) ($admonpara$)) (define en-warning-label-title-sep ": ") (define en-caution-label-title-sep ": ") (element hostid ($mono-seq$)) (element username ($mono-seq$)) (element devicename ($mono-seq$)) (element maketarget ($mono-seq$)) (element makevar ($mono-seq$)) (define (qanda-defaultlabel) (normalize "qanda")) lprng-doc-3.8.Arc2/LPRng-Reference.dtd0000644000175000017500000000777710523404244014353 00000000000000 ]]> %orig-docbook; #"> %"> lprng-doc-3.8.Arc2/configure0000755000175000017500000000400510525651247012675 00000000000000#!/bin/sh while [ $# -gt 0 ] ; do case $1 in JADE=*) JADE="${1#JADE=}" ;; JADETEX=*) JADETEX="${1#JADETEX=}" ;; DOCBOOK=*) DOCBOOK="${1#DOCBOOK=}" ;; CATALOGS=*) CATALOGS="${1#CATALOGS=}" ;; --help) echo "./configure: Generate Makefile out of Makefile.in" echo "Syntax: ./configure [options]" echo "supported options are:" echo " JADE=jade to use (default is openjade or jade in your path)" echo " JADETEX=jadetex to use (default is jadetex in your path)" echo " DOCBOOK=path containing a modular/html/docbook.ds" echo " CATALOGS=additional parameters to give to jade" exit 0 ;; *) echo "Unexpected parameter '$1'!" 2>&1 exit 2 ;; esac shift done if [ "${JADE+set}" != "set" ] ; then if which openjade &> /dev/null ; then JADE=openjade elif which jade &> /dev/null ; then JADE=jade else echo "No (open)jade binary in your path." echo "Please add JADE=/path/to/command to your configure call." exit 1 fi fi if [ "${JADETEX+set}" != "set" ] ; then if which jadetex &> /dev/null ; then JADETEX=jadetex else echo "No jadetex binary in your path." echo "Please add JADETEX=/path/to/command to your configure call." exit 1 fi fi if [ "${DOCBOOK+set}" != "set" ] ; then for dir in /usr/share/sgml/docbook/stylesheet/dsssl \ /usr/local/share/sgml/docbook/dsssl ; do if [ -e "$dir/modular/html/docbook.dsl" ] ; then DOCBOOK=$dir fi done if [ "${DOCBOOK+set}" != "set" ] ; then echo "Could not find modular/html/docbook.dsl." echo "Please add DOCBOOK=/path to your configure call." exit 1 fi fi echo "Configuring with:" echo "jade to use : $JADE" echo "additional catalogs: $CATALOGS" echo "docbook.dsl to use : $DOCBOOK/modular/html/docbook.dsl" echo "jadetex to use : $JADETEX" sed \ -e "s,@CATALOGS@,${CATALOGS//,/\\,}," \ -e "s,@DOCBOOK@,${DOCBOOK//,/\\,}," \ -e "s,@CATALOGS@,${CATALOGS//,/\\,}," \ -e "s,@JADE@,${JADE//,/\\,}," \ -e "s,@JADETEX@,${JADETEX//,/\\,}," \ -e "s,@SRCDIR@,.," \ Makefile.in > Makefile lprng-doc-3.8.Arc2/Makefile.in0000644000175000017500000000752310746255772013053 00000000000000########################################################################### # LPRng - An Extended Print Spooler System # # Copyright 1988-1997 Patrick Powell, San Diego, California # papowell@sdsu.edu # See license.txt for conditions of use. # # Heavily modified 2006 by Bernhard R. Link # ########################################################################## # Reference Makefile FMT=LPRng-Reference DOC=LPRng-Reference SRC=@SRCDIR@ JADE=@JADE@ JADETEX=@JADETEX@ VERSION=3.8.Arc2 # if not in your standard sgml search path, something like: # -c /usr/local/share/sgml/catalog \ # -c /usr/local/share/sgml/docbook/dsssl/modular/catalog CATALOGS=@CATALOGS@ #DOCBOOKDSSSL=/usr/local/share/sgml/docbook/dsssl DOCBOOKDSSSL=@DOCBOOK@ all: $(REFERENCED) $(DOC).html $(DOC).pdf HTML.manifest REFERENCED = license.txt LPRng.png LPRngT-L.png INCLUDED = license.enc y2k.txt NEEDED = $(INCLUDED) $(FMT).dsl genindex.sgml DISTFILES = license.txt y2k.txt $(DOC).sgml $(FMT).dsl $(FMT).dtd configure Makefile.in LPRng.png LPRngT-L.png LISA98.ppt updateheader collateindex.pl dict texmf.cnf distdir = lprng-doc-$(VERSION) MOSTLYCLEANFILES = *.errs HTML.* \ *.tex *.log *.aux *.toc *.dvi *.ps \ genindex.sgml license.enc CLEANFILES = *.htm *.html *.pdf license.enc: license.txt echo "$@ cat license.txt >>$@ echo "]]>" >>$@ genindex.sgml: $(DOC).sgml $(INCLUDED) -rm -f genindex.sgml perl collateindex.pl -t "Index" -i indexes -N -o genindex.sgml $(JADE) -t sgml -V html-index -V nochunks \ $(CATALOGS) -d $(DOCBOOKDSSSL)/modular/html/docbook.dsl \ $(DOC).sgml >/dev/null perl collateindex.pl -t "Index" -i indexes -o genindex.sgml HTML.index $(DOC).html: $(DOC).sgml $(NEEDED) updateheader $(JADE) -i output.html -V nochunks -t sgml \ $(CATALOGS) -d $(FMT).dsl $(DOC).sgml > $@ -tidy -i -m -f /dev/null ${TIDYFLAGS} $@ perl ./updateheader $@ check: $(INCLUDED) $(DOC).sgml $(FMT).dtd nsgmls -s $(CATALOGS) $(DOC).sgml $(DOC).tex: $(DOC).sgml $(NEEDED) $(JADE) -Vtex-backend -ioutput.print -t tex -o $@ \ $(CATALOGS) -d $(FMT).dsl $(DOC).sgml $(DOC).dvi: $(DOC).tex @echo "==> TeX pass 1/3" -$(JADETEX) $(DOC).tex 2>&1 >/dev/null @echo "==> TeX pass 2/3" -$(JADETEX) $(DOC).tex 2>&1 >/dev/null @echo "==> TeX pass 3/3" $(JADETEX) $(DOC).tex $(DOC).ps: $(DOC).dvi dvips -q -t letter -o $(DOC).ps $(DOC).dvi $(DOC).pdf: $(DOC).ps ps2pdf -dPDFSETTINGS=/default \ -dEmbedAllFonts=true \ -dAntiAliasColorImages=true \ -dAntiAliasGrayImages=true \ -dAntiAliasMonoImages=true \ -dCompatibilityLevel=1.2 \ $(DOC).ps $(DOC).pdf .PHONY: all mostlyclean distclean clean install update correct check-manifest check correct update: perl ./updatesgmlmirrors ../MIRRORS *.sgml perl ./updateversion *.sgml ${MAKE} all mostlyclean: -rm -f $(MOSTLYCLEANFILES) clean: mostlyclean -rm -f $(CLEANFILES) distclean: clean -rm Makefile correct: dict correct -d dict *.sgml HTML.manifest: $(DOC).sgml $(NEEDED) updateheader $(JADE) -i output.html -V html-manifest -t sgml \ $(CATALOGS) -d $(FMT).dsl $(DOC).sgml -xargs tidy -i -m -f /dev/null ${TIDYFLAGS} < HTML.manifest perl ./updateheader index.htm -if [ -e manifest ] ; then diff manifest HTML.manifest ; fi install: $(REFERENCED) HTML.manifest $(DOC).html $(DOC).pdf install -d $(DESTDIR)$(pkgdatadir) install -d $(DESTDIR)$(pkgdatadir)/$(DOC)-Multipart install -m 0644 $(REFERENCED) $(DESTDIR)$(pkgdatadir)/$(DOC)-Multipart/ xargs -i{} install -m 0644 {} $(DESTDIR)$(pkgdatadir)/$(DOC)-Multipart/ < HTML.manifest install -m 0644 $(REFERENCED) $(DESTDIR)$(pkgdatadir)/ install -m 0644 $(DOC).html $(DOC).pdf $(DESTDIR)$(pkgdatadir)/ distdir: $(DISTFILES) test ! -d "$(distdir)" || rm -rf "$(distdir)" mkdir -- "$(distdir)" cp -p $(DISTFILES) "$(distdir)/" dist: distdir tar cohf - "$(distdir)" | gzip --best -c > "$(distdir).tar.gz" test ! -d "$(distdir)" || rm -rf "$(distdir)" lprng-doc-3.8.Arc2/LPRng.png0000644000175000017500000011157310523404243012455 00000000000000PNG  IHDRPLTE    !!' #!!!)5 50 $ # )!%"!)$#+(&,*)*'&1-+41.;86864,0.!&>@=;@DD U G A<;V.)nv o"t-)Q!G:CC@>JGEQMKTPN]ZYXUSOQPa]\pKIa_`ca^ifeqmlspnzuu~{yvtsqop50'*+(5*84,+*&-13,2+3'75.2_vvv;w/'Iyʁ9xRș~2D=x#']}hH0!P'~}38W@ Ν#|7{W[,[vҥs"/oݚ_[fM꼼܅ .]z͛nΜ>'C90h"#^-;vIb5Q 8'v:vC3O}ءܖǗ-.Ξ?M[V^^VZZToVRhoذq .j-..q=\]:/we{MGϜ zQD.!…aWj>eôL/KRJAQ$ #_gxtڜU+UeGRlZ [j]^al1ÍiswV*v?$\NyGuUuޚwS]~$v\vaB&O/ ^]̿upěF[ZֽHӢ=s{ooݼi10ݪ/  JZlml(6X v6[|f8py9[ށ6-S(+b՛:~/ vDb91:7VEH ڬ욪*Rcs[bqmB E6%E@N@ILGm v{XVK (JJ-z7ᨭZ6:?ߴۋKĄK3۷!1hŻ>xGT:bGPkfon6EVgsEr+tM8ݍc/n2> S=)ίrr  (@ JZ=aׄwl( %n(»xhـ)Pn7ہa8 _Hs``7f媟~4 α>ɠN(_w>MK jsLcTS^QjY†łhJꋐlӦL}.;MԹJg :b 2}( r4+/7WZK+YYSgko]p|.}g{X 3Ln 2[ vUD$R~N'(S<2b져[PQƎ@,ɑ ˇaIEFXnyOӵQVf#;t\@WĿupӰ'5]].4"kFu/L%%n"F0YAXQVshWú 2஌ѷ (Nj). pmE7W"u]5YKz.b5PO(Q_F;a0"J}3dWU׺^erAMb28|ӊ! *`0Vjli]o! *Ȁ=FDPN '4}"천@{\X As:3"цS'q!EO=|_o83p =5 e_o?^(x6+,+Yd4" N 87d%Ts@Dnm ?D"%V/~Svj6 fd#B@P(64p:c;U @fqd,Qbp6BdRHDY%qKBIJLz|$H HɥTxH`Q:27bH ө0vٳ \[ b( #`Nd%ukkV[]pѢE6oٲuM{}yq'ހ,_=ݷpႂ܅YYyyUe@*PM0/7h,rWD/2KvB>὇|`*o)/s9jcvL ۯobZDc( 5UY -}|˛(CypN<""<E#eɏY ߻O9s?y{Zg)ˁًa++0N;*#VUZ ??/ wґ|ء-ߗ_+j-bJfY8`[!!6毮)t,cܑG~*1~\QH gKpC{~҂f誱ImE7 R{`Pu4ڀyd*hsV[XxA_6anб INL-ݴC aN)ъSQq%4˟WC]K!(ɕ !&>?oႵkK E6tZl.t:%PW&V=NoNSN.! [Iԁ\v{6 łd5Nb9wb*ņVZ^^[`̽9%A aBDemLT/cQ#F/6@JEp~|on~ T8{XqÇr}W%)vu{ @"+HBnݬm B`y|kl#WnGJ\k,/sX[Fjܠ  KBUJaǻre^A}] p)`bPCΝ?6߿hAX\\ٸ|yek.WBw't x1jfQ8׹o|۲© T_vTǀb[[U79nmi3tK}YVS[JdKyaZ-'Pm^fm\t~% pM~?}dc?^jF8fX uEńb %:+wo|ptooX7[D񒯿ț/*X][S*0d,7o,,\^xIu1MQ7A4~w7/ʪ}33je $KL^7\klqɓK^0*h{/(X[-llhÄ+`j^rc-8k>_:J9.QD?nZ4Mžqan؅66*ϻW0F:yD^Oau4t\G<L'9$GN_6kwCS~yGO_$m3GtU65RR*`uw(ҳ##L0oYTPҋ@X𘋋K7zy6PTW&vCMV<@9Q 2d R0(n͍)):Kڏ<(.Kmv`ZvE|Wn8OF,CM(,R˺lyx-aG—7F0bP>}5nt5#Ҷ ̻-tVwEB( xMFpos ڕ vQ?~薔*lN&s0^KsWno=`cwJR}q:KWlB|/p+]~ Ste-.n%xHJűzo|hxt>ߺ(a^n]m*[1xŨNr(X?ۺpzM9X`5 n|uO \Il]rͽ.u 5ˎ), -?D|ѬUJ⺆Jۃ[0I*|I#}sl7[HWrg]Cd9᨝҉X:FAVn.VZZY)xjYS>LCWd'AGH![.#\CE7뗮r1qvx~悕^OR6Xʪߏ,F̈kL"ͥ^|EJscwӠoy\1DGxʍ%^\)*t֔}ӽb\p1EuZo%GAeWnIr/KK0nĶqCMpf/زtqKk}Yj޹g ҼZ_XcR2~Z.8{JPXT_W/)js7ӦcBP>JbMS=Eob.*KPC4vs"I64a ~ۻ[S.U'Mk^<$D7=@!nTv.c+,+ l5y@@CR/|4)}4=ݏ/.X:#;ЭRT%cL{e٬E6C6>d[7>nWC2B# d)xV4TV{{t^ @כ=*ݮR5x]܋.=Ooq70x$,9܁{W9meO> K;xqǎ^&+B?wI1M Cb奶ƆBPj | V~q 7|w8@m"/ <3)i&4rV UV8 :DW :0525)2- __هp ̌cGișalzf>|ҀAAC(n\:5 "{1*oQMtW瀆/+'K = @CD_k34$,2ɜ͟Lr;>mLKcZr\PI. R)slNiO vg[?ܞiϦOsD9cMF5{GY7x1,Z#UϜn;5p,t#m^c#Yu?sӍ~UP?4QQi]^axW=|яzk?7h4d!wEp##.P«IPkﰼYވؤ^ qjSqT#@0S6a,a,r Å4r4ߐP;qG.ǿJ/#V +̂{}uAAIԑ8zvuˉ(Lh'rKLuVɑ>%Z)3%O:K{% *oW:>J G{vdLrc&"#R_ˮY\\Q ܢhht{ΠU ~)7yN[C]ovvS!L9FC(RKh6];H̀ҊvII/1@('1FeGϳz5rH}>#`N;Hs5[F/pkWrJyzu's3]7xrk.XR 5X/]{Kch[|lp)H)O eж*Zؘdgũ>.yT#auRt\L 3=Bޛ5%A⿺̜RO飏ʹhl{rpT{Πp}^wi|c_g**arNHi(2}v"'ĮɌrÆb$#|JިRFYY8 'vPw8o·,'*1M}PM\  '`By9et J ,eJf. fkT@y*-U'8~WݱW$fLf+LjF>=>E塚@UK9A_𽅎+;7k3G袛QärtZAɽ]\!;-(lVQ{LU 2isYBo;Z7B;aHۧ SwdFuݼ+)#~?ll2MUh'J%n߮&|wȶ7Lwr2~X԰UPbv(ed_:1ݨ"^UAxsZc#k-J!mݻ5N8ǂgS72Z8x&W qEĦ=s.2n.Mm1m#-ʨoOp E%6fmE .Q R-Ls/ʗ*7f=TSTjYN21}%I ՜(kZ;%Kko{y9[&u8bQ)8$5R[RN ˰ VYn4ʅ@)SI1^*dbވ2RVMfh钐ԖAK!<:51W镡k@cScҩF6HUSNR)] Q/Z MY* ]av^74abƟ)skS\2L\a.VVAՇXXn*a;=7T¥m?-1I¶jR~NY:;m27-J2{]QQYwm١Ïh맄Ha#ܗ8=[fȃAoχ~`L=];. STSwd­beAL-T@I݁v_'uNpRZbK7I|Qƅd0J9*[Y]0ACu~0B&|ԍpC¿=qGjE|2* (9y$$IY*:Q]Yp>A$t$IJu9*S`GVeڬv]7d2LZkFϧ \ep283A71?CF~-L$^ƚx wUToSRT}IaxY3K/V2*UfU$nB9χdoE`l瑺,644Z^ܨ??Gma?(抽sYDDN;( @Afw/?Z) ]ԊE"X:pL;i!\vbXOW-\?Qu%9w$T͆ ߹-Fn刷 ⻝QH\ݙCOklb_ zPTFl(a:,! gN\Bcd?E@TkU3iqq&Ԯp[nܮh7Au ʦDJp2er$o7Uv〝gLe7U5}~RςmBR"X[pkim,/uykA?0AIr?tiw|J)݊f2t;(Deg+ԥ-cRRdC,H&TzԪ+\@o ,N1;s1cT%Mvq>s].-*4f$f bVj8{~S'{VEW jgOdvn͜_z,k U?SڢF/R W ֦Nf3fP<nH~'%wfNK32-24¤).i DovžeA( 7L>숓wdU-JtAqu5DI339Xz%&lfACLVx"k_ayUAIvrNb}F ̣HC"PE;sܜ<99yes??Y5:2fC{9 Q! R3c(%-'jJxY q>nJpXhl[<\KK+[8́9h2a VE ϧKzѿ8iO 況W/ܪƵB3fuXK{ǻt-\ M=$a;|PårKGfSƛ&^?qnbDM԰ <ܤR&‡_8ҧZCM،~F.TU#E~{ҌF&EI}.U7#9+k&#YTF,e{(yKѤZ"I;8Ucy-\92L贅^\al8)u\CRp5T?UM$_5DlnWZpIVsnfNu>.) ިRREnF3!&:DBMT'1HM yy!\O3Q+xJi1ST0e-b"a XjLR͕1 xZM>tNL W8dj{f8=ɍCpKA@D W> 6>"iQN&Xd6UT(GO KY=c7Ludz#s#?Im2:ǸEce)f0I(dʨ1Njh iz-MSf@늑 )* ig'ΣmI7a ,iyMiHwKW#ޘ<]l1g>kZ'x'gԐބxzD zIRoB!gt!x9!I ʹ1kF??XlFh7)x7v('!Qn:gy; ܔq0-҈/m},Yp)mԳp8 K7qj[zH,WuX2!@B+! `b2%IdkQD݌B' su` P>Fv_c$h1}ՖqmkR )'7GM03e'欃+&*ߟ^N%kOcEL$Aͧ:+OԚ9?/+] piSfD-=ꆊOb(O#b4>C?'Fe7bj~E[F0w>^Ipfc.)* O%1aAUK!nvW-DkNI2M3drF*!f1U`%ރ*7% ϳ2yFA/>O]1!8 \LWn;ҸA2 6r46Dx9fb3s3+ƿWS׳?Q}6bŘ݄2 ԍr&G,4+&)məV ;yfٌ>&I1z;xE8v4J#m&[@TrUJ aF^91(̍CI=2YL~1xr^ZK`10~ĎJU#p#G٥A H]rT6i"AT3i􏢶ޑڌհ)đVj[g4)ڔ+JnI ~; )vHrK#y2#7䦷 _> ذC f;HbSt=HNf%82v͗K,ttB  2x-̫x׀$=r(v7e)0An2#CӰM]vVI0TkwkX5]G:*KgH"yXCJ? }of͌׃ŭ?v0;uD@qћ74eRv,"%B7#V ;OJxl{prZkg1WFleHϜ3S23A (N_ s]ʁ< Yü7dz3'P4 403ܝHp`Iޫ^I$4K cgL9D@dvYs= M%,sd݄B#mn~d>HZsT5D iLZx'+  ̑IR2BC*˫ 4iƺa {~aIHI~fcHSArBj+gW"MdUmNleӀ2 `+ͰKMew0 G#@USVRzNTFIU;ixΔ\ͦg.ݮpz`xYuSMx^m 4*Ufon둧p8@&R/MaN\sjKu:e{Ĉnil:beJ@<bJY({ĸvs^C~eS^E3"AO2@s6-#3sYۛ_nG-gNi-EOJ3e7z' V:EU64F +XaEoE;(h!G0L@j^_ˎ^^"~S3FMKn=g<#cg.ϩx[;FW$b5/ \32zbӎYNQZ OjxKbs#I ?'nt$JeWAt$v1;o52IG0))f5M8C G}Tih-r-/ެ2Swsf z9e5"M N)scQ)ٝ",lq;~2d^۵n(Y?t;P**3͉;/ݘNK3ArypSf[AҊn#(W1Dvo۵j0YbcgG^E{D^nڒEVFg: JG6vO P=ˬk&h1> ådf FRLsբ_l~I$ލKpFH= ?'oQUÖXKQTН*(@nȨ;"Q;'dϊ$r 6RBfCf?컨0E6F#%:ϜJM yyrA6s;%ߣVq?y71"d()6,'c2s7SCQt_ly.aGxlv:(DkF f-jS*Ҏ_()3rch[3gZUW1]r G@~) ́eŽa ɂ&̥+ْ~!Z J=Au'=d&zՃjrpUv7a #5Ʉix8 'bxg!ᦽYH,ph F|,JѐG%%9sɟ :`T9U. R꣚uHD~KKi'R>^f A q$dzlw馶4hH4 g3XxROQf(K Fۇe6ڽh;&\τ>2fv+{KƟ=AG6dfN;/1$U9qPIv3`b$3kWv7VQW|9xp:ph2k 4פe({&3a!w75D:M/x(8%jQӈj \^${ԭTi)y|lop>~FC*Jf1Dq]CTH 5,^E|*;YvfP h ,11nɳQM}9y8$A\Tq}qڴv}hC4ޭǔ;h.I3wS/jL%R3Hxha 'UcI6ǾxY6vf9O5ݜ|j+=C@qQ9#5\Yj#RM\-`fg z )lFf*cf <۱!)|2hꈢyssغ)2݋2QD,,#p ۹]NHʒM27xjh\T Iۍj*MSh'j-M>N\GG_= ݭU1]W\y:y-P%mvw[x6֠6'N@M4u2#gT);4%$Ӷ NwθOٛ3jndx6:g C"1MHo̬nIK37*.uػڦ1~K`nʧAYB 6j 줏_~8hd=(G;$G.z.]KamZQt54788:ihs;sj8WRo0"wJK sge''&jtIA1x9n"_I2;omDt4%+4 jlV?P%i$1"&Jw/x7*q(Jf*mDcP'<(D ;&o͛&̪Uz_M0fyA9#d=Ч3Zw 4o jc陼щ-li,v'ՈYD9;H6bVrZSzBvHB+H"ǻOYHW[v1MIo :wt KI^wy XEj+U7+1V\iP$8uWn F b7\3Wn?ix񭄵ۡ`bX4ӾQ_ޓ^Լs+N͜(vfIriM&ROB 3hHxEYA({taŜX u32#<(x"yQy,Ky=«[d%=VaTmR0glZ%)F i4; 9f1(Ϝ5(nIa_s\ٍ>a"7ܕz]:ĚaÌLTwNs~gO+ wI8,->ǢV9qvRUCÜt;6^zYۮ1ƦHXU-(tt1I)WPtbyWBlbzkT.A s 4>4ecs{hSL> 9K2~8W%ђĻI-v O=& p;0!!¨tX:ei;=#WkKW< 3aB$?ȌS1/>=ЋglNVўS9 i?&YO o6`ܗ '|^P+cX RYC)Aͱ&u9vcr{d5@ݦ=Z(HR3Qt#)ЁQHN%n%w+T A$cKmZ$oXI$c`&<,Ƥ|ϦƤg a0ŅQ**~Qϱ>oP9g֘GՃjL}y5*këkdxsiQEBou KΘMxmsfdxFdzq&]R|ZK:kw^1dd^GiAw6Sw)ӰwN%Ï$#)zR;@kbnGR1W:Z5QqN\ Vn}=[ 3*Ũѓhx%sܔmƒ9ͥvf1\?\JX53Bd3 X51Aȉ'BRvʋё:魒#fI$瘸Mf~tխ]HorHhf`͐tW|2V)-'mC?nꎯ ۔6>MKI ,;qAg3Ӂu2zئR.G#]pLh*Uk^f4̉z K TwL|GV6G噣 &LFc9R<<lj֦84,lHt ?jn s} vg[ڞ:DW~g1Wp =ӽY 1s/ }-s8DfOgN\5OKc3I~!r[Evr!,9H]8ypFħLQ}|Ma5bsdN=yg02$ywYD*u,T>]V'O= m>xw}mS966de9Z֘3É1X?01#)ڦUɳS46'sF#9"=78 JͥػLyZ>9 թ SoaS9d>dzyN9elj;Z.5U17l:&5j |iWMAwcҾV/S kECAɛsdQ5zI|䓆gYKQPf>m@Ya}ycpJ>d#z2Ap_ &=[&bhEߡݩ\BF$a&PHi,`L 7%yhx\uKU4:>/mP_V_F[.C <%'&?d3!St*w{Vf'I$j)F]Ee(Y+i5Ez*G(G9 *JhoUMR ѯ*૧'q ?18Y~"u (z']nϚ&i~}#DEbC>Q+?3XCcQbO#P8R=)y)z>6qa EmY%("Eu=Eu8^#;A+g 䍪ZT)&% <8ƼUX&%UI~„! p)(SU& RP`#-$ _Rja9>ԔdMMZ 3Fn W.>r19ZxE" v8 537zJ Z.3,< 79) g= '9;Qy[ɧ>ba5YQFMy|3ڑkz,oJInЧ #AHiofBKsVrݍL&`Y1k<֛̥uŨ;!߰9W7BJ[K/m߱zK/57`{-?_qشetoO7} -Yc5vP?sTҚc41 iL.Y}K `V4~:(mʼnL_cOgpWzQ$jۮ.O2tHRB2Rs|(֫ݢewR_Fl-eؤgɷJjVs>C5wG_ x%S4aD7 T!@3T*\wZZIFI<3&ECjYMl$L~u3eo36cլ|Ltԥ/fZv7?(J~x+/ S)Fq 5/HcM-X>&-:P 9.u*ɳe l K FҀ?r!HEKH $|Id.݊Y8dE1BvHII/VWrEt] {sWO'$>ǚ6Ho_1MM_vcG~BfσQ R­C*J("g"^pژĻIͱm$ڌ}U*#;$ d:>GҲą".!y"yYxs9HV85K7jhB*,2{S/,%[CzRG68P%!wD9nGCH x[k'׏_G̤9&V2T/DHBto)뗔SOy WSni'6 O`aԩ-84):M/4+;_")k/h_8Kv_Eui!-v/lBsfZj gVigfjƶp'hK2ҍR$yTczf"kr ٙLj]2HRkp\>L}{ӎ f8m[:Ci,s~4r TA'83U IJ̃1!E Hو ^6#y-;sWk+xDë?⸴O96nԖ򺩙,oL|[,ǦS,y2vB"3f8G&MV_dzi|vL(h~iw 361VM)ŝӮ&iOv%>:[zX-iu`27(!v_ӤךMT򟾞>~i7/eh_#[wm8c%*Xn@\lY Ft#IōyC%_`(_d uV Y`ug#wkcٻ488PԬVnw'!F滢΃PA<;%CS:ia}uZ3U%M)nQoUkStd>~v[ZHb(4aS)M45R9`9vΎZu82)Je?^MTM8>0>iH<>pUƦlOw$IGw io~3af f4=`Y¼ ZU4KVt6{I“lFkCVc)[) Leٗ̒B uĿ7r j|אt:~RϠ.Yjcegp%|ASO~ឩa 㦾3^IM|> 3"}VV%4^40N򣻶a:̯mIK?MX4ct%]@&icϦz4LNɕH=(,#r;5f (r= y6Q3U07-I37-fEUuT>dvZI);[vv@R9:^gxYx R38DCWīJ0n"np'NEyuv4~Ȼ,_n½_ѵLLQZ5j9ע7- zpq$Wml *W *t#LCFr}&Mk9i:01qWƨ3V<>޴}d:4]fK}²;8Xn갧-a-1gdè_& 3{|$Wvl ǝ!!_5 ɃjR3x9R D ,כ;#3c2RiOqʆ]n2QHKl!Ҽ"8 X47j xv LP/2I2̔ h!6USfx@Ӟr+L|YeU6-i~&jQfk-̹0 =y6OCLj#[|ޯv7q;hl @P' M8&tZ2`ri; jx i*|IE[ E9tZqFLݲ%6V_s=NVyJ޼\BW~ߒy$lҔ&폀>i6~~ ]G_M\4p2}nO=P4MKҲj x2˥:fc/X&3jq4U%~Cra&1fr `nB>߅;`\7LBq7' :,C<AߏI\@n5?@M 2oaS[gW \ƷxuN"eHSzcίq}juZ_.?nSR4g%YwS(jQuMo`Ep,mV`8لCBMvtJe8daKϐ7t7۴DZE|^槅h?0KC %AR7&Ic?Wi:Lx-c?Qۈxa2QTq6xnFwCwTᝮ[0ՊMԼ:--oy&\,Lj.c{ SE:ɢ+uh'ighʭj G8%%C[eyݶ%+sJT d-XZFS&x#tԃM<M|3wC|mMMi68sho_9hF*,@6Ndu]51w`g.I%N㒹;K dPJPk$u#Ŷ(nKs*'Mo1ܵyjdB6_'ioݤbVZN%|I NsY}?z^Yt/;qե |Mz*Y-э3Rod5%uXzG.T pwoEex۠SKhIVcohN5ne9铫2H)[%Dqi,N2| 5.Nvi As6`7dLtxm(Bs%'ܸOhtFŦhnu^N'ZHSp&$c>?#eL3*RKZR }{y m VvxN"=< 9Vrǎ^| w/nQn{lL3EztLd8qڦZn.2͑~S0VSQI4X^`ܵS"6ۙ鯡ڛt,7+7$nٴ԰ 39,8k,{A 2gFcwe!Mӛ}>N:V-\&ɦ@P-J:}FVc8VbY+76]F'K֨CA%mb`PRFRZ_~`zAEy&*c?LX5/bҞOz-HzXr+hIvKOdlR+V-v]Z"MQ̏`c?;ɩZy!(3WlTVqr򙶽);NѪ!$ZzN#W sqVXmVA{hy9g`:cug+A1O%rjsW;**ڵ{J=`k瞜u"K$yI=jIO֪m'~vL4vb@Frlڵ^Ɗ 6%QI:-F3.ó[`s' H,ܒ_c扸#l45t ƽ0v4B2{O߼0kdCClUD1q pe\.pbK^iQMޖ.G w(q#M& 3x L;&(|@)4)sxJl>Kit_5Y{׹mj(pҭW|F6ue\":#15jޠ'&&QԄf\"rk$_sϚrWQK+jr'܈C0̐ym`$qzQzs_qX G/tu[Zq}i;b/]]h-~tN95F;RI) Yȕp<0y`*GYYyŊrGG>އ-Ut\. lZ8hh)]&6JЫ;h2 }|ﲅ5v{,BC@U9S3.-[ͻwω^ZVN.UvMb|FP:?p<(+kzE7ZܭhfP؞l*xW/|n++[^iY{*oн#և 7>}4 ׵ei->~"mkP;е#Q%Zy'p7oTTEW=cY9lbֺE\/ρ>y!z^CvN\.87/]]xJ>TgY̱=uMǞ9܁޽ Ἵڜ_90ؿA"\R~h黻f\ymhXƺ"oUvWZ/|Bn56V8W.ryYr*´.&/^z m^OJ OmVĉ \;Hj.h+v9}+k"ÃS!G{Ye2EK"{ tS fSqE.Ic vGƧ{O޿w{V֮[  m?Fw7mkh+,vbe?x'V{rZ$XJ.3~[`#4^ΜI;5UϬ{%bq_ԁa-S_N"HU dD)O(,T^w~cWUUYY`Xuj`u[`++=E[DŽ`x'1c9p~/I}G2oY&g'; J{+[Uu u2B]f/X3}b[R}{-^fd஀~,7D|xH s8~$_~) oʨz͟Wvm+(DOGmCN8qޕOw4]W}{1ru`Ӣ7k}i3h6{;6<ㄱĪQ{^,ְؑ[wlӋ`h Vy "nokp{]Niso/*9Kx O) C?tNmIaubm.rj\^QQ>~:p[Rx5r"a˸7) Gz82 "Z 7;~76:ׯ~K# ;::~P2m@sォ urP^V3^{Gz /ׄ&Qfн0P[7ݓ=? UohW@ZlD\ ^uMH_ML`}۔SpXl EsYYNI K~m]1TƖ-^eA/ G4p^?dK[x{GO%*_ =6\_ 5ָo/ͪuX0Ì7*X+-+6׸VTlz~o,iE +G)I$B~I"= xoߩKɰc!fͅ>+ŐmqWT ^,kі}_7obLGj'H~Z5+ʗ ŰhҀFh]]C#85s 6ms1& {o rVu!ě X,.ȑ79 L rb+lW{s. :-k!rwur;{~ Wu86FH:]x0 ȡhMnw{L/r)գR *\1+9.5~^u 6NoS Q~7TvP㖛+Dʫ =o{>-.kb^ɺ4 H"(]8ooz|}ݷhq~ >,\6EV̏`a2]X!8mٕf&>:g_7MdGt|w_]֢FkBu88#h8z[C=轶"[ >ުjsrss ?'~gx=o{sԳnݺM>RPP[fꬪڕ5e R79[^6;a@uuF`6ύlxؾk㬩^xs^tm+7aσsfS,v02U]]pl JT8eeUeYYy8V<52:5`H*v;‹Ï0l`#1 ۊ@;jV,G1I1 ]WnuxcY>cϨEc7lG~[=AaP.,,wlr$ .xgQ{ܞ n;y,Č~.3!F S6B)E֢ nAx2\6XZ Y7,Tz Vge-3D7I-BbڔW D_{7ߛށIfUDs5<;E Vk-Ѡ4'+ Tx R0Y҃m! d8GPXF$N5!xM9jN =?Й.P QLT2y578rb2  )Q1sQ2Aw؜_-nNȴAT1|ȎJ 9+l`yrY끛8VV-]S{?TWoh*lkꯎWMi ;:>dSGԣ9kse$wkMaGato'BO'<h-aˊÊ!b tz$h/szkW)Xy;;xQ` G|MKѱ^$#;OnY47`zQ0{vXÍŠx݄ Ċ.zSv3娩qYw}o޺gDZ _ʦC\ ֖U6b (=>}z`muuZ\;X"M 1HR-v5ecwڛ.O ,ߔW_?CG>Gvqٺ܂y׀6 7'8vx\ :| >WKhUYUG':{1-^I c7MM;u̱/{<҂յՈ^-H\Kɲ<6a2;f"zkj?Z[pM~?8p:*&R,MG5wXݧhu_ԙcG>ۿ{y'₂yyU+Up濦 'N>8'*IBĵq Mҿ?!?HO?;qر#G:o{;S=r xѣΜv@JhmHk:7Rj`b;$IENDB`lprng-doc-3.8.Arc2/LPRngT-L.png0000644000175000017500000015616110523404241012772 00000000000000PNG  IHDROf{PLTE       !&7 1 " # )"4"%"!)$#-*)+(&1,+31-<:98534//%!7B:G A=HFEQMLRPNXVTROPa][b`^igfqmlrqnxvuqoo3/(4*85,+*&2+3*75+."[2J ]ߝq7}=d0@|a)Ep,}#ٳ{;G= 'Nx-<~q̞=}ܘٳ͆ /p x77ghSpgoGeTBüR'YEe4KT7)I==_W M/Wx߃wD(qGtz+z~D?y#Z(}7YWHP3ȗj'@ϟ;M-.?ՐOum*s*|+erp:WlɓFt$,uy|Ʒ Fu{zNl}ǎ)bRm +Z  "Nt_܄p#{2X]¿*TkZ"23\Šفً jѪr%.Z{Y3goɞSO "Iwh\D%z{?mܘ0M5z%ӦN*]|E$]3H&WPƀq3@֪re:\|1xKإ#A8|n0P<-5j5O]nez~Buȑ}Ə->]*+s:N.?uTi n UA))uQq)A֔;j9tjEs@##.FFb]k0+#֖xJY*rUL7g᫛uaSHI]P߭'W_K7U&WV9OD6տ@+E',CH4@QBr,VJ:`R*-; MwոA[qL5Ηձ ~:Ʒtuݽ>brMUu) ;qJF (Pap28];G5ӈ2g !&gn;J3P(&˪ٺ.r@s ";;jjꐣf#1YV*wr|kzƢfϜᚊ jN5Hń3uCRdG|aبEDž9T4r/K]Q~ѽ `rz0g2k:5A0V2;_ٸ+PLظ[a5ʲ2+vt[YX8~\*VI ]ԎrᬞIuH卑XZ\D' pL-%BY$ c(."g8cZ#<BG)pcWxT.[PRpV:UUrF;]jES*:J!ˎKn|IJ`Y:͇*1bJ&Ju!R ȫ:5+΂ 3Y1ѵrx"EEcSXx7{ܘ" 3f?v|c;vܸo’E4bLDtc~-ȅU* aa@pʌ_Wo~ޣĴ#$Zev7OdF6 VjeC;獇&ń}Ys8^cQ\ ZۉFGյ3+S>n-r~] xhK/x7|k_K/ʢXx[n-cϜ9PsC8]J]e tXM"[au4隁 }r4R:)+6IWgjqOؑ'=f|Ί!^Bss*)Susf,.-)VZzOqII1U++'WVΚ5gָ '.|p+;wwȑ]]]19bQ b'?N`92ٸ^zi'~K nj)UU1(es8ƹs͝Zz^u e+HO@ꪪP;}.tZO^v_!JApE:kfMi&$TW=џEiRsGP1{l'_z͍{ɓ=7a\@YfVl(]Ԣǘ`(X3/[>K>bh;A[ej{Na{׿,Z8VĞY>b'Pr**tCww=eH®< ZЩ~Y`]Z`=HҽMS^^U`8Ly趫e1]" o宻~hR`,2&vwJkD(ȴVf@D+!`Dݭt{7Ң2aJqa=9iSr]?4GMŸmV+N)N8hWbXf*TS3z$F#"0SU3g)EiQDшݬ$°'hȅ(2UTSI:* sdZ4A\v,xPc`numݓ3qQ~iO ,猝Pҟ.RR; L:PW{f}2׀2Հ3|w=;a)Gڦ$`5 @/~l ŘK1Yb gxǷ>~\QE 5ێ `鴩%N'ZcK0ōЦ]U{lW'ΙU1)tO:u*zkkM¸s hMŮ &U(3fLo>;y䩓==@($1}h?GWƚR-E>d;ӍJfa1gWL&Ѫ:%ӦP+&(sAQ4痋6 fSm߀|R²ӵ1t`veS`*Puu&0v8k~2{\wKoe1Lɒd(X^(AlEIɣ>M]arGez=]\\FA޺KN=q8öqٵw o&߈eҠ,,91ci혿@{Ǎۯ^qMR?lM1q4$=- WL&xV} ̺ՕS&;u,("eըߖN`+&,|HBaY/sԥ7x.+R{w/{+'Հ]rJH}Y1 J0U4kָM*UUwW^cŘa$B9/U|5Yc簦"jY~[ 'yںN$OY SkZɴ:u˃!/G/%7H}ylɎbRLT:B8T!{*SVSQ1kN} hohR4~KӜ$\ȇc(+єݰJܡS+ apk1~ƢS5VeiI o}X0qoT XM5XX:*u;$Ҳɓ'Owm?~靽Ǐ4|ᨎ^~EWcLFw}<7 Vb}hY3PDWeń{k/S$|]]߾q)J[t~ S:/-C7|*E/{<(gՔ o[}]J<N6Mqy9b$tgd^58pɮ7.xY3*t*ݚ:$ 0̚0q=ZoLȺ<.Sw0%Dsd䦉sfVVUVMak wi͟_W̷7?(xߤ7iUɶٷq]fGcaM[`.Θ>D"-k'bj+<^8V\5 "WSj*++l:-I(# LYeK2_0*G?#xˠ¸D ^9yrKV0c-KӰ/LgJL/,QL?[Pb~ӫ*' Hxt~Y[4}`d[?.8?Jy =v(-.-vOB7ԂPtM{dS(%\|I18 0ϟTC{m_O4!KC__g}کfDM7{zmhb|W=_pTՎ$HZ0 (r.`nFcͤyL?Ok4OX3Atuy}ᄊ2*+ť%EE3*//7?:s s'TU;q&}w,G4MVf`|Lk7.Pq)Y3cҳ_Q]S2 A}<[g(]@pT2WiS̱=]]߁8#(V֔S_~_ ySKJKԕOW"a)B!.6= \5`8Op/+70d6X={WW'+/a^7lt[>DHgZ;8.8?i)vdC[QŤ Ίc g=?Jy f;d9a횩ĺw4qn'(]tuD˓F/3T\yST`OZGM r^ԭAّ!|GAiaU=ݵʜSbYIiqќK+8ww~ USJ5h2L5>;M E_`R{N TWk% &E{˲!`Q-;2;%(mcʑ#ufc5c_ګ\Oqa'-jבWo;<5m{~_KHйy08֗\A>;ЅuQgU&p C_f͓AC9wќSN+q:]5o[ze/p3KJ*tqJN=u!F]>t0ًnu=w/|$8 ׎=SѣG=wPT.G#,RTSw]Qx{c=zO46T Pp҆yRIxSD FL׊u <-3׌{x_w3^y%Oz<-Ƴ&W6R/`k `"> 'oB6&]$zTDO"WUYY~+{ĺXyk 0`1JCjE<\ardJDNTk{OPc!)ɍvUܶD.L^Vpe-*]vTU;n ?ed rN6% a`FFVFc4W?k`VUyAbfmWۯa׏Zض`nDVf۴SvCv7,YnŸzrdcgTܷȡ\ a3mLv .7 xJƒmocvQi' E W>1,(g Dbo`|˝e /-vO2uDu5DG5CС9Ły>d qȾzСCg ܈<>HʔV`* bixZ̼! {lK#HFe(2tlIVX'M٬$Z9^8g oZ'tsM%e8 OFiuS'/|[ v8[5g(K0 0Yxy1RkeP`csܳGy@5m^˶~^=!x_'QZ:w\kƘ_>HEV|؆&#PՄfaXGK֑f {l@,`s[!%.ر/CV:} DR ' _=aA9 f#'*;wi<۴3"s[|)S2q<ǁ=8&$(-h{P#~+)ySeQH?{VSJ-ON]0 Gn\lNYBr rᆕO,94K$ŖjKϔnخ(1` EAHYmIܳO?s+WQpm.fEc|f'Exli\΄YEI%gv"' ^L3YɁ$O?,,+\OV.KO3z,ѺBYU-21Ee?VZZW\tÛ{{яrx"S;6!oTD;tC%hyD=G C윘b3Ԙ!نmC]蝒 pN$m+ ]. {O*0Gh ퟡ.g bI7,_ u*_keBᳮoşVZ\ /E]X/̊^K20d"6PW2| }ӫ0ٷ憊]I`tD6XHLV#jP,!Pņ@oJHVweڃzU{:hcP`|JԽCCj$*0Y-/Dgq} 'BRDD+U=AmarteR(0E a婋pi36M0#ZVF5#MeuaE IhŋFqY @ Y͗7z1(RU*nUW}VL?9SGHrzLlG" \yftIEsjĈks iNX;Cx VhRT?>ќ/8aYi$ԇ`X,ʋ`yέ{XBriW}Ϝś,h^3$lzŠ{DcAvnz:R=ҶeŖLz^(|4Wń\?cum<ޥ5nRNko;A%WU2͔M&u}Wu(GɅx݁xadWb,Vjh2I #x\rBFh .9dx:WxXQ~Pt9&)w 1N_G//pp;삍*tD`)E ԓExvBT1* ૌ^]#;[k..Ya< %"$#DZ g)E~q%\/j=KaxS\>E+oe9C#^,O"Ɏ]eG ^<99пG>(Js6Ih#) YP .h'U~j=YRr,z38%©u|×2?Q nA=[IE4}OHX0I 9NܥJ(=yVRZg* C֙MHLI=MX ^K뤼#,߉O6(Q ~r菸مngIq]ݿjf>B~_{&]ߓl5htf?Y^08XՓ4k$HdDd(aMajZ9Őg|fj EU-?BSR}* wNyeVA;*b?]IY R 0E޲;ic Z7I5 S}xr<.\QsΝ<U>~:X,oC=BYx֧@vA`⿫ D,HB#y'Z%X(%RԙH=P"%)^}8u#V' rj-C؆!S$H6"zQ2qK?rtZ%CR%tWWW:܎ꢙg^1h+34NwYE'߳ sMsI 2OUg$O*\Z(Df 9*#5dϦoUUr땬/g05Cr ?'Hf&Y~1H%3q⡅XEʽ#s D\ЧB){] |O^ d|_(ǀfT;ɨt_~p?go=e)@vvt {l_DDp^ d@"=&H(LN DwMvj'PΉ0K ˉw|yǠPj:t|,jgEdibv WK3$&O%!fN:ZcAGg԰k@C=pԎcwuC/)%׫x{2MOM ]=_nj|O%S_gvձGZ&s~r@Z1\&e$sJB2sm6;OMJXvήU bNDI>je,99l¹8b/W(d "X57r%;@!䦎#|Bu7.';k;#!xD,qK5LU2$<V 5pa_bwܐNjyA^̢=ru2?=,-.%ɜ e vI *J/MD~,DOKڅƃD@GѰ dbi,Ǎ?~1g3O0 ~6i p ໳+L>,"}D*iYO'}1KT[GA18H›F7CMjr-+O_s$Ij7ezkq䆝h;:F-q0%Nɾ؏DĀ4!Ϝv+>=2G¿9緝Jj0 Y:3\NXܒYW2 KKϽO TDe(49UմQ|?=~͖6^,,z^GP|b=C ~4t ?n˭K@~'8/&iEv[$c '@+lYb,"!_4z!Zpg~:] CBk( Y6 ѧ.Gex;Odz ׎&qYwb@Qߎ.^'&v ]!JR@.oSZvj"9eV ?da^rc2~ż+1pxiL">WU߃q%Ͱz΄/>$YL.\&+Ʉv旍ũ>%X=d8>ʑajpj$ $0Q~x=QAHAަ]2FI'1KfYfP!795)qN(pf.3MJN4df ;v(ݿxw?J#?Q rr6߽&i敡$!7LC~J"c*Œ]/QX&d(yYD|2+E:o4hȵC&|'e9aTo C p'RYJȸig!K^I^571Qo*{ݜ&g>G` y0ԹUtS, :{3p^.5J߳Sk=?~οG??`!C39Y-/S϶ ߳Vcn&RGiej4f1:ɰe$jG"?ޞaaƘӡ}YBdwIL&g{JG d6gWP,> <1N]-[m~瞉+!;Qxn/jX54 /|zLff4\ⵊ(_ ]s, $^t|#˥_.!2˴x#r"Cg0莑NQ)x/mgrlu s͇MFȉ|L-W"Ɵh'zBqPeֲ 00 #kEi9*ZZ&?=RA. Ι4 2\ӤLس>4VwHXveg r)JlKgIϙivԕ9)75h Sr]A$'7q;Qe8}'SGOK4djR,֯y?!KDY,.-1?(G3 =o$QA-R!YOml&+WGY;x>l=񗣜$-˭~6@j+ѝ7em= '"M<'& HUDt:ז94KU$(\1+bjG8L#4$(`V4j뎛bl"Q%dg[6h.ğ}Հ:0LRz 6h9s)?Zv&22%f=һn ?B?2W:񜘀(kO4d.jM fsWt&oMvL67Ӗˣ-!)!ϐќWyα^QV'FtS)0z`KM[U x #B&us6ǙUmدScI*2SDoCr8$ L^N֋%zD =m=,av銜x:Gt`(= TQlPlH2;UdODTK2?%|h%/~O4 ƟCҼOHghE('H=WB#\!˸ R>7-WrmU`A$|R P,N3F,#.rdG7D{ƺ-lHQ P=EҢ,ߋɧwXB3;t[ Oz5|ZLyvn"dٯnA  nzO`)Т{}UbqOXN'M>#P[p;w)=5| UtD#Bާ rҎVaKD9AsS%T;Hs,οc[sz= tXj9,=K2m t$l48&]&D`ePS!? )Ëڄ-'.;F ܠ]g}K$˨߃K}<(a]/ @%y#-y$yD8J@zW2?Y̼Z’HҗO)=n},j;zɧswVnDŦϋ`e2v)c׌G!4CckPM D3)It3iЂ:gR0 XJa[:bб6855IߧcH2K'aņXKMYDN.@07kxdr )XD[`JՆmnƋz5z 짯jN%qJ|q$ҳYM6 ?2F4嗫ޝjG}G}mbx/1&jhb(vq͎!&g  FOP$b>iO=Ku> C<&I&RIzEI*RS2Q^ŗ>9iވO8bDzYF>OD$]8 gIn"~P+wV (}aPx~-)s8wFr}-n?=g;AgBTIr-FPEx4{QJj?<ћge KO "HAƟCl"(Z 1'a3!4UE3xwx@L(O*lŸ ^/ M:!AW;LQkfkOmP@#ʺtA.^N@¬'Z*p8e6?)~='%%f@T ?LUrO]>Ԇbj}R7ܯÔޯ6-vgs?Q/> YFĚxd)V 4^2FNh18I6GV\kX}b3٤$Zk'1V刂^wruF0ditWeAQBp|^g%˵_|5[wZesISoEޟ>+@ߣq`n?H"DMі$oZ2o`?QԼ^=%tZvZ(Xl]ۯ * (O&n4NZQߣg&n0āv%q#a:*9f^1*qb˭V.Gq,_7Bw$"nD2Ju Z% , FbN+Md!%Z/P|LTXUlnm75>B}ߴj͇Ix63Ur~94ϳWGMghÂlYN<C: me$ox9 w]_Z$ɀMW<4夁%tXszwDN[x:e# 4Dj2J&&l$tmF1/tQ귋jg3K}#mlZL{'XduCb#)ֻ^C`MYdžzxx.1K$ׯ5Lc!bΆ1 ;AaQ>'!yZkmnenQRyT@a2Oz4;#QM>B|&L`l\z8umvfEiAD4͗#1)b%jf''SX}ܜG㹴K^2t3iXSr$ B⹋ vqSƁyAd! _n\:#]@>`}et)oHb -*Q }h&fIxGbqԻѐOZ O*c 4 j$S&ԩDu`5@]Z)!iK`wv]0MĿgcƟ.@ ںy|哏z%Xx]@ò="!Qx^?a5o -1HV;^!4LϏ &?MGHT>`=(' ۖneŒnsk>i`6D߳n}[T?b)$q }6њ=C>O;G|\#6OĕħkSC2i<^KNĭVT6ɆڈlLc_(DԽ`yGXˁx{z CKO`#&>,,%0<ҟ% H#?B%If}O2WstG5Dl?Il &ц@3a ChcF1lla'\SE4"iXC|+w1 XPG/|~gē\aYʋU`#A429{EkfET<Goxi6.'!$2bdCgb.0k@7z\ :c<3d9["0!+Gi*(lؙ)43OJf蠥Z%:P0_s꒙i#WMCv8чD:H0*S;C]K!#JǨ+Ǯzer(,cHD~}nhBْ o$$P/H&w!~u4( 7KUi!FPKE:ak$Jwy`({SӐU~1`[[ۇ[|| |:zèqIrOj$$[.<ڔ,в(013elZYi)lb"{XH5&¨Zjqs P;n!\5t"Ow5"aIhfFFa'do VUII*1 s1! -R1QmD>"!Cjhhxzɤ>(ݖu.c%}QYe,0U vH?\o}.! RVl[XHKNϞNht@ƷE4!U:#;gI*Mfa2i#DL]m8i9KhWnְXchiYJyfAC_qLޖPجwDlkn`< JQG-@"dQEѦÎ/G"9]}՟F4H9z`Ỹ3؂~O{\_OZC5L1Y7hbiFL& ¢DQsc$7(X xh`,4UA`*rv5ك;dg;0c'R(G:܄;QVH`׸F ix$d"?+c668h$ !rA-w`UHKy[쇠k=+Io_r">tDž"D,lb/!? 6-^3G,򍄸g\'D}$:ܖSVKf{4AL:K%,ԘoDDc.%_ZWn۵-/nmk!RٻyK<:5k$5rO ƔD1C$RַO衦ٖʿQU Q6,\pn慡/Oya^e}O#CmEmEFcbEmO*퉓5`HL㛔S//Z[)7{|iyt?*-ڷPI?Y~=L/sotx&~^p}-Rt&%#'zSnQz$ ;$)mG3I.q:S-IN1Ӂ.ܰ"$}=DRD 62OP#ޚY,Ր䂄|wu@HV`*gģ{fqmPwPx7ROÆ7"`4 R^ik@T/ԒLWw~:"Gհdm jH!?: ZVb{&A_;,@Ј$78:̛,)ĺ fHHIns,KS j :$8OiVQ &ZzbŎ@ H4Fo!M5<;~VyL h ,kKŒ$N fM(풔I6;:!`)n[ XdXAhj3/?v:D›>ƚݛwF_DK﯈V7{m;O=Iega$$4ffQ9-x&vW`a𰐟hUf?XJkIހL>Nq|/@☃dK ՏD-9dI7D!Zz4~!Fey#dk2"I 6%NHģ1+4a JQI(1/rP28>3-"V2yL岺R=)QK &B J#,׍au glpF=O:bHRrV+}uXxz _SU,L~>Zw =7SaDL_n4ZowDQ_"^Si6U7,mja}F爎 eXOXgk?׃=ҰQ .|a\&:&92+un8&RDcz$!YNHFA X%}RLqH9 6EHK% ?ş {WOaTq\)$\mI÷jm(PYBhGkOY&ꁪYp`g@4#X/qlFrVH;5.mTٟos|[ YG`g'28MdKq'|cYr^UV )?P0sLtLь\Aic*H(cT'GqI?< FۼKmKXjhxDA#?aoD~s=j'xf\&1k(3d{ePKe{Ud:zmT EFX̼,صS&/*P-f6\#2"'IQyP9gbȆK ^i 6::(0B'DNdS#Ey$ATXo#;D.G+x{Q"솅̴LÂr^, $ _D(Z #q x4d.PQ-HV͉OWbiwE~mCrBmDI~@ 3mƔeӦeRATZ=sE nj8C.&|oC//uNcOIi rkܽwU >/^_cװ Ai^Ak9:Feë́h7OQ6y&#cJRL& כY:hW&2:6дH"+Կ܈q) @VTގ^S_,~ǟZԩ թ"j @HGpl+͐erҝ ;h[AO@RVgZ2͙i w2 &Y;uѦ!6Q m\qvLhdt,{=S<;llMj*%' Shu)jAv$aV0x'&d g|aEzA)Xh7(W*f0$+b^V.nػՍA_KiOo9rKuȌM'.E@BLgxz9Oj҉^j-+qV.qn,%lGQ#wmM:AX=pX"%1A@q6`,fIs~59@ҰƀˆL5 =ǩpTBB(Xh1;Yz'l Ϛy1z',1H3XQI|=A94lCO(uLґOJh@ COoo"\4c,'0)9u Qֆ@ lʞw$O@%[7Ov? eݱ^ cK3~,Q̄e k5&sYiO0x{yJ C4n֟@bv uW6I\,  cB::IrGY4Wq)ÖMX[eEMi\zh#]hh;s- +1t#;\9b6 |61t~Qs$,!ewb~|i  ̂MR:gM ZSWUTn"ha(/xzsw< nк2k=4L-OD`ŸTY,i'0>t}GǏ}xE+N"y'LDM a$*ddl#FTmqVEz؅woce_ HfsHj+v:Hª#f?u% 3@ve5ш&8"YW }CJxIiDpnÞ9IfhŶpҴ5-/z"ÎI6(*-wN#(H.IL"6Uد_y" lRC>,!7l[ƹ"Eى \ Ka걃+!4DV/ U0kw8+nY9nhU=^^d'iSk^"ޱًҭ)&"yɗ( j@3t&?Dp{L$ːpaC3@DnPUQ,3Wk8@Ŏ24`x\hOcAoP~~uog``,<m ?/}Hr|@33oyۢ#n<yN(pR,;T[ jh,q8K2lQ&ђ kyj HK6DsTQd"6>q@V T`R0Ő[WK}ZvYB^ZW,E\OfPM fNقpTxE!OҤ7 Dat}닛nxIm[o <%Y5Los `DT#S-HؼڿHDɛߣrapKNڃDe-ZhIb,AH1ʊA_#]=,(BAqD/I:h|ESS  (lA!z33RSPQv -9?&cL̙i(,XA)Ӯ?! *)\57wju1H]`fEK$mL!$tb#+xeAXC6Ԅ ^|c#7oK%XɐO1mdmG> dx3yɿAcԺa5'4|FA?JJLlh|a}ۘAkh i/,f)SψǒdNpi07H1,ӵu`3%aK',5߮Fa9D2,ӯ%=H\_"Z>ӭ~1uJbBs6kPAd"DZ0'[:XUVNjS[~ j !O^)ۿGSة"+4=nIF)*fӧ GJ1&9~F\k4BshBwCZ< 9Cֳja5uBawlF~bqM6߸=ٕ\z;\skxbu зsvߑ-ڰ\LY,o0I阇?7w@_j? [?oZںoNҮd3&kP7Q "lzo?;MxkuĹw= X |kG46Ј+9Lӵ fnd15TŘcϻ6Āw41;D Oxv t,~eQp_85z:tS#nE~/78jȑ#/eLɄm'p TΫҗM$oSM 2ğA/a-rN0۶bG~_cqCX?i'0hwR ϒ;aUuo(4#c "Z&,λc쬴LfSW!`ɄC8SE`X,^. Is&ޥ֗/h>dqƀ=JI,8J&% 0b{"/"QEY3b] q'm"꡵CxLb$կ6G kl'jgT(TgM-+ D+To2h_0`p ^Q@﯃Z RR.Ȩr(x҅Mq|K_ۼ~K$@ӼA>8& wz²%_TS+|ɤG6'6QfgmV[_xukǚ?Zkf\m|zJT{QyIYw DHi_*$(4K2dH5Tn!8E܇eZA), Hx.Dr(7D^Uxa e;c1BRj0rb0؉C$-0-DК)hTOY@XJ`%-tr~װzxUG &^7;18G>?:B"y P6;?>*ߺu֭n~ n5GPHثZW^j5'W\ԊO6777#Єy n^[ǵx5`"xhn2duB~RqA=sko O>5?yH5^ Fr,D^mєIwv{Go׍(((=]}}$_ /ķ e%p pV|Baw7_x4n'"=o4C׭{w;Qz-:4Ba].m""{dЌc֗S;hPvjWQî_6)1' F?}LK`@;V1 #9zkG8em(G|c9f{) ?|Z;N$Yثr:2 `< 3̟`~?JAM$o.XhGAi6Ht{a/ |u je VyVak{9NMP_Q yC0/^RiyKq!5&N2I Oa0d2+Iۼ#(^@GF 0dDsBF yf2FvΜB$- D1wIېk ü+Ӳ6+*~$<Ϋ͂g.'wtSb.S(d6Ȏ$1h9 4 LЫ0 RLboĞ "j&}=OZ%OQ7U,1 ` s+"_=Twe P "Yl&{ yi[#`\#A# rz`'Bc ,>nY,9IɔDcz, ]M0b#J Sb7WbKXYbs #8d6͹#xgδ-yF"},2xxufLO+%$<i'SX ^pQ I% @"d@u[]- |D5$d`C(K5@Ë& pR tPR6R.Oj0!K!d(Ñ$Eb=p$גнIn,P2'ߖT20cfAx)Â>'>3͍Ѝ'OӌkEObaEP !r3Č]9PX .D⥑2#;6Gy]Rq| 'CsO( Y(q"-"|'BG WZ\j% 7Rr&U}ђ)=jʱ6d0LK@Ge+@z_'LgOӜ7A&0a9"YN9%iw>؊,ߞ4a`E^y z.dIWĹ5X[GJ౜_JN=i7gQulIm]G5B*QR4/mV__( {ES,g*JU SK(UN5i$phĈCfsV5L9Y IDATZ>S"נG#dD7ZGN`' :z/d}!+lUnm$&ZѸo/ަ('p֭Plٜkt}>}b>'Fw+&ؒ8]z7>G?Qb ܇l=e3O0G0^L4vޱ_n }04WaR<{CNE[C(&;̾>w,_ζPF .C%\XUgcC$1$= AT]c5\/^syu8h?rlNDVaSg֋5k"βXXV,`az#8hZ:Y*ڈIU5!S7xھ8-P Là䑓uu]*j'Y>~t~dOsozֶuɍed߰C9)@y0 QO- 2ԵttL¦|"ǒg)^k"S\ԐInR)@-XQh-TnӢ@7Y},Õ;k uUׁWJa{IW>F #R r+g 4v0RG8Y "X*f"LAPFsB TV %Bݞd^a.vGE,zhr'|W7TTQ]qA5nӍhD _xry168Óf|Bq\'oӯa>c<gNOMPSX}pT[2~ƥ-} *wϳ}.Hr㗃v/RLёbրrI껲x!'epף & e9]ْ)g0Y*Z1>d'P("`cX\8 ֏}Q7m`MێQL=4QKv2md6 :YugcR*{#S?c$7$3 .]KDߣHKj]K%i۩lVT"C(|]$)`,5u'f/Y DX*i$wwb{^x̪c&.@CًlzŒz(J :'Li0_QA^4ݚ<x9:LF x]fUmWDՆ+y ߮Qm$yx_Nzӿ %vKn :L&sy찛Y]G"/8P3M&ɖҺ˄fƏϋv\+HietApVhftĞ_wV4؊'ɻ2 9C\nH9K=9$@|>44W/#o[aєU vjh ζ 6h,]ݐG@thz&;CtD|y <4Nǻ?A*ſqg/Ojx^_p!jڡaUl g1LKiZUM-^h|>#_|'(B9.pO\L; XD"6_O:O ̔4hgDla,(KbLg([㋜Us;U1-H^ef Dl"'L"msk"CW^)ՓjzYZFQ/VM\+2a!s 4ENd(/vA >H5nդEЗx@k3"Qv]7ŋryfl ̜88nUy9؄6:p28(+V|փɷ߶?I1n7$)LBiJ\Чi]/SgM 7(`kAzq{*xPKXϣ$M )9Q,H}V *P-A=>[hbF G-P6ƢI> JYnhO-EcWF`3!Ir`W.{ROԳS:C<Õya?[lVb#{߿ʰl0Ӆ3$)3EIj߉Z,> xx=9P s1l5<2gx5frA]m#zٺ> Z`.XmH,aW%>aNևI0s,|p #Jט!#wNDǍDFec6^/TO^m> `=zXQ`:}rkFz1YcDsZOJ4*+_@a$G'={?y [ʹ KIϔSZ4z|3IkIX:ױ Z|DeDG3| b'ߑY}{>}k';֑5zf_lm֚Khkۼ8?lCjcGPٶ6 tlm۲ߵ+"yn經}]mk#SP9-w}ܾ$Oe:| kEz{%;=a({׏ l>nR۶vYmm419n |zt;sO(C\޼o /OBvVاgC WlC!fJK^ͯIbRT? 11)l$ ZZk#2ZA Ҭ(׸/-fiN7u\S)_O= 9>$WIѢn YIQ (ZK7oxƣJ(::(/)6I1BzC PМVYeXa*TD/1u7y&azVƄsS,ZIBR=y`q5_тI:|DX淪I c1NrH}E#xb?elm1?<?8 :%$){au ^gF0߀zjZHk2,$>J F,RQ$|l:<!rA-,r?[ 4Q-2-(׍~y(iW 6p/iqnKfj8u;{Lh*?M"Ĉk!_!YZE+ APPɒ&7T&' Ϝ[4\?(0I:,k;mzU9놵(Ic:Vm#Fy8TP&$|9.9^@ْL&VJg~-JTᄱA(l80|#O9e( Py-Wد^fͳ\ifH 2PEJ!H,~4mRaSʩw8z9x/F40cHcԖ6w"j58׍Y8Bj7(ӢGjK `1=@) )cFW')ⓏP{Q%i:62qXR[,k L^TmG^[(L iqzaX[7H׵D)/ cv簽 2g[hjkj 0P8P3$QH:mǙ/R' ?‘(H&o.,U^vO-+ug`ZN~&jL@F>YF?Ji lkoqh7ݠ?̗LrD3!5o k)J.Nh;E=Tݵ`!E +HD@oM[}qt i>re50~娝>Ԭ+?uOJ*>8x77@ ͂=wzha@"W =P"z_,vsOYMs|˗ík 4+6[j惔AovSЧIX}M|)')uhE VuZa>!o@3@KiўN7OQ&-?`!ɘyר(Ϩ5k2 ͘-@`dMd!WrΏa?q0 j EucrĠ|Zfcc9[Go'MX8,q~")2rĈl#:ݡux;SJlC:t}փPQQY],-++qMGi  UfZ4 7.qLvZ b >& %4N^ PS.%* K"<T#ӜԄ4Q$H]|~JM~*`2? d7jpi^EIx6DqPM`B 1yD}5hlZBР_3P/>=̀CBK0#Qj3}Ϟz&+h kh/!}1M_0|ŠI?$gvMˇǧ- ]9@nSK9pi`UB_}B~ ' ˧a1 x} ڈ66uv3/PFH@,?&FFCڎ;o ɯ&)7h5٬9yvED^W<X>%ih&q:}FX6bʕV^ju+go]Kr^֮ݰv݆u":脊 wn(ZNgQo~"HE!6/E4(=_B*nc5/t=! e`t׻> ]eo'@2% g Z1!؁b 5"i]SikJ˦? ߮X䊧D0i%ϯyfk^~k׭Gu0r|w3m[K̷m胶>v;Ow|kק8yND>։Oǎ:tpa5d/7->nI( ;:t8t(C2A܏;Un\'zN?^GsMd<" VyG zޢ&̪.]SU1߽?zG1Hh5xO*9jJ>lnW4?DPO<Ѵ≦'j&܌ OYvNϷ>g'u0fذ7#9uۇ~ ۷5h"tqHksqءcCHa}I2(]Yx[^$Ҵ>HKמ#aCv Y0^iF&Ϩv/t$(yhLt괛n髞 ԛ܅L/޹x{9rޫ,c*jKsOu+XZޮXEsc+Q}v*w|SOeq~΂P6!NF@8%2k4uFa%sc8 4F.?h;T[?㦢:0y`5X qRFԄ bx$g-"b'OY?}}}| kP҅U3G+bG6Ήkͫs]%%Ӧ񛷏?~N;q?8-yn|dD:{gbehZ`VM"`?zNwٔ7O|dBkO Y}) yfP# E5_S.*# U\2d NU8KH5 o}۳KpknhnUzuDH Ճg~r8Kf )J&G\Z>O7) el|[pJŎI&9ꊧ}ɐߛ*wr!(?~3w(y}8A '[W߆!/ 9urGMp\yyN72Wᨫ{¿sJQN3|AJQg5-O5Ft>=~ 2teTLQώes${b.ѩzo?fgIqYԲ)ҹ ~ʙ'{r|ګ]I)#::/ 7h=%=$HYϝ[*\}+&SrVчz t=^yUN{~iiiqtָ?u:g)'^t+}KBMFxE eщsOxigm3]/vβEabe%%4FF/jpG}sL(*)r/u/s;nw{bz>Ҝ1񚖾[o=O|zI]KcWbG@xV'8:Y?7wUUpW: V.˪|h]X'%bG?pyҒRwI `bPc |}7&ޓ!6 Ͻ#IoY <=DL;yJ>u[P{6C(8<զW5q| uN5nwYYYU9c/|}ўHB%'Ü<pTrA>ڷs'teTϩ݋ +jJJj%{lgӧ.|eޣʹ{L!Y:&2vZNvՀ($Ӆ|_5Ʒtuuݎp OR:p Dʎob| JRG7} Kbձ-AYa SZ#ʃ '3sFUeeeu{>ҚjP@,.(VZ2mjqqY ϻd-ڤĻ?&oաrVhI"*7qv$1X_~ƍ&qs'L)}ヅA(T;`qw9{-wHwW,W S1WþG1ғT/̖HDݷ8gάJM|4;) *-)vjfc鿎 |Ī?}+Cj QBg%7 96cw^Ӝl?۴]FYyO#ڛ'>^8}fє):=NK]NɵsLЛ"~Ue-_V+ETNۣWlh,4l.GY8{u=yoǔO"W0 jg=u9@ ϫwK?;ك7Ybwi&p-z9gv4r4n.}@jTҳoӃsάr:n"7eeen=wj ';uDm% 6L'UӔ8S$tʺ<*+?lI) { oߞ_}o;3k\55uRT0Jqf4?w]z_mmEE}ykS ws*q]݈<(bk sn| +"FO籢Fe)7԰IPG^Z"%D2BCCuv cIN#~ʟرI|gYߗN>̘R\[:]1 b(lxZu8bSoથ^|_x|kVXE OVBh+Vl tw@V7?y$PȜ8ٳ9)-tGM-)Zy~9EolUOw ӒA׊n ( #uu_[]bLyEmuMUYi PS--s/@ %.MRG)6i/fY:AR RMj79L12Qu憥$0::{ 5]HLḼQj֎!&ȧפ$*'쎑3+|*8BTo8J 0ʸ .&N !#i< pov#,ɥ;v>o/;}GL _ol 4^~"bmnwM^ J3*ϝ?owqt&}Ŭ1ࢷ#]ǻ,dqCLY%^w26a_Opk+:/tL).vL]tKtbW|ܜqoW75]?H>hUWp|#Oc1uwh8{Q6)*!0}҉1|< a OBOt灝;?ſ>x?y};>?6 چp[7oފn؀pd]G1AXx I?Q"| )Fk]UhbS̀*ԓO$Z b'cMo@ց#Cq\0Eփ}ԚBAj7CftGyN d?.jZSxkRo|7C?CTuM\B RC3VH<:SvD[SH#F>^I ̒o>0U1e]#ޱM*$Ӫ3,=𾅿yo @J8QXhq }!k7{`}@FS^>s 86WIIIkj *X1Z[,sb鬮t8]5­N1k"0W'Op@|:y~>8h-[sGf 4Hywx6igZ ]?a[%͑^mtFmAfQÆ5R  '9ko U6`\oH8FoܼXS֦]qΠ-YcFCIs8rM_iiϷBSTվu}ׅf=rFUOsAu܏:v׏ܸg߾/IaB:Y7-SE3. =zdߞY*jgVTV`S1/A0x RYVSk 53gMygowϩH/o~Vt^H3qH*%{Nf:\΋L~o/z ǯ0GH0P_D1NKc x: A&vM.X7ߧ֎1εY( Σl zz% :b.Q7:@{Ooy⹊鄅WoJ'0˔)%%ee3G]`X  E3f[[{{{. iSкpXW}o_^y '7{j+&UTYҩQn|00UOnwԚb'MR`Y~cޮyХ5dAz%d #*~OiX c]q}sY.X+é0C {ld]izZݧ}T6mOKA]uAO4J -(FO~sʧW EQ(\UQ\Nn0Wɠ]`9`qnx;ї^y7ƛo;l$޼&|믽K>?󮉷Ə_>f3gVUCI w`_e+%ġ?E2l"wq KZ(uU;K _??҅HLN?ؐ:xѥ ~-_Atj,H{>FcԒ7R3';+k*A!11]T7[EI "7 :A * ܐ F+Z쨁t0 QJ#a^B ?۩Ƅ*t̟DKu;U3njů&L'-FtUG=S=e8t; V˙xG1O$a4&6glӕnmZ742QK Q[PJпDž8y1uO1oƗ/,pV;&"#jL"i~u<&jt U~W5{QAtT;=&pcvUf " ;d#||7]|W^GyY@DW#TKh3Ѹ[t3 H6:N_oO)~Z:Q5_NL?TbH#vs jӭhЅ<vK d¾|>v$fk+ 94b Cu);jkQēwUSTsVM6U0 螷7$|꺺:)%fpS]2gi(#U9j)'Mb[`'2kAl l QX̉f+fΜ]xB)h5!btf4id >_(|T%+?xH*ΡAɍQfK1ndkdF3-:<3 m wm^chc26aд-EBf@[qp S`[{ܸ?}O$ۛF~-!x:5zGG*(ugeT`PII S@#rJQA,վ蟾ָavRJQ:xK\SaJTTrꐿH nRY dQɔ o/{/L2.N (oׂ ۣᯍ ʱ#_Ι~SUbF80¢6Un坹;ÌV嵟MҮMH+ެm9$U؏Mcüs̼?s2s߼;sOB@X!B"=('`m`}91z{s+rVz$9q3]]ϟέ{zl܏Jo"yM{361~%3Iī ?4>Ho2̌:59!2M&0|J?M:Dj&Ȍkd.bc7o>{Q.\<[9sVxHB"qa3R1Anp z}{ {'zNP~ /wWg=4SO;_wnwG DJ:DQTKBJ@ȉoEaz<_l"($ml@px i)MtpR VztѾ-Se~~0LxT{w/ء/vw.6u޳g(F I*Q nؖTTp;\:WD PNE{8Sp{`_t g`x^ \Ȩ{wG:~kkJ?s)^O.@D$#RAe5X* lmӺ"4x7Ϛ"mmã3GV4GLC D0r9|/ ϛ.ǧ8}wWy]ǎ=¹s=MW ƠO{?DpA0 Fgvkoއ?yJk?qTסvaK#rC%u4 4׫ڲale4kdnQf>1rMF[ fkB,S_5,eI_|wos׿ر/|ѓɅr`_1z~" OA`R@^p=zȡʫ|7nFlgEzb}{Vgm&Dxd#@/0s7Z7j!-J6Vv<>ht3+pS mI^W+e6(Z|ojk7>].8]]]=q/޿~ *pFEA-&n^sȑGN>ܮ={?{Qsߩ_Yc %k15\)“'p$}!'ӟA19pJ2%TT70:DuiEıʎSb(J~xwoo]pן~ϳ]{vuu͍Sq|psz2t׾o~[oq[w޻g_x`^.SD ٢86?Sз!TYrSW@=LL!M_s15l궊^7K~g,iYX5-EgUCIQ3%A]7w0ksoRC&W7o<~̙O}޽;wuŋ8_.]t?޽yw}O؈yju1-`#ȾEvⴂe-TH%NfI?>{IloWZ5`L*%*4W (?c^>K={jI=y\A;ߋ1O-:~_PC"݇@Pp8ٍ hcZbHQ[:ֹ٭Xے5s2Zul*p$."[,nLMZ mz$c# LQO%sH. ]Qqogg43OB:%MƾMG0旬O[_;ُ4Pk2-E"3LetJu}Fo(&aY]T[aw;CdMQ(|.*Ą kU͕SR]SL&п`ɺ4u(@Lq7^)]`]RLS4ϯ@ꅖJb LO*)?W"TU(f 14t|jp0 fc4C Ä&#<*VA*sW"zA=A1ɚ `=j^jbdPt*ǣeEC:bኙ0w+oti?1NjU' MpFaDdeˀ^^D~}T uǹ|M*A)y INQf$v'=X E59?uNNEei7g_@́f1ǟgĿu2 <)pPFLmxlthxdQC3@Z~z419YзmM x05ьPߙ-g~aiqyiiieiyӕU*Jj+!gVߟ9ժpe]Qw-elryS`\OU}'_y8|W|[ ;U˧ۊ{5ӣNsJQW@?<{\S(@Y8V2W~ZBsĒD @6}wpgIlR|Ҏf/^VZfm۪L4K$jLbڭkgs?gAwv򊤧CiJL"QeiR!s,TzI)(an?uqMI RE[M0:9< |fx8 M* ]/'Qiڅ~&I4P9r76f2IJ11cNlF99?m7 fQI/3]k @y7ų j*ӨPkX TZp1{tLc)E\^4T8ۙ㻅`3k4d:U0RCv#U1L5YM%.t0s|Ȅ0qaaTŒr$FpZ姇R!Mmn 0EbT- P]A+s|7TAD;bL,|9?m7ZESR3 .?Qڟ&4EByAH\U@kt>Ukر*0ɵJ͎s~f]@p&1}|!ŗ J#JH,E<ՐT1Hy*uume>o;]u` =UL &+__6 E D C" U:lCO"Tg%:2*e:acP1gi R%Vt[J;eQOw1)*@FŤ9a@ K4Uf2؜:JkoK"@ >W؃7, ݂"_].(mdu/9ER}J AQA5%<ZmH~rH"3N[*R$,`k#XHVú\n#TZ!_62*h$%M6W Q^>ى8Y V`DR3ECHWJ(<*Bt1T :U -T 9J6J)jO2:{O.҂dN/+VĘIE'JJC6 |C >{?tbjJ+i9?uDmMJ@l=~E9N(Ȓ4=>[ 3FK D[,d?f t>ޭd$[b9?u,w80EM+`m $=! S$950?D/W~e9% " *y@bG"J1*3SVUI v@C"/0Y$sVQ [){3v {,/['>b DUSx qih~x@T0T|_tBT$Jq6+1uNgEFUwcM(|2NKC7LluAzWO&[0TR%[Sj20 Isn"յQFEm@T6vx{;MkZDbLµ"ȔfmF591i jDXͭe{O'_Tg E#VE xI`F_ze׌|t4)P[XvYO;F&,%Pz ѱNCŽ>cUE'D5O?ŅO)9j B2?y@s=\k tPL,JlctCT;~:EMkfLx[P_yb 5 zsȲ>P:Q^ ۝v<7>6u]! 讫#/ձ$-e5wԔS}3dit<3jٙ}PO_yBIdi&P2[2PY/h>$O~7ٰʑӗAO Uۭ.L: pRlP,WBgصԼ 9?8E`+ccEW1\#M 9"Jhmq"oIENDB`lprng-doc-3.8.Arc2/LISA98.ppt0000644000175000017500000311500010523400373012413 00000000000000ࡱ>  13$%&'()*+,-2`!T"xܓ1os(؄ QTxE7Ús,uiW5# ŜbDL f%3 30]uerpvW]UwuW\>w}a0]}lgfBt Fm!-oS<bjLwW[fVyybV}G"ݼZu8=I9 Dqx^(O}09 p _-l%N[SĦ43bP U/B^fb-QbĦyaxsωL b7eITt*]'^_%=aw1@&]P`*?Z??DZ{ >Hw g gx*8{\4b> Mp\#`8 A0u6z{rV"S;m؏{`.xB!|m +qj y$΂p{>{c<Ƙ:dtK%0o㸁8?/n0zIp4w܆jݝȺswcٮWUv ƹw`pλq=܇F ~xi Lj\5 ZR.s9eƐ=@f䬂y6SOFp0 ?;AonKq}V^眮f|Fq]?\ 'p0~aNZ+{G|e?8`8?&gt5SN韰ZaVzq'*w;[?w~~,sEcQM&v?-~[\-~y~9 ;lW1;5V5Im0 qq8nj~TnAMҭ0n#{N8.8wN:G^i*kpޫq1ӣmA.Ra8~? u CM1#0HK2]sWwO{GY1ё$>ysľ_\?/ DP$[([*Dȫ5o>QqLOh8}Ta4m` `3{zV s +qfJ,̀"Oy.>U\X٫WY:a}t^=T+x;>y}ygioœ0AF}޿WşQ:'>qB/e2MMLN1Sp1e_s[\3qx׌0A A-S0ɸLϽkͫx/ SMgcs%w[uލ5x>]y_OUߋ/;N0_]dz}^V"g{gϜ%gw =?PiϞYno|kq!ƻ}swƼc^ cQy6a xUioL:Sg\z#UG6f xV*  f`~۷yJ-|J-ߊcopW˯p";IȟqWgYp6C΅1z1a@K=\rM 9SY?3p3p~F7J;Y➦K:a7z҄UM잏1C 3 Ep1&}H_~|? I V? }bu.RbK|gҿµw ޅ"З̦il"Sy=,18G>Εs棆@oCz3!V@|3Vcp1(Br9ǝf^7DNm]ͺvm7Ku!K`+Vd/(@a:1x(eP2 b_Y|0TKC΄;QhBx7Ea${0!01=FHe~H1Q`(C1a} ya%!ޝNxr7ݰP* Ri=tH%] \m'YBe +$]n,T.SKa$K( Hƻ( ,u4N6AQ${#|#p\W䪐Q<&<|}0\|WrrYȥdRQz "r݂H]* w.g) G*MwXWޝzn$c<ƒ)bXqcȍp\r-V4_K( HeJ>w۵.q1 yY HI-01M܄1Lg;c( p8zr]ȵdRMjr%_8]pg֗z ZHI8f*-p+vrǝ]|R1 b,;pgn ]o6(A֨3kߥ;"(dm:LǪוݳ<88mp;N.ag:Γgq|x<1GLg02i{g?%lEp I)혿K:]o$6A$QU<uO@xyc "f3XdYt*C &e8Tjr(AUW^#a S)fD>m#+-Hy Su2c2F^%u@_kGmϽS:;b;ag[|3'qh ;]exއ`L(3I!SccV4}潐wb :W9{^ީl]r;2_I~(0Rekq}aUN+3Hx #?Liy.y蹰lx:;bn8,EHI,*Pf9s.)4 P72q,0 82PH>2&0F7s};סngt,\V^2ɾQ]l2B~.(OiE<i;+7l$=ܐ9dɢL06s˺Sqݰ[JϪ RaD/L sD}=QhIIU[Sb8_X KbE3Er9)z^!)",!Kc00Vp_+7=3c,Uzp.9r9MbfN>Lw*[zE02ҝÒZ!xkJ}~#,!K0Fv@orEB@Y%J%̋saݭs%©.>\y=+òw${z彽_}'s_!^t{(~bUПJ ]:\D.$1浒 :ۡpG3b*&\y<ıbe !0Bu?U\ŒXd YQb\,SFpe9νu{`|O,C`-e+)Xa}X]r;Ļu&ԩx"7M$[ީZ` yϯW,w=.0Ty}+ ,X(K1^`8 #CɐdH,kK3';0wy, Zyq$"qܭ|9,] WCbe4j ,%%(ˌQp .%#1Fv*-SKYzIO ڥJpWl{G]Gfܵ$dr5W+ነ˙Re='bGOutxxn,1 GeO(Rq}n#=n%} iw[qBqdu2j|%ZܫAPB'HA=B&X, ?H"{$<:{#Ȥ=r}^rgX_3ɝjmCN7x'tT&ӏx"q< O 4yYe ,L׳X3X0hIgj=f{= 4Gpԝu;:TyF)yJpSqXdVkI# .v+ک ;OD$M{"y%E/=^a^d YʔX1/ceK/$xLx<)h,%zU-v˺[qzbܦ z[yڽ,d<`"KBE^D^Z}Wj1 b z y^RJpJPk jgckIqD+a;ۡpG34,(\}=1b2La*A$oym,()zY{ ML%L!k2vXVb'wWa[.twbIӽRM&XTv_: dTI]灷^b Xc[M|L%L!k85{M)6tѶc.t ;:-O9%IѪg㋌w14Nfxdfab,%%1)370!w;^'宴pnuhӅ,yI$t}VTv=YLd~cUg<̃2zkŞBdO `K86]3k9ϱu|t"܁&aa(U>KV(Re=nb?Q|LP^g}tҳ껷UƁ$eQ@`N!z(cJE9Pd8%}n.t8)U۪ez1̔A`!`JIYraJƭP jv5wԤ=%Β)JM*K dUZRڱ?9V\MjdjH-SEqdK>cǫtF?aHVu|UIׁ;7jUYT* zH+;9$Pu+Zfjb*|f\FU&,,TWUuhS'}VeL׾ѱ (`{43-1ZJ 򥟭XGt7l'" u:VlVUu'}q@3ѐEr9"=fGwdNْQjv4hbtTݝ*I}>dotNґE; Kr 9`;Ƴ:IWU3Ȗu~ NpY [u_Q?ُٗ죄gG]Z A3hg:bt:FA 3:FIo(,Mag.$h*vvU':WըhL+W?ŸipJXդ͢cNSSGWLޙCq;aӆMUb\IxN"'*/{N=^?8'd+T#q:~V;Ci"LK8:=y6u:MT.ɞ;cH'ȉ#DrZrO?Ÿ8@͎4ʗ~NahbpST6UIp"JN#F;Nd8N?ŸBN 99:+qu2Frk#nْ$t*Ulu8$8Y larLNS'r1# ;+]Ke啉ۑp'tb4H;{*Lz\r69 ϩ (gʼ6J1N),9L=YGl'*Lz_^,MQ**>1ss0tRNl2+bw,ҹГ qUf' HTzt 'a/̦pgWM==EqɮS}])s\D.FI HJ1P)'LeU8S}>?:a;Pn^IS+|%O}0ԛ\L.;v}_XḘʒ@_G 0V WJʘ,P|~}XG.:!O?ԙ!!ei0I^uxgʼ)+002D)3Fu=H&7:ix ;kq%_`h&@%[ٺez3[a<Q&('Le*J-{[X7.tE+Jn8rRfVdn%6:v dqݩ2Tb _׆n.ش>vUd=Y֞&SJ[#ٺV@gYxS n g9)YUj8^F> GfE,V42f0x2)`| _ߥ^ ePH`BXe<"F%,9ǚ:%t^(aKc/w*Udo JOѨ tROHmH9X9BO"F\%uL} #p!K>[ߘnԙq)/puN^C *u'*9Uޡڋ"zWE8֪fjDo6"<-Yvvڤ) m^W]63}(Fx, Sꕡ?P 3jNi\#EpGHwuǕJQKXWn k @T&A\IH/]BF(u%ꐪdDwpʻ]FN+<$WXf+ jw#=6IGdIhT;WY6]CQĮPg?Uc5BQY$0n`n cԈF- 4(79ѡn]pƥ1T+Ud]qRjժ%~&.#y#rcƽ+fӧcn|ڵnԘ{B&3Uf.^{HOc(JuU868P0V:ױZ_j]3`&"~9!sZQK,dmFlGY'fl:Se\uI MiΩ/PEtAfJ?ߍ{*%ߡSj#3[=#n"d(|\!HuNڸNZ ${chIS>#{Wм*̇B(D"Y䱘YR೻2kn.ش ' il欕%k`g{=Jb;CM䝷x]G<5cF3iaZc=GwuDwCwAw,)i6Ud=}J j F^%~<(rݹ%xͤO0#@Nkx:b;MKi5:3W]ړ x+@zLiekץ3O(c7 }y.yx[-5W{N ^{* &T6Ek Cz< O(-Kkf< OaIiܝ, mH,}ᥜ^tw¦|Ӊl&SdxLױ`uN4{{wu܅&owBWf´fцc)A䳦{3 ۅfvC)y"ũ4dUySԧ5i`L%oXq[6#};ŃJ{L?0MLs Oޣ|ފH3넛4i,Udlk3t2 +B V _AE4 `6;u&1)PI Mgc [ɺv͖'IZL*Lfai&A4',pl`$`XۻCibcٝb _Y#ͬnp|'M3{٪K>CeY ]LgD7;|  Rt2]deȪX+PK)Ǭ#n7VdKY$M:$c >2 u01'S|Ƭ`JvQftΐ>+ǩ'Wȏ#w&)y4r]dm3`:U }+NcMu&̂0eNeSq+U^-_ BD;aƥR$+Mq%{^[0L9]Ύ,ڍ|_̂Xys`.ɏhWRA:vf)0*1;Y;& l;٪̶Sw1pΡJVUЈ8.0qp?<^UHg=z'6>q(kdxךgtߝ#:Mi>~3f1kW!*.BmwBUȻ yW!*d 2,T XyOg$eƣݱHځLiOTT+Z }w$߲m\ ,%J`YH)'Le*LJ_+Kvi#NR_,MRa*,sR-dqg)ZPXJJ`,||dTƔT8~HdayGf') ?&)g+LJT[J}MkVXiG0UKb) ʇ-߸]twIKT+Ud-8R YJ+ZDkRiSBrWoJfjBڲTX:vI4,MagWQ}Uc%O J G5ޯ:h$MͤZLSh&:ۉ*8[&'I%8$chDS3O|FF]4U)'LevFI]N8Bݰ] y:Ʀp*UduZ;iVEY+bvN肕YBӨ$U1ϙȰ#k7*Ml)WGTjuN!uvh#J%[LYҘOgC>gr"Fs.|g3ޤᔫ#iMq%k_a/_$Xz=uǕ+_| 仐 X5;ȗۉJ2.w$ʒ쳯0%e/G)| _ #߇`rRRg{ʖ~}F%낛4[MSn*.sZNڠϦZ ][,ՊgdU`%"#jqͰԑz!FXryNsaPgƥilz¸=5(H+pg<7U+m:H'tJUduO2u1~4` _.inGNԘ1 u"^vi'mR҆%O+^iUN`%"=>W*-L+sU[Mrk+nq)y"T.^kS:H;i>CM6\Q]V!:hb~NVCg^it$mI:əNAI F]+q&KiU:0@N肕D6Yɬ\Y:Sݓr~KM'J\7l']Iej}&w+NHkN=V\]]4*F\tΐ )_w#&=OK&pf.dڍ/_8_@?WakR/ #{@~4I i͢MhǘA\޳n~J-KT${k#}E$_Tm-plK| |Ȅg0%d򥟱YGl7Yܔ?8IU3v| NhϵFU.M~] uP H43-fzks4#C3e3~ۑh7ZLڸMNfVydwash"^BԃT-TN{.үwd.شq)X"U.[j?d TJ"czy"d<1ȫ`*ՎnJ %jv22$8WʲUdoǹugDgܣX DD^)2L7GrqLE X>o^z=Z6C:1Oe"IYUd?m6!4fu ldc&)l-Ȗ[9*F%ʢX[/s֦# ,?N^WU})6!p*jc,i6#dː2ʙ*dK6eݰ](7xY"7A.[EIv0Ҷd|OZ V\YUa: aK+0UTeQc_|i 3;;a;OKVn+UdOm1mabר; 𺷶%cGf'c3Yܑr%Ltck6;:ᶡdd)l_%IҮUm3IkT!ɝa+dO RRE5I]Y7l'*Mҝc$`Wd/1vev!;c,XI.+ v=Ȟ^!{eLx|܋j/W绱D;Q$tR93&{Fg( OxG3^<>gzD^ d<xu11nF# di MΚ$i7̮+مYz.YQv]a7؝{2{ymԒ:>^J/~sSu¸7v%q@PN4 Vu,L3ٍ{dVǨ!1|fl̖N,9OǓRkYep+م -9)bfWZAdOcofj&-10V@fL\;ۉf4pOVT&ɞcH].FxF]]n;T?XU>̾J#i igqoʗ5#N<%OSILʒ1vd[ +"B=mma;^io#WB܎FiaZhSv$ۧW*=DI:MbU`npgdk>Aٖlۓ+խr_mWcřAf`͡nBnG'ASdYFJ gfgo7~&kEh?ch΢IYK~uD^ 211r[-Lkm8T{l7xtJ_;r[;D;nRJd*KY*n+EYIHK`wԺ"{ct}L2}P7Jx7l'Lp=Y27I8${k!Nv#ibn=`O؋?ǾvtzgsGN{k֑h'l< vJ_er+TU+%bN]> [r[(U>OȧYXPPPPQOO'c1f(Z%kN|:,aI \W.;('|V(3 x+pl ITBTC S>SL~|cN; T5mWL7t'xܤD:yL[۱[BAZHsDhrX(1f#(_C||?pLZXCw4էV錯+#ͩSt-*O[[%A1,9H3k^E.h `! KÇtu;F>V(LK)|KS}2}؎4攘')Umnfu#|Elx>WD~voE_k"N?F?k"N?Fk"N?k"NӁWD~~AWD~v58=YCk_YC?~1 s)@~anš~ ?~ږ~ ??dMNcQ'krط?z!}O~֥=):R 3O9ۑ~_?>7k4mףfi Y5~xG-ic4Q_S.+݉:3D~vCLqt'q1f)݉:D~vC\#&q:a8똧;Hǘ sQ\8G#q\8}s`6;dc̢1f)=rXcG☣ G018{2K=N?Lq,gat9&QXG.S^GC.Ǥ̠AsCsacg. 88V}|_w2S=N:FsR,8Fq*ccGUfi8עhcMaGktu _+c-j8Aw8w Jz8n q~üFc6̢5?[=N?,{ړr_2S=N;<FתzYQYw ҿ4sFr=?Ӯߨu>ZVǏW A>N zq9"Ȓ|:v:M?r9k5N@sN9Qۗ>}tU.+}/..TwTԢFqc]eYLV8*O$2;P&er9f~Lߨ#Kvş'l៫$NCTM~K?sȾ?C x溒,[!˙mjufyFBڼ۹8ss:s8kpg2S* woC5gk76 x!U./fI{d$| [[Y]cc9Z&Øe5'mu22KN Tb_|o8p(G<&<*|wk,CI,W|5`Cg2t{w9K=ν 겭ocQ9 _fF說q[UccGQ9S#N_Tdwe fRfآIfܺgrCeB^Tϋv.t㭺KvFBxn2O=^Wˡz]f \)~Tǟg(E*fբSfhѐ+2:QUGxgenK37Hx^2~IWC韭|=M?oҏ⛷f:(~jJ?oŷbӏ[Glh%+\ #wO㻙os8ﻙqxf焂LOH`-`]$-5wknN {KPt[K=gf%| TWfVwb5:J=n|cK&h2cQרq*/OQק3gooJBxZY'>?NjQzQ|/@ 2yy߫ + Q|nf0(9O?oŷҏ[GV{Q|#(~L?oצŷӏ+Cw4u.Cjlhq74ou'GWvnFϽ ibM+3bLJaX.؛ǟ)LQGaX7V)2OOSKEW,ΐ|uIl [d+P3/?g&3>=y!qgJ=Rڒ+ɛj0Cʯ$oJHZd)sʕH1~w)nUįQ#zfD|-S윹Y앹IC=N*7rjIoSx꾙 I {]2Lc ~!} ՔߑLc2gS_3읹M63ޜ1}r) SWqzf8,3_=,>tt_׶o97TK򵽽6ukY$eYn!+Gk33կq9uMf ;Vss-nџ-˄e㪳(]muy Df84zE#_??=IGM6k<.^eoolj?:14/<m٧zކ->>'磛f`!#/ļ&&sԴ0E?X.x pUי{e9NkRet؎xMwKt;1M6 A$V}@ $@f7,^xw=ID\&4qhu~B0O֭ǝ껜oh/ZT3YFjD=ΨC-נU3g|i܉;p'<^jǯ _.-#2蠭`~;\Q׮k_XƺYZZ[:_Uk:l&^76s|'pl%E.颽.xcҏ>wҧjUO3uj=ŝٺ[h&soFuج؍XZF|~Vqnfܨ%L]gd^롇[|\+>5?zFTrc<8W\gsF-ܤ-vv˘g-܏jwvW9n*~'ivQ;OK2;g>)ٽ9{p{=NLta[} =I{}FwM7{D{p` ~67ݧ{?d>|sߍ~:*Z):wN:4w}LtL|LtW{G\ Oxsv bor?bBt{_{^{=_xsn;wg;{W >ߊu$ػ'ĥ>)o,s&C~7A7ٟ,db?ӑS$1xxxX'dڟL?&џItaFbcB{cbІq7M?].2030,dslrGӏս^*rysFplŹF LG{=7?.~,xCqcQHC:F=1b "0vƋ'1>Df`&2%&0 \c,0g6?V\-CwՎ%<^6F#@ UOo=s[uzsY Dkuzib4uԊ6ƽKЎ2,X*_M[#F"qc4xf `ha-helFHKb" -S9ZA8`6ia#\ '1vnKbqFx4:*#M7p]:p p]r.6lգv]bC,^⇆c\jJSLF:(H.z"vkcFY[):i1fnokCk08^V//ę?C91QMP8 CxlM[wa1GLUV{kZݭ$ڥ Cqw'ݑ `' qw<>ݎ>^P~#(vWeIrX۝]ݑ f7ډ$ Ӱ,]IDw-25+'}D z4Izt󀻈D;Ȯ ?*r#w;[xCF_bj{>s턫ǎ7_v~n'ăb35e\̴1)t5#e2flrc/ƌspxPDO1*'%]dcBfJIcli31ԔIDxp0O UCԽNJGMLㇴr&cTYl\\cK\)2{|3 1W sx~9%= YT31yP8ߚJнjc>α+03.3,e#͵4樹\|?spks kx5g/E 0Q?&1،7 #xc0V<8efd(`!]VÌ9bSi)mhG#SHiD?<Tҿ/PRE(#s`\bϦ\f?Sf!y=sDEQ)@7(D%(E9*9ui nmbCu <5O)WDҟSOVjz Soqf? Q%T"b*wEri̯1U=RD-dbBUWyc-e\50󹘹,Eʱ+T%Uj9(P(Rj!%k+Pr%Xbq\*|P?P$~UȧFO@,Bz_Ϋ!F jYKZک:گO F1;oA+ڰKЎeb)|*@!P(A*"@jUC<؁Rκ1`jC+ZЌ&kD֋Y͘*(Go 9a_U5 Q*>ը᳗Q뿆s\S,iu '*N8qQ8DC|ET(GKYb yy0k(7` )|1y)E(BLk?88㢆0/:puVTsRU:>G|y8UP18q"U,}lm\<3I`yw:S__}`'Ļw"~qL"UeƜߤ[|>79#>R/k ' }B۽>ҿ^xzۏ|y#>}g%C>;ri/ٴ= Y<1ٜC\ڙEb\$%b_˴wv/eq\oef.r{_f XxOȘ.2ORDʹՂVm?Lz=BK\ f8U^kj[bViÊf_jڷ:ԩFDTzVNݪUҰBڷ2jV?m3rT4Wt+KjV+F**W[ +/c|/cxLwU{uD5њbm >ưXTm5VVk~ mz[ Z^5{/SŋV&8W ZiyhVLwUl-Zw57fklaMm0?7s`fn*n/G*mnoem0;;ojfkh^52[{ө}5h' WXيj}gk {cYn-+~֮ܚUVekT>eRaMjS{3MsJՔ$֏85hhv\}(Q]քz[ ցlǭ537V߉vuGzNXqk8n&WR&˸52`_$[sҷR6>UnOXaq+*b+(zb&ULjIhOuĭ[jGX߾Ʒuʸz}5$k}jn}6udPFOGՌh%*aé\تgQ8*X۰u [ϰuq naka":#}V)SH:L_pYT!_[T[p(Dr [[pk=$/T}Hs`jn?7|l !Z?u[/uF[Qp+ U[AՃDUZзR0pIzt]ZA| ۼ/<~/rrsvֲn%C?пz *y{٩(@!TZ(KTL/w,J[WHد!j(0 8cFg(OM4Mi4MGa3#yn$d,b,T%6JpQFہP5D+JGqC@qD*D#FWSa~-kFj귫%Yhs:Z"cF4D#׸T:Ja1ߍ{bbk̜ub oE6]1]1bv^hcT:&HGEHLj8iǵ-f u9b|6bF!941fn_x9u2Y/9^tWE<+&y1QMP8 !<60~xlS#&B ؏xY]ͷUΧߢ{>] ֛! 1?יK5YI}XI81?#;Bôq1xc4y> 0G`8#}3( !;?38sx_yC c,5!q}b cc} d)uQg:^b8/ƩO+ H7Fpbh+p)uR ߁k'^`8F{+~ua;>57=wRk155K9ލ1J1t5Z3+mVzj &Zj#uLA5*'R+e(E Q(@>8w)%lȥdv? Yg|&grL1G#|,Py'v Qb,D JQJF1Do ee?S9b_-rٷ=dy{߼%̵y7D@gOhRZUFT2'*U*"G9Xe1բ4&CuzP4FՄfm\jI~XǴfVՂfՄFਏg V@Tȼ*_Tsn`Ec} ͪM-VKT;\Kłw,c,"Ur,B*QQuԉ增>u1K8&TJS 5hBj)gBu},}TDT:"᪡@jTMY8Z9hbV`E\ >_yZo6k6:696Nű8[XSʹgmrlO֋BmbP(3ߗuZ=c@SLLN.[^OWDxF49,`48UjU ĩ%n؇w;8yXF7kuzbuκXg].汋9b~E(xbz2v1[^~|ƶygC=ӈ&g2E<˽:Ȟu{^nN]m#Mn 41Y/ۙns=oSbލj3b7 1 @)J(B!^Q.7v/Pb,fW9urATYJU٪Fs_SXgqq '*N8Q301C?D[++++ :1Ì0spGYGYp5y$N4,5{9xoMI s.0whe(WPA_*U}2WUl)uZQgE8^#N1(}}]HrQsqgqF)u8 Q(NX0؜{}օ];玟\?P5T%*D㱑K[0y<6*PJ:wK`/? 1G1n{YϽr {\^\돹3[1>o#$! ?a`6mlڞ,l!N.gc 1."/%ڻDhL.ӷ2kuD/1\Ӌ\㋌~/>br Sä:~+- K_IVP\9L$m2ubbk+aMeh_; %n}$Qm$_'U[V:le#b R"~D1p'$miZ[ð [k#Dz[B` #$]d[I[-paEV&?hf~^^'rOFrg#tGsWc}a=Q]w3aF}T,zr+y?<`t~n\'^W0]^@7h3nfloo,Y=ͳYh.xoޮ:{*t6G]97[|ͷZSK~c` \}77flav-̬QKSͩ<͝|͏®6Vm ?~P)73J9Ex+@*Bx[JT*S婏,SDx\=UЍmj!2Θ.nbf:2=1YY*[堈6f,Gg&fnG)2'#KlMc} HwՠS=veKX^9ysϦF1* | ShQdcX7^Nj*o~Bߢ< vZ 8<^9,4N$Ͽ89D{| `wv>\ g! 7g`:7A{hwO4A2k}. Ĺ@ߢOh'/븘0鋱@'qJfUDxM.y@g :od>'Ql,Dgzi5FդUjdLbs mTzNg5:LgMXX/vocSfթb){<ӱY,r1bC̊bcZֈueΕ n=b'f)v9v=k}nW3' mmYR[׶rE.wV؋=؍]<=9fu޺Yk].Y묋ybNn減 5czXNNK`bxcƷ |1`+Ѝm؎yyxNϲ1=C;؇=ﻰ=jnStf7mb`v0o;?aN{[c'lb΍jc3b ȸ6Xk8Xo,'^`?kS{GV8)V0cZ8Wc syrAlftc+]|sG7;gqq '*N8Q=B0q!:DC!~1b,!wfG2to)qvivi~ӟvNRq|Mb] _ ['.(qFl?fq2֓:Ψl&Q1X-^g jˉ8˸38NxUX81c jf9/Y:Qp~BC>'~E>G~d?^^O8"1./%ڻLi2L.ӷ2k5L/1K%EE 2֏#>~aϗWJuoTZUVSJ[Aq+'U$_o?[cS:JX;qk&IG[ IXՐUhV9ŠFX9V*o:D1p)+6uh=ía5 N1DSn۩-$)p~`+r`*V>E /)Av>[|n>/ws(yrm~Dn=̡䯀q'f3"u7ͨ#q=>#hNw6se'mlv[$`'D},vnfjfݬtdʫlq| یp vd}l Y]ѵ>[7Su3n+eM*\zŜl0QmJm͍ڼho3wh3xNLt36kPULdȁ{|\z<) "}NN6'g8pJdap``4'x@ yD?7((1.;fmFfmf¬ƫ,^߬dh.>?l>lo-Ȼ%ngq0Csrn6f3okы^|5.fsm6¼٧ɗ0e% -el&f2XeR_3"=~3T:>7Vx_ffSk l`!Xrp9)PВ$&xipTו'%6N ؀ HB ;gYĎW`Y#@l6HbI{ʟ'Ui\yZi$ UR{Ϲ#ᨻ;5Q8wrԏn('g1_M }jOru?;~}!G;mZ++ {SbJC[G}bD'^$Dǧ2qrtSg'gJޯ&-};:鿎88q7g yI_W+~'/]7CSUZSM^ݩ*q)#/gt>ϥ>^%?8Ļw.AI(wWKVKW׬a`uc矃<kXѯ؛]{VK?j'?I/.}gVܫbtNL뼘\3]E1y,ด+vo_;x뉻gۢ/[\v]uץz.б:39|]:GIĹxww'G!fz_?o"ׯϙ眉|Dǭg|ox"Gb)19ڸU/sϩhs2'3[EM,P_L2~whYBU7 Ƿ~ErD;(o63_M:1q,`&; ?c&vMT` e=GvRx/oVUDP!ijJAڥěR b7=x{Oũ8w8~}~ Շ8 TE.ugcb}Cs9Ȝ27TZӈgOl{ֻA cr>Cxobv{'yv0ɬϛ">~+SC,c*q\rw:wD.tG?}⸦0l,d">#vV Ż"DJb|=7vj!>!ڊV9 ֹ[F8ɵуS\+ir Zx$(A.D'diP*.22,M)!Tc'{Πq =|~Rϼi&'XOW+T2w]g8byb'y򸲐 4HA21\XKu5Nslzho)He>FC=i8D.p y/AϣF4`1QZy5h_ LqGXn1 DirRJfT1Ԣa%8|8"Z(1ha= 8%p A8|\GT('8YGmqXTCsUרj|>~tR?=YA}s1HA UJVeX(97]%.U(q߹"y={%]e]!r\!r^%Ur1]elWU** |.s_{p 2A7̹O?ϡ܇o^e=!UO^L V8y*PTN+PUH Vrj~cꨵ71R_V[DjefU#+ $Uފ#R2TȪ"rEyeF' z!T+xkom໭z Z`UZT #+諃saiO0 _ k,{G^jY;n?zv ۹vGċqG}ǣ%=(ڣG#Q;hvW;ݮ|;mv]v`gQvџr={Ϟ{=wo=rO];<+)Nd{/ 1ت">+ԯ,z_a+(K-bnCHC:|Ȱ2%&+[ B [:GiNkxzN,ckyf=wu~W'#Q8?бO =qp6'p=;E 4N%_/y{2^ֶ2hie8ɺV1qq]8hf~~jd1Ld>#UP" @&1,~rՍ187N$q]HcHA2IHDO8q5Xq:CV팃"yH`~F",_ XOOO&.rm1q +U3qX%vdeP\ V"d+HD1 Ϲv>9-q0Ɵ<}g6VH^~ .Y+UXוӐ$#`/ΚL,ԸK֠5`θ(f$" 2jn}n9RnE~Q7IѠ׋P:t5Pq9ʰX@<Ϛ+e2vlkV&zc&fe ~v`:XC۵:%65yb=t똇V7'g>Y|-g]iZJ<·1ǚm#|n<,1fwgk9g<}-o)1J _F23 Tk5ɩEyh4ӿX-l!v+9Z{+ZJV:k+ZKuzkl$ZE\GF=PTY*F浑9n`뱎f=:1S!: XM5X+kTǨj]gcX] b72c V9BVbyhr,8F+q]D y[E VJ⸖36cr\r-ڈFvrwcY)jZZuX Gi17{3963B-ma7"HKRUnccw{ ͽ7reY,Wpc)JIߜ{fj^~$93wv9glG@OzӎчvZ1OL-эTp]Wt.ۙ>:_'D #1*Ƶ=i%ю=D1zכXO;A<΢?ןy|[/lAoq?zhG|3 :0#:1R_[Z59kÄ_ ii4m{O~N-_o2L,GY9cӑ˚j&2Gụ | D{GGGIuF?1:NN̊3S/f\5i*Gg#s UM,ϣ|x,r/br1sIF/,eLFZ2\uJѓq=Tw tEtWWOJ{e,We̳ jhG3N#:?Z[%Eyq=gY֩q{'qYY<乣!06~Gq%c/˸2u/c2{}}> Aُab0&nb+r Ok}Y3vdkcS|?/^oxO;g|wgtAW<_r]TGGIuF?0:NJڌՎc]u?c ]sp|]gU*1m"'x7179nAH8ߏMO{QU/T=.цu7юWcܐč ܔ@s|񗼡^^d722R̍d X^m?{`ߑ>gd$ HUQX-j~;1NWP1I,&c r}(M?C 9ēCӈwOcOg52y.UHEQShϚ,b dbb *bL*pEj%2+l7Jƹ{[ ۱C.[X{?NiS~ƺ&b1댩ȤL۱Mn~;bLt5riǘAL~gkY3S͠}#WlӣЮkȉڔ80sש3_`\;cLfIjmXci?Clcs*w*^lŹ[\9V݊$ rX5aeحVT|S [mTs+a7z_u+n֭ԆZ:2k+X _װ:ZYUTS+>TMGɪDJW?S?gVyruO[猯mښg:f| /ڥ[tknW:Q'údX kA1BlҭRV(d|e2*ZEcJ@G[uLژ|d {&1-~QugОOVH_uwU㫍h++L;-5_O왴XQ0vXa3gϱRi_] +n5V+ɫ=]{aj$^H+k}TVêbXAt a|uS nVlu|eϭ%%dz9~gvn̩\Xŕ*Wu16[gskl.p\=|--~fkg5^]| IꚭيU\AsgjfeRVXǤU#ZvzY).&l-Qk`Zu/_:_+qIZeYekXn*nekVW*V^u!$pZַzEZn=K-+:VZQקºTl=*EuWTjUlVlʭNW܊TRC֞z[cXAMS֋[ց׀DcD@_ +;nVcnLPUIuu䭵~TRb'Ub_)BRH#*#s)(a$dDk%nmTTHo%~ʇxrՍ*ɪV1 ^MN"ˬ}ۋWls5 b ݶ=Suuwm U#;>п^4ÿv~&7*<ޜe_{ū&jUWCuUUmUǍz>׻nX5;jtGmGUiߨo;jEՉ)jIW5QCUG60uQa6{➟m7Q-bnGPhOv=-1R MEь6fsĽh{p7Bs^L4zM&{11k1k1k1kw;:t3jܗ+N_F5F{DUJ|U*V#@kiwMo3홓Qۉ~h+@k5Z-p]94ђݘzi\5cΚ1w͙esԸ 4} }Z^R}54b FCіO[kKm-*Pux]m}+j<Ǎ]\HgTSS}}ToG/GOQKJN:ʬ;k&p&ddLLd!{^cƚyo~xG Չc0j2ZHgQuQ 'w 7 `xd\Eb qu;xvC z|5XU[wI?1To?ߋZMNFmQu%7߸GોD5?qDT_eQQKG}%RQuUs0:#xZ1.5|GyZ̏ΜQuQa\i_wW+eq.jMD5V5j|oL_D5b2j+$++pUWIu?]>%Dj_/G}vC#uZ7ƎFQ?_㦨1nwFݠ~F?=|7r^}GVNqv;j9jrU#ۢzwu|OTe,GܨnR?'FO1Pˑ.nb7<纁E =QS|q']/jWNw}?^7F$3WO?w:jµFӨc$&u"}|u;۴e|:4<wzz#^')\7"Selytfx]fbx@=覺'z5}яޯx"ab} 3|cO?9&҆Շ6ު.&9l2T*;;M 2aZXŽ1V5xTW XKnz x5ܣji%V`xC+Q#\k<*xǽ}Gu}L=X0'âq$PX|K[}#P̙*X-z9z:zX%+X2ѕgW5uf댛fN`2 q8ya <^JAR,`/.Jyo8䩅XU*TE^NTZM*;rYc 5SKb7s1k<\H"\D ēOěO|vf<+cy'x]0p]P̧=c`cV.mwhyOsqý-2:-gxxlϯ{=sDk~۱b 6c6bcXgr>YUgWe(=Zs)4Ji1P{X\gLڛ\ڟN?4F9đC<ĕMFfwg1,ƓŸ_6fٌ;0K>G?q \cn# H\ Qbg}>g(=k؆؁yn7C{c}߽İX2{k!fL;D.:q`36a#6`=։\<=ɽ0fq_fcqcbyE(QKhg -E)mO)-e8[Um\omQi7P\ڡ9)Na;wAӹn:ma!|U Y(Rj#DK.TV{^68E O1bw5뱁[۰;;`'abnvcs:crvb۱Mߝ،M؈ S2>ɴg8'Z&8efO'SdNUXj'ʟ'*:-X)=*uiD'sO~7ݼ٫Xb{>,\Xg`s`{+ID='g_q O;_ٝItfg_93:|=Sy,Ns8{&љ{=kc$>c|Mj|'lRk% i%ey{9{=_\͟4 {n&`W6kܓ5rN4$MS4OФw-+:CS{^ƞ rIpNy|cWbϮTJh.3r'WrN'T쉔$JyqU N$`FܙyG#LGx=f#=˨휿\س| R 3Dx>"<a?g:Ğo?{>!LB==h5=_`$:G=7(^ ꉁ䭵n*w^ 9#09g'+SO}$ud$(:=sVsJ Ju~~XϏmޭWTu|uqݗ85u,<[/s+ {X]T [=Oro:S,HJgA||S/p^=3Z"kܩz|Uq>+Vvv(Iֵ$gĵl[N\î~]vm։j֩ErWUmªsbɪ*nVju|ZNOV+ENUVm%:BW+^uN}Tx֩Ӷ&֢:tX{5\Qm9yTߗO[u]U_INVqʱ[5>'oJPa[?SnUt_ .==#`W;2T p5B(5Zq ccb.yqnmXVUjgTq訵uX5Nǫ *CM$5NTdҎEM9đCL97H0k&e\&cuUb*Џ8&aPhX-ƊUj*CHw~60-ܓ}؏8:"F>{78&^77Y0^M!CM$>3&c "YV9릫\P3io-稹o#&k>q QuDy;2Oyq@!uq1fӷ5i@.f.fNfnYTL51 A[/r/#ܛÎC8`?a[nM%"JfT܌C7'f܌&Y=)p3ݜ @ww'Շ;ywٵ=~w>$ݑۉ;ypNRnw[+s1)dww+ah\~7v[~[h>{D6پB^ݣ{SwOZWr^cwnݥi]irZ%ۏڽ\`3ýe7vGDY;Ý`]`^HbKKst^.v*~ܕ+#1;{ٝ\ wnG`m>-ܗ1{ܽT*)k;䭵n?-wM%G D}{YܝR;&Dw@v$$jDU͡ 5LHqar.+G9Zݏ>#eF~`:;#v)Nw%H]ȡw*1SZ贻"SvqLtG'2C-fݫZFfGR)~JkڂCڡ=Б:чљ>;gbB/x#ud5Opz.{$}[ϭ؆j)Dv9vGqdoT;G{OtptTTg:1vfvk{1[D;!)=dt`ΌΪ ܦ~OIlZs\_<8&Er<8*s$ꅞE6|oцv|o@8]xƹ*ҺG$77+:m1/+UxE/%<ϝY8Eh$mw~OuÃx]Б8ks{weyEūߦ׿N;A?oߛ&KoC2qXb?87}/^vF&oz6=aWEGA10^7~   vs8CہNҿQ?b-hy~m#D:~W8>m"Q)_N|xݨU'YuQ]aƜ^UoxZIdXдbEDcQdPBEǼȀY\L4 )\3h#hs<M"FFFQb4X5x52DLj "YLt٘u hk!_}D<\ّ7Jc<1 \ꕾg!.a΍b+D TZ" |Zc#橹1Xֻwo?;#g`{c }BqޘgxExOUv?ƿ7cbq|̚u1cˏs?^}MLDdЎ1Gck F/%>d_yc^w6>OiS>3>#ψ3kgψS)s)'|gnjoW(_Jb)VqU/bz]K Xڊ[WiRIX/q#FGT"aՑ*VBT=ŠGX+a#\tr+_JFj m4bn­Wt>)Zpkn!pBBjʭ,؊B|Ձ^*+&A>x^ra޽|y0S ];0!uw3/%ȴgzWszSys6?w繓G>I"Q{B >Ͽ*3Vvj6nm7mYl.dgE36m3 lsL☌rf\86[l36Cf,hf}' ̮.q26sfl:dyN&ͼ//ÆW7:'&j6_js6/AgVLyCIOݴYMɴK0#8;'6X1ܢ'0gJys)sn>0Q.ryr?7.,׷w[̠ l L 3f2c}nf.4|īdĭ+͋yTsx乻\lnl>[[sK}oٿKˌ̹9Y8ͼ\'ͻ<ͭ\Z?+Ll s[9~wb%^O\zJ3џ'{"&~].Q_zxXHW/yT= !qp1s1"8xGh2_<dž3!hT+Ւa.HߨsXݸ=P}{y}b :9_;a:džFws'jcxT/#0RRdʸ+[x{Nw`۰śc VcV`9x2b)m`1m~! +~?x^gq*b+QKJ2a9V`%j<n mm<:wwVxX}ߌ|m}&~_26󻒱ߓרi۸J]P2\opu4&6a%zN|풵'ӎk 퇾)VK9.ZE܁+q5V]wJ$X2q]T)*u%P;.ZץXV07U: obT]vIW+UZ]ku[bzU*+9 O`-a=6`}/jc+[F\e]qTW'e \*&{ocTlqqZ+ԕ*u&j#c ]Gַ7NUOe~* RC`% K+uX"~Ś~Z?w\?*XXVXʸS+pKv8vQwjZHWUU7jWQVx|3[ho ob n!6/$ne ګšzZuv --̩Q9|Wae\LҞ%κ0jtVuPx}}񕢄= [ ۱Cv'3v/#%{E]Uj&j:\{8] UoVWU*X^Tg̹QSblF:j:g0(я1Ghkz==}K%KEm/" ?x"˧zvm =>BCc1 !!8cCgC}0c b- bl<ʸefGJrI\UVTl5%$\x$[YKX[SOܺE)HRWI[[pV5 FXT,W+R;㛨Vq!$n$o!ijFچiuڅ['&F!zCB|M!'T\KHm j"4t*"0N8*ۜ| 'ʵgDs-zk'y;O}i{*ȴW]ode,zOr$Ρ\y'/;o0=*i;yqљl7{fS;\ujʊ26cm3jJuN96l̗d+$_5^ a v6fzWD36k6k36fomflm6к2',f[3AF5jo%ɻ9ܪ? s6?j6Aü:'Y>Ǚ<%rf7L^Y_9J04&0v JLn16k <yS˒ܼ<<ߋ%%繹fw7e¬,^sy2yx6g3weΗK>526\]et%bnm6ՂZwvpa.qflͰY-W4?͙ϓ _6RUlU*Qiqi0^MPb|D$oH5QL&ɘLdlFtG.Ě͈pLpe\nhZFVhi(H<ƋW`l1iCbj5ãbxDK{XCk LLUBtҞ1C<Y flb :kҞ1@L:#G7 bqw"{+|Fs8+238M;hmw~O0N;ܞ`O2'܋SܟSܷӬ38Z:xyy/eo*~_C05#HhXK jɘB\^VqkFM&nkU5JT#.~xW1KEϋ#p#2ψ?x[ P1$]ދ1{PXBƇe}x?{1CwX#(6s"KƝ\SꬴmX7㩶\6آmj3*3؝V'HiUZĞ2, 1V*ZE߫c51&5ļxk6ل؂jXc@)%$j"mulQ&+Rw)<=yq')VgD>|޻O Q{(`*a R,s ˱+J^zkN^m/mTLL [YuFf.S8N2Oۼ'i9 ui>b#fyXGZ/2/2'/27X<_e(R,- N8TnMU,5^lae3 nFf,L N ;v vNvv;v[OK`wsvq ww;dv{Pn;s[cwvWlw+ ]M=i6wϛh[^7>ߵMmk⽫gu{TwQ`Of,`\;;ݪSUn٢d{T?ѭ3v3}erdtϘl wfZxwx.jW4sow=M=(\Eu@h,:^ @wܮUuPETMڮ!oT#&P=1@oGڱ0՜~oSteL]_Wh,dn)z%C}i' `%o?ǜc1_}W kޢ&z2nMG68~YYđE<*~&1Ϸ6j´j-y=ck؍]Oc'v`;a+`316qFl :\Gc1(>݁; i-clM)vnhxN܁:HGM@uTn0vbڮa"\olM7FH+v[ʟ7yg6*jsY2QOӯqL=E Oq1h#+}F\wQ?/xiUHZ5EHiQÿ_JZQJZE=wiF1MД5ii3.b Ϣ 2O|&},2&11 w; hhLFv/-M3pc3k!j;qQuHZ5i?1ƿT~>>tS[A]C}k듈y?}q|q\@#X\&̍q WkԵ.FqbI[kUWrq;.UXE]GqBQ_j:jЦ:źurO+Wju 1UשocUSJ1UK\US\㸚6ԕB]e1j1vWz{1Ju^_kuku=k*thFQ׳MTSڲя՜שk_?K%({fVY*7;e3SH%IgT*'s sMLɣ/V.dL2Lnvfl6)$d٣0sd3E6Cl:$}A|o|vN[f JG\lOEr7'II>4{r9b76okaa<1֟+a?GLPlb LdLQS,^gdјǘ&ρQޚ*)j*C'^cybc(@!P(^o,E)2kcOVS٘Z*i;~+}*f6`6juLwLX9bnT6-*a"5C 98cy3ÚM<Ŋ :JlxuP"7/aJfg%Rz;tj~Ɋ0faXƵ 9Yʬme23[6Fh{lm c>e>WpP5auDLGtOw`8F\Lj/0s1O<8f3/FTƼ-Rr JP<"7SEXL%X"vsnb#W`(#2b1[(%fc)cXJP؊P|,DclL7fОKܟj+r3k]vNI?;o'_}i8mgsN 7NI;%EF9^R/71mtը\wb&faB(X,ͥ20r\!^KE x,47NqI[%g QȜfO|kK=Clg2 "1sxlL7fОK? x^85efc㜇ȓWT_,0s??y[b6Yfx.0cl@"RsΞ'k#|EHh%*_s|]5O|S H cZ( ||E_V;J7Lq)V=A]pWdWAeŢ,IrO=rEvdSV^Ԝ>3SS~݈E22ʞ3}UdBYɀ{|gt9~4'~#RίD9xL;$s:rLc`vcrDYM{&4&LaOXJ0Q )&E1lF/IhlDC2S ?ӊ"M#n郟:l)B QJpk ӿo>ݛO>$/?Ň_1t o>Ogs^wd?obn<ECa ֏n> U`b߂.Zzua3vsLk^Me6Iqf$ՙi,2 {&m"v9b3fZ{bf{R&{0hO҅ ɦ t3afs$ZVrF5幈]3`:,CHGc*aVG&DE{+VkowC4&h m`᳒E 6]mm$I/ѹyՓgWH yyɣJلylh6\MY~j٫57Pi uhQ -fhFhC!CQQ̫rb?ܻ}yZcb؏u.x/(s4>wgwl|W`AbEZ1m`9.:5krЦ]ԯxCF5E>WZsA-Z!0A8yS&z탽xݨU4(kCc=?B[ݿB>kr%|b g9|_W|:E]I{D;Sh?;y<Bch?C|L m`9/QFOQ+v>/ؗF5qh9Oh=O`N`N:i_c\j_a^/1"E\qTZj5Z-ƤA=}v?2y ޣ#_woq ~ۉw+WqZ?w5\Ju޸K2Ͽٗ+ؕN8mag9v.E&?rjP_G{Zo@Fo*xR6[J-Sj|uըS.;ΐ+i;rp).afkףrݜTMBq}vv#=|g#6,׮p]]il 'q:?v.qnÚk8{+{0"1y2sΞY˄il:9oπlfW=Ԑ0a] BX&1,Em&֞6:a$8,:Sa2I!<]DJلlh6̧d qЎ+tA?\xM&3P60h&h|X-G qMõ2!l MFYmra|m=fbnĚOᢽ\ ~EY[–GQF{ b KюDxDX,, XHjb,|Īc@c~\MGr9'YO[F.ϰE)$9g3d<=L (m9^wuH{M?ϓH|x%q^:~UWp}_wοw}=_ k=W;e5wt'7;hKg[#_rSٕmVe;+H*N}ױvuR[C%%G40BqjuZQxTkz6BYjuisϬԊ%bRk=#|RZOv%)Ej#\)U5F]z[U%oWW;UJޣ~&D(9Y](WJUӕ,aN[O?E(LNV:C_GYmfjQjPr:ƫ"MQݮgwVwG~UPr5I:B)$¼|2էJNS/#>PՑ%~/B.5OjzXmPrzM-TD(H5J.Qj*P1UWJ>S%QϪe(?A_%J>k3Pϫ0J+`JP2;B>Η,{"|UJLd*T%To)=#|AuS?>%uzY]:Z%_; 1»ڤ.F(Yyg.xYڋA"~l0??U?Pr*u%WT%ةʶ+l Z"\&|UmթJfu_JQbV#B?߆S',iv2=->q\fZWev9T@u½XkIWR怍)M!dͪ*ʡ JFUŰ/|s!QJԧe,@+p/@V?+Я_>Dߵl%ʅh7썶{ŰA!9,rQV!0Zܣ\m  u= ؍0ƆmFhϕy<2uhCa0DrO@ԡ @|BP %PA9uUN LоkD\7(+2VJ$25&nYwZ?[)eM d"YI&,2ٳš¦tlɲ 6iІ+ǚMr\9F(" VC1R(#11,̻6DEG,sl N,L"kDO@ӜN@_' s"*G 2TTR~2X˄i712V %k1UP~ˇ|"M[kiܡ_[,M2)?^ Yxa>Fak9,Gy\x+!se"JʓMV{rX)| X!EZ!|!܀7V&Bf+<-@}<̙`-og߯{e{IxQ9{AXTa ^*AUFm= Y蓖2V.Tt'b/ }Ϸ~ʭNԥܼބ70v[6&<5x _%zԻC;:{: "('뱦c}rPzhCB{Yh۵ V $ ¸1k9 Fb^XE>އFSyR;rϻAvfGH%{T`|F99JʄRVŠYs' qM>#+$|3b_ N)7O[k͕kA>+BVj(KX)_c~ Z;5:Ӿe +%B*r9=G_GW9V/H%p}EЏ*ID]ߒ u B}Ra5"(y$$[ v} _/x9ʺ0- 0BVDǎy/\dn&o؜:ׂYdnq^d&3<,qjw˽$~P+yLd'Y&~P%Jd*K ILI2ELHC&2LL>"(ّLnܴ&UNW9v:cr?3r/1ً\YKl"Rt'41RĤ&&)1)IHL2'"4D&!4&0I;dH6La ^&;2oF\Cf&09/3 =rxP`!O|@C}IRիP*xwT#BU6 tPGETvy$~ap w&$BqÀIN'󝹳WO|ζof{ %R/!ֽ'l5gΐ[-Kx3>j Y|/Uk!~6X~/+lqhgq^YVߊQK$ZnfeB kX`ebޯڭ}Z鿑8[$q5yC߈[^V>_K"گQdnhQbxP0 q(fy<%^w.W ęC;R߹;zW/gXEkP\38?ch3DSJѦ?0鲲CVTf6%YE+)TMm뜕)ׯg>dD,<~}77yɣܫH};('/pfsB鲺E!qL?ԗN1Lnj5)K=~쐭=gL&&!+Xg#.杨nέBέ3sS+QY΍\Qz9t޸`\}SHqɸEiZ#q`~6h$Íڦ"X <V!9F&9W{\W"q m-*!J QDbQ2UmL6z ԣ{s%1,Lܻ^a=v=ƫkdixaQ(u#Rs^GJa_T-&9dlyV`c{܇sju1 ۽9w \<:y*hkփ8Ӝkp88F.}܇עQ5P |%/F Sq'Xn)EC/BRb榔0R֌ѥU 1q(ǙrBcl8yR8mgzux^䰩eΘN1^}mѫ0"9B+:&8bT*#[YL^UL>zƭ) ޚ_/8g}ຫ z`aY1xUBŀQ L |;s~ujb)s|;~N߿ɿ:jߥ z=Ao=(҃2>}(=:Jo;;DmۿQXBc]w>v˦1hbW=ڿ1!W]2^%.$Iu-Y{-4:sد%ko8@\Fv8#.$IH&-EI%L2co nW!ɵȿbLj=g{2V'c%ߗ@@Ε0<(?y]~qpghw=;G x#~yG>}y\H}L ŭYqRNэ8cQ|LfJ2NB"kBZ8#.b(H"L|M.q0q8erY%-3u4iD/\;*0غq8cF픣Z8P"(D#F&֟j~-L^P9Wa\5qL{@X^_ja\Ov"(#1F,雄db/8g>LKcli3114|ҘmvWjMO~sm.$IyA øj\37h|F_sb}N\[cGbhޏBApc.lYSƐ1H;e@ gJ"hOU{d׼Fs ueA|I֢Fը/5@9J r5JPB yXEd# \XF]̵wce1%X{ˌYaOl(9*!(DQR>@%}WK|$:LW}>re\YrE2K_y-8d]Ca~\T;L;4oJdO幢֢&{j.ejR,^+5t58Fcryx17%Ft7|5ק[mw5:zvws|}1f<m|KF2 &߯||K#_ry~oޡ7x[⏐o#a#gq0΁;1a#o92o8anwasy}89qvUV4 &WL]$9*V!N_fRsLpY[8kz"ۦW11 x@8FWSUYYtT3$&zOZG S?\FY L^Lo [1|BvTފ`t%0U1j;v3wm{WRX{޹9vsܹ ︃aW32<(SɃ`:;rn|?k>x;1;k玺?`A9+Gﱝ գԁ{?j7Iwѓr+UAbf+G[f[>VF+ڈ6YFe$IF&WOfjh>kF(4/O#skdYi` lZ=ke 봹Ǩ|jFQ*>QE2- Z)"F\H4LN4(UZZQk>Jg*M?%8J9x!ch#^ĴU;kj(N6b8&C<8V.$" G50Did=lh6ZV FnDLe39-䴥HҶ1m{<1[, QF$q"p+sɧ?(26[Z5B Չ\(bV糐cy=n,X*` i0VK,ͣesL9J0cgkzl0тV>06o<Vˍe6ƻ|2_c 崀<6-h5Nc#qMĶ-6߶KMӶ\<3/ hA36`=h4оXGC\RXl,|<GY@_@\@n5BG>;IrC @B $7{)4I6^K*VV{ ؀ii $!ꝻBݖɖ)K*VUX֨j֋br(& q6}'ukZJm>b\|zQ:uxyF`8  ȱ28׌fLV|5Va5֨뱁6PF"a&bRwcrh"61ƞiLl:XjV-hf[ a4Pg 󥉱jbܚlb f1c(KXVcZֱ^h5ĝc#׍ 9V:V >jR+ia?r<)/U ﵾ!yvH_/Пy8OsqPcŇbM Sp~RŇgJw;bY ?06e{R~װ M },3CBɡO3p6QUy>apaC,CC T5RUfJۢ ݑٖgxӖO1L|?I;>I<#`6~[x, M kQWƩFcpK"ƭu3i1h26qǷb(Dݭi|0*ndj.Q;p=sr5Ygi\y6j줋--lAhkжEbZ"vP۝l,C犭N'z hC)r}h3ryͳI䰿oEmh= |K6ӧ"llP:,r8bBft982j/rϞg>f/ͳgY;{.rfkl\"3q,\=5fϲkf5{6͞Igт3{,?[ϒEώEό3b,?ՙYflaf bśJt*zʟg虩 F9(ɞk%^)S${il#(z(/ 扂9!o>(QWAF ٳG3GQĜ%gY!F(љϗ_s@91q~y5s' ?3dEŞ h@OO0=әJ|91qv:Ӊ\N#7NFwk?DDt2e4N4>:v;Y[1>GZxd);1 V9"юN;:ΌZw6NG=v;r Ӷ%F+(wʏ3Gq))gj\s$]~m=W*[󯢯{Rv_l-Mn~G#R?D7r\^xrtCwzKޖ+ԕo1F^]RїުIaDQz׍/Wug|~?l﹎>׊A?zQ D8V8V^l=@1ceWg6u+nyfuyz'z75'^tnx)77F\ꎵLTĥ]eSD71Sy~ ,ʛ`.Sen\S;z'z/Kl_Q.}LN^o彷w\LƏCڃmmqq8,~@8x:avW]}h>7Ї>c< cLkkpxy?~ S_Uw<7A}FOR vys{>u1spB!J ]U!. <㢛POK/}z=B곹PwPMtg||=3'z70ckC}nrϳ>k9GuώrV؍a7Xu3ť<6~H]ǥcZ\g>>:p~D=vyA|߷L2.R7ƛhk* "e_|껀=׹}gog;IuXapY oe5|&je3B, ]vXܩFQb3ZXcN$ʜDwݴns7mL{'a3yn"n" Y@]qjіQjdX:};|<\51ELL^,g+"ϙa),Q fH;sD JU*WRvTu,&L6 t4԰gJjJUie(<)A1$;{DǓy`bƪ1nFe ;a.ry_yb8u{ x_"  oԈg9+s}u3cQj$ew*quz*(\%(VE(d;@SF`$7Fі1"_ +(Sq)F JQ1PxVr.w7c`Lcjj&lʙ#ẹnO5GUe(E QBU|7KF˨3sИ"6g~9g7z]qkqY[c[ \g#80m܇{xl^qxG8Ü9Osy@܁~?!5eu/І(Iw\g¼x8\#Vw0ϭ;)BYxLP$u[-SD;5HLŪĝ.JU*WDپFLujS ^A;hSuν]g-Z-}e;$ʻ+|UmT [T[E|Š30AWp=4αXB i⚠ɝc<$b>,sTU J&1P )+{7;QBcPTjB(p[T+瓱RDvkhikiZZZZ:QuW hTMӬZjU^cX)VVFf>yUWu eƪrcTjPh4V"Z鏧Iԫ:rԢըB%*D+cX-q08.FjE~xV`9*ĪNffh3K__}ӪVwOj3 mwܣwdo|srO82 e|)_I}ou<^0$^r7d;Ƿ#y½7ؼ~; p;985,3fbiLqq_VGM` '0ӉD8'1>>d5chLt531K5be)H4Ktx SOy[GuԷs՜UD9_Pb\! <1r)#G<5\Oq=p$Oqf?0V!_8@9EG qGREn@c!/rsxaϸ{rP#xT=WO{98|Os|}~AuHݏpG3_)|\T{7l"v%u١mjڥ [f]зMج6V6a;IZ6jVK56c6bx!٢ڢll0nvݵxfz/lm;mxzF4Ӻ.Džy.OYyr (^c#Uam_G(]{.8Ž@}@?ObM}ǏO<ݭwbݱcߩ#ne5|-ֽ3b/^}0=0ĺ祣{]"s9=.D#bǒ%GNGwĺſž%wwwwww$~J+MyJ=*)})=)ޓNľ$ľwľoľ_$>X  }?Gb#N wb{ N .=|#&^  ^>އ~K/ܙyW?ꎄӽPww N.0.*u7AGwwttA>Nu@bĺ{tJARJ)6û EZw D-%` ?73`+'$vt@Gwwww+[c{xB_*s+{&?X_k_^^[Q݃+ۿfhWدV_^H^BV۫{Z+˃/[JoYU*`u^r;rvl{uֱVWJkUj«c_n9&ωͣW?ϵV=+UΑV49 DV/'ߩh9Xlm^_oUN9~C=rdU"_\b@.8rxzxb8zp"+`EpJQ`o߂ѫ|+V+{JXx݊V+v +r핸µWo#WnնӬU ZQo5_E\9ѪgJMdl^$lYck6tvumb]_ؿafW+jWFWګf:\){:dQXj'[OkfZ#;kb;'Y5&/"vjWv~elCԊؖv`UJ:kkIkbuWͫ5r׷k[5Mc5oVWWZU?JIJSU__ػ;)WpUkϕNk=5Y;5:V{ k֭h^_:3[zR5"֢N֠N_w9MdibM+WګLy+Lu^U4z5i+IUGNz^9z,b^-ZJ^!Zi MdEh3BkBrv+B 3Xi<sU39E/ŗF}>`\iq[_ήLk88UL"k8;Uث=6^Z|Zj3XiҴWg2JV_"V[ګ,w_U^Zw鯷Xk*5ɭX#OejʶU ɿ[+!?zW8ګXɘX2Z>#>jxZLv Mwj%f-cB3,wll1.4G<5_!]-P -(XlYB=be3],,rGHw4<7>ҷT0b Q1H%jn?u6ݽEpo31KDn* X"K(X*n eD&mY56{P_,b?c! T:ҨØy9eQGQomϥݹ\M)c1s 8#E(1JPTrT zB=#&CO8I>'#<0BG e#}:Sa}><00·ÌaكOG9ɔL=7zxx8l N y(9y8цx;1hh5ci1vyixyR5񰱴7(O_JQ*_x\=u0Q'E.S)O9C08K^6PbH~C_SWWimT5>#0}N=ߑu[M "*CÅy6{d;)n5ST5MM\xfZf lPb1]bY ,el\2(?򩻀vЦWH[ i{ah0g.ҟ\^e;4L\ ͤ^O2UZjYbX$YZn ;a[5D2KnEXz֓ݪ2rU*."QV,-e\cJ-%bBQ DGE2d# ?je\`r#O*Z(Q(y\U2{zꭡ B> T>r B&2%XL"QŹeTr(1zJs"|lcmRVh-kjUήZk嚹kZkC+ъ4meU|GDE-R-}-5cTpk85·ΑZΛZΣZΩb e."0R^@iFh_Li;ϧi'~ѿ4F鷱1XX,d\a1cd,R-3R,ac1e(׳rXVcVUmj Q,_R2El,S9EQ Q>(QYʩǨ P)ZT+qZj bAT0F9cSJyesl7^kW#[F>fį:N XzA~/ylT^f7MxM}+ )],IbaX:C,}L,ǻdLd!;.ː\!_mT1JǺLSQ*WE|մRkZ+3Vb@ [d"S6JE}L"UH eIX*\q G,mxO9gXD(f{-g فE3v?J4N;JgH탤N#Ry?w;uQI{tAnA'vnۉtxt>o;LOH;JDO#S vo[dmPDv:VfʫeէS΢c=y3w9 9tdm'v'v sV z܌ϗLy}8Ȏχ#sp/MlevN{X oKu׺[jv6kcg9Ffvۙ䨜Hn#[;}Jhd6Hd$6\+-j6]W\R_J\4:,~ioGd~v~䜋rXxo +V꧟Aڙev$~'X%W{22&L2]~dvhAh vfYafX,023@?:"9_d+߳=?3 [jwA^w:Y].\.:r[mA>g;y7'm?lvZiZ$-Vvى٪IY)Y Ybk?jFX-5%_㎸ z6',1oqmmHߍ৖s/ezF9T^3R0 h?^Ch91 JFgswWŐkz=,Ya9񴽩hyK$VCy"hyrF6,5Rp5B?eq%&5$514;>4++B364#UI(GJQ"yEO6(#K)s SBZ@yICi"=4_,ๅX$\,e ,Vː\QO> P"|9*ٯS~=5RwKȌC«\ƅ"-T%qf_qu'H?nԣ.d|)B1JP*1[c?l`̍z=pXTVσgOJQb<˾9jy3_R3a"(@t'D_^;U⻈Qj{ԈsE }OScqQ1cw1u>S"r(/r3)?z2/z3?vdО ږALڛI۳Cx^W(y{z_#<-JhC*C91*ijxYX%mUv\^ i󜑧rgy~JEWϩg8!r3C*tsayz򇰚NTGR*;cc!u#v<6-Q*kTz ot?gl 0>Җwǻr^.]w9r帿KYDKDEԵ ^.lW{2ޣ)}x>m}@?8?Ӈ9q.Gg|K_ߡo_{/;=,Gm7=RcVşQE5s5I|N_;g W칕A|=9obϓt4G:س$=#c#f;`&#Hf-bX$oƚ8Rp/e86"39 {"yTkn"rŸ{5pys [,?=` 3c? px>~IDcy}tN-{ۙϬ|=:SO|D~X8uHڣSv;];[2u?G3F^v9{ZYw;~3WN{itl'/v6N?'vm'~`uJ鴟Ds4K#ܨ82)Sb?`; ~[m%v맺~'Vr'ARc*+W/aMH^xOǮW?s 2Պpf~>g~䞥f[|+Ȅ3H4O.O($Nǎ}NZ;1*kv#a`"٠ ڙ`y`~B9`tgg/:s>;;|/:Fꏜ4ONb~AڗhҗɞnǛx3<;K4gvѹ\d&gq~oA+oďNr#9;S8?}ĭ-^Zpk~fhAvVu&# ׊ί:KZ+Vr?1Jtb/LrBE$r3,?n 1Y3ij)fxfc˾02SGҩ36Ӟq]@8jJuͣ?x}ͣb.ex>F]jfi 1Ut B=.W]H[;;#||VqΥݞYya3a3EE1$?ܜ ,|r~%әvRg.2R/c(c9D Ơ3d ~ -(/eF%XEXta>\1fQLʟI=3oΠcAfо՘Egчgҿy4XhY-b1`ʠ=6fF5@,hg!"K *>dq K|^7 9;:6mb.1[(ߓ&;7a$c㌥N,g\Lg|D$un1&Ϲ<7'leeBasŸ24ȕ~wv]:od>絮{y.tua+ڰkWcVRF )PO]uԻkr-~d;ͮ עZVXXԷ [xnvnKQ~>(+3KWc3zߌ=ze+xN]\+N|NOy\YuY34LL;'خ3,[,)ǶY-mݞsqyuh:ֲF\p:OϪs,g,UjyVO3U>EϪsy8_}W//V%˗/*qXmYcY֩؂6l6lӲJnf&q

ù;;D[ [9ѶYĎgN9\u:_}.l} \@/Po`jS[f&cMΑoU;VZoV)V_, XkY֋o٨H+{g=^}Ⲱ}| tSCTT+,WR^n ;y~)o7瑩=nں;.6lEɶ`36a#6{]VQV\ya\n;z޸+*؉]؍=؋{p;_J\qA[BOP 2\*ЗO?}b께/|qlAo6ql\ΘTwf@OՋ 3Vѷ-j36a#6`=ZՔg|OOPU7uXM5X+.K껄z/=q(}I۰]a6ޱ[ #1V7&K9ƍ{#@]7P p}~@n7フq͜V1A!lg eaoP^ R~*2~éw8Ƕ9&6F{Vg&vҾ3@ Tj!m6U͌P^ Cg,VS:<1@Wp}Pq3z֩҆f>D΋ jeec.vZv؆X.ZxlS .cmƊ 'v3>3N~]g>81NYt9ߧs s,sLs|ssܧib%Z"ь&4R &3~Ϩ /ZeWGyhpfLʘIY({u̢;gӎٴg6mMg^{l6l6mEf1>839fҷ|O:}߃{*7=vegZ3,gX3*l?̞ě59ͱ+,k.ez܉=g%0cϐ3#?z3 G0^3ilE왊ƚ8Rl/n)咸s5`#zžgMĚ55\5=%t<9$3fـӜ 'ZI}WrtygrpޯL}PTAvWAQ>Jcvxku3<'YOo? Nc'r>lr f׉k(k?Nt::V6)YrIp2l'~l~$ I魟 ȮxɫF'^Q B[#sU;; R?3Q? 3 jeVG'fbɥX *I? uV;a|^+O 2Vv+*/ 2\ԙ?*>J_tי\/:KȎ#C+LOOtNz=?SSw{Az;]vQngvN7;s\d+w6;gkyZӸ 雝I[Oⷤ9Zdvfd% r0?[LZ?-ZoHS8cZ2ĒLKTe$eeeN2rU2"K12EIXeeIX1rI E%yI D.r I-~z21,J0ie@VL5CMgijX$Iq7mx2l,CEU"SQ"8b(-颜vf,3fgLgCB)y*md"#lM L52m}sDxd!ːñEQq,D*%"1qnx2"ةKRk*KeL&划\Q`LPKrXDBdsYtE:38Y56P:S:㬥-*hDի:Y唳j):jzkh{58Tslj8N5a Ǵ㼜ca8O9o4r>5XU*kDCf4F^3}6=ݖ]цhi-Mb;g߉%2Vw\d8GDRA{2Us(IÝhOArMƯWe/E~y3S<=gEsPӔ4e?EOQS{>}wq8cig8NYΥgsx/p~K%ۿ,Зk4cbuB,RdЎLE{ld,]Ts1~M}YTԻt泏1O?C[ѲJSXEj:ųԒT/2ECXVRjIͪEXj ۯ:1S@iFڴmh&g+}>iH,Tr}KǷz=RK3F,,P* 3ĺ0/i לFju=$iGX)Q.w9JUԆ̳BՉabe6m \lQըmvs=\_z/{5^rنhlfMȾ.;N;4۰m|ق؄؀XXUlb-Zγus8׋ꫧzPG[<[x/a+a;v`'va7`/a{E=cPX40.hbf0nnƲm4DYF#zvk^l6lUmlEm ch_-byVb6Vc ֪uX ب6a3lAJv1vPvMmj;v؅b'Iߍ٦2fm"ar}) akWc׷^x+wW{W4:): <6v:{eD8_{r>d]W8L|J}Z9Qg|V4:&4;XVakFlbj y]bN}ho7%ϨOOu+'տʌK'/8S}iC|rmM?<[9ѳ&zZ^[XѪZ(Y|ca|Z46ϙ_ |x&E,B0s`,?;P1`'K?H>#Fj$vB?J姇x?gHԽ *mN=Hd=HգxIzfNgF%OǣSi xd'~QQ'~Hcgʲ;ʱce:^v}:ul:v.mv?sF'rEDJNJ4:2Svsb~g{\92O2 3·ypmi1`;β Ưu:$"덕v_wcg[;Ȍg;>WHr#܎[?Z?[ciɅq W?wV?_}3*C t+o9.F\C,?;3:hf=LzrA4OBsx)gd'%W{ 32sXdUL2"2Lk02; r +l  y9`t?E~3ȼ/Vg|eQ^XY^%YyEr;,.~ggp-nߑwD-;qNۢ6+aӵZtm=e5*=K<985](!>znX-59K쿧Qnvf:|lKT.fgbYXd+:3*S}J4O+2S\-tNL0͊ߣK\.CNZnlLIW.T|w7kNhKiuq_s?rg9n7.q9nwn9xW~/vC{߅v?muO6OVZGB áC*~{1z-at3)gk+u?M[L:wVr8w;/*CBΫVzl6~{y}=rgJ~.XF{*iimK,[TR\n;v&W ^-&:cݛw4FxTOKV^ޯKԱxvmtWMhF VtkyԸEZ:g2]{o[B~/@>9Xl0(/3#C]JKiRڳwuXE Qv@92KPLU$ѪZh_ Qbe(G[T lY͖5eZ^֞2ZXU7I首J*-ZVZx>[FmUԸ(lԻڷSvjFۨӊcj1Pg3xlTz_5ww2$V0gJ2Vc n|NgzmAύr|X#ѱXo3bb^kh),OR^kku)>mZ|vG9~1˰)7u|#'}ewmk7.ǷiIWP~҇>m?s`qH, })SR(b_FeSF{e[FeeeevX ԣm@1׼V 8nc>#>C8S>|G^K{i}C/%W(XGz6Ve} 0 9^+VL}W2c| /j~XӖSz86qvr d|X#zm-aW:~D%"Z{x?W1V;2z0ZO^kI鳵[k>O~+VaBalVoƫbZO0BwXFWP6i+UB!:=TTT!p@-#)f ;vv|wwowzKV_}|m{޽}_OG>c8k׶Ogݽvcww ݡ=ceG؇{>vl8=qb_wp78;چf%wvˌsu3Sv/tv ý}ݝյ>kȬث}yig|^}^{<{;KeEvrurҾ[g{F'{f~}UTϡΰSK{hԷyx+wյuiyiUs잩W}R?}3V$wO >oOK gZu'o?o~?'q'ݸ߯DQ?  ?A~UCE=c꺣f ?+1ݘg"GL1&$c"0q";+OxJ"sTIUI;^Vc 6cܫFl`nu̹V9NJ%FN]\zOcdl6[X0~IBWIEF(X.^blؿjlEIӔdPFq2ūi69lk3/))+QkPZ7_ DWIY4~ߊSȵ(K82rPJ~U^B#sGDJ^d'~ߊoa%z>NI_ߤo7[-P|xx>\h](aLZ)N2N wYl6omD[?sl3WI uiRԿvv.E].s277 smN_DNk+|i_`;>gOcH|D6F6QT ڧ>9uigK|e|-|)ơyb'欄vwNj'z'`w0_WkX9׾ğ/\q.bΫV˜R4w?εr)vSC[{iw/}csh~B8N(U("j֊QBK+)TUq)% kA+ىBqGpp;RC[{hw7/铞}B5<є8@y u2Gp8 )VlU8rQ*3(6]$315q8qD1U+wP.oh^%2=n gulNMj&D23xwN|N{'nmJNIɔ֙v՗RK|Lz0܋sIͷZKJ]0Sl.%=M|I yc`f窩C 1LC,1Dvds<7p?CSӴqzF܉H܅k܇) ^aGs`1(2ЬG̝3?=׸cbfOC\cx~>Dc߸2{3܍00n {mF[h70(g`崙H܅e }u 21r1TCV& VҬ6/!E%9&ڱʱ2 ʦ~4F#2(p7Fd#G4Zc5gnq;ђrƤe#q&CC5s]%Z1VoKQ.Z0ZsqhFQƹ4v>*PɵSjx2>;1N.eޥ(9EcrQĸ3@DŽ:T..KqJ\TaTUFKegEK)L&tq&rDw*׏֓kHEcڗ~C11 `|SSCs(5Jb(x(Sg8F{#{^3!b+E1nӍQ8]9GEϜ#{6a>Lӟ05=)'D O}D L[k+{cgA뿻k~G[3k{ kz׿G2 ]3]׮*[鬬Wպ{uLg Nfڵ!d>8kbk럻7h͋޹k]jqΚVk sׯpʼFE:>TN?VuُbK5knjwj81JvU#QQQrzлF=QF5:F[656Fɡ v5޻[(yݯc|HMV5j7ޣ)]U uAJD](SzsbE PߏQjuVYUU"F[RU Se1JޮJrcK::}:j7Jmwo:"\ru*:TE1JvSTwu(Fjv([}/U=FɁOj[ר!Dy|U ?M(yވQVE Vc֪a%U,F;b5R=]n5?F{TW͈Q~5E=&(V(FQ%c=1J>9yBj#xy%'"~1JN&FJ\A]c.UU% T{5GQrjΝ4FɅ|8qlHDC)?FDBKTy(a*% ImZbE;oqX9~ CYX}?/Fɳsb<ψQ2Q'*|2٬9E|e+Uc,!,۞ =(yD;Q ~Uߩ/=zۡv2Ou^Nu˾j!s;33ve}&,l_9}Os| 8!_e LC/YWB,\ LTL瓑I?y@=OGh'ri7{)>ҟ\Kqy<}&0<5x'c bczFUJE?(?7HYa#ӌӓokY;d8ޚH=Zur,#a"~kpM\zr^ZG_ Qpsy7 ލI㽛y7;n:10c8iӆv90ѻSLB1yyLtL~lz9wyͧeb  BqnvD=֍b9 z~HMQY̥ 1~̣O\"0F}pN;io8po8 cN1Ø"9mƭ q]x|LfS2^#x=tG'QM@xq1O01PFKۮ1xLΔ?̇)yso\pΌSV*w9y9=WG=TRjJ֊& yJD^1&kG9c>sл$QwWrɬצRwQ'U}D?~5I ڟoj^MUp"JpU~Tpf_:j<z9n%B ޳H5yCߨ7{o_Σ"/,VjjRjj]zX3'+sjA^?a οN09'Ì߽|-sNDAwhcGyXhc/xfeFs V }bg9¨N翜&13+R(ҬO"]hd c40Vl% d JtE[3 Y8Wœ˵ /[/E x/wb:ҐYϑHA ͟RD8ĨBv(;*Bdvci=<#c b$AE.vqb ]osb.AN_#3z>Z'˓Iڋ=؍]yw'vkc;{cX1&>;E}"Xtp!YHgg|DW*JAD$x.֊@Dߵ^ṱE{]|b8ǼdivozX<6" Iq :cx8gPXDd@?rE"Ɗo|Vd6ięt <:>Y zqx$N'1C֤<=izCrhoLN,cqgu:>7Ή<1\7VX1c-"w5u Xc!>;_qkE ׹5_TgMT#?zTUXQQJ (R-@> 9F&9{E\#qx6/֕K[c O("nPRUm-VLVYPn]zro1xlZ9VX̽kkfuk[YM7lx}l˱r=kب^9ǿMm#F<5<>{Hgnfw|33lmVS8Izpo=#8Cu܏b~T*Te(E /R)n(q у8pF/8K̳#GG*%*Qjqyb>afՍcq:q~~w.FLb>bqR($W!y]Ǭn8,ZF^*ux\z4u2{2dn >g1HA sb/qݼwv@X\\{2Kk|^טUs}]2}fe|%C#\G~-U|O\ U:/zM*oB\ U&ު[lobWdȒ1_w]sxk1B_ur+o%\ Uc#j_i u1{dOXSZ_C 5N/z }2cTՂRB:"8U>M29bdR8NC'f a'N9!_UG:kOOE|($JEt?'xOS'Ovɺs񜠇NX9QAe3hi9z\:$#~ݧO?H*:Y7D~S`%DN۬IzG❀GX+b79\bNTv4nm؊He}Rbו7s373͌ɬ:(D"B1MT_Oԏ.aQt'p)&i&~/yzK^D$H&B|Wc1Qpv1?>"" qf}>C#NA]8DLuGnICWs!ފC,b(D"7㬯q6q:! io+yDe@?}N<}Q<pb\ 5xL9`N~_2/4+\VeE#C<}|H@"1ȑ0Td14ƛӘGIc^i1U\}HfMOWJo$LdZDcq}JOa|FψC,b(D"7묝q4ZWA2.YwMcF8s2"(DR[t׽Fs 5]U]_ѵX7Tu%*PR.2^|!9F2 ɱ|w.cc 5c/a1}Ld!8FryG Qdeo5V| nfvf;,ւkHFNN\m]nk^=kcO׻ =YEL#džYjׁ>/cY<7jx8DCq1\f_c\cW\e]U|_j|_k.<Ρ⌨\Žp%* ܏J KQB^/@>h!N6,g'|ԟqp i?(rK.Z<晏Qe(A)0Px dT2.yQ?*_97ʬRׯV%u:FubE/L~c\>Ǘ\np]썛&sɚ~gqS'V2Z7/5 9=x5~["-r"m&mqf\mm{6->[=-߁od7|}k#\G^*Č1j_l jUP RIT(êE#*$czَOz7F55FX[jjTwW#CVUFh,x*gQf<7:"6BoM?Z܇[)T%W`*"WW=N;v}?~h ~_9gxNCݞӺs5);}?Q>^\( N:>i{OOSu8>_'z:|l~9Ѧ3Eqf'js"ڝH(VD[1jo+Jkd.R+$>d+JNt kՌ5Vj]]i'+Uux:['ct% {<60u2Nɼ֢}cg'*61Zas쿢ժ\zyb 45\yV[ P,wY 'S:YNd>?W#ZfAJ6kg#^,f]NZ`3ɰYsEc=xgz>hm5ڱ89Әk#<7X io,hcnF+ZXUhB@z Ywc%+ÚgQxb6YĝEYr/yˣqOr1wbR,C (EQJT~5j+X4_G:Ցu_XzֱuQzײu1 =ReUBl`\ƹv2N\:Dq Q@r/!1&>EkЂVY\+urF9*xnTZU7E;siZנ Mh5~JuuUF9ʬR:ZJ?8h5a~VHF6\M]8V?f=Ԇv:G:y@g,1%֯QԣMs}ϱ~ϱYύFN:kt~1:Z~ZkZFt7uGu5g] CK) n$۲nqEe ۘL '4CB{ܙzeoϣ]msw90*U [P(a]xf}zRчspQ"*諂>}e_*xnTJ3n̯}P"@u6*Q />+`>F(B̿@~WZՙXqQhc0jdܨ癶} 6ssd\o S1 ϵf:G= Fq͢dyb6*p$T*B5E(T G#?P'32P U\#O,:kT-c.W+jᘵXǹzZE*Q~\]Ǻ.Xgc}:~[ֈ90 ,2,%j1цv>}zf,fnYŬbs(Em[Μj%Vj kE0JW~zzz:@X˵2p"r۷B-yӷ%K0BUb﵎[~?1g7Q=;} Or؎<_9&*iJ?[o n-c3<63k3\733k¾¾=Vr}7?ٕ03:1N0x 8\otG֪x1<-ГsN['8xEWJG0g7뻟=SCЯ/y<$P"y}P"Z51rQΘ {GOS1uzw_/s^n66mQl|W*qgR/.Py^y{w_+S_ |3|cSGk\>dO|}1c'~>_k|oy;|Fe>yg~HyS$fA RS&>cnƿBT2JYߨoENR`j"FA[̗GyaүK|>W3| ȫ~e{?yO?r ݱTT{9'$/IGB"TӇQBp8+O2bObPq@>dO0k{\.cxcюG#'kR**(S<6Wgd3&#F*sgB8&%_ {{Ǯ8 G++zǩ0qBlxT_j b1A(M&d0)~*qX-fx'1qLl8jiߘ.T&uXġ)_׉1'ҟ(u:u~/ se5Z+Fa}\O(#>;D?9=t*&x fcd>;ΌunQ UU/}F~6JpLY`*XJ1000YMA bZ0f_Eh@_1fު*VEKJ@iEHBbKԋުON/ՊR뛆S0YM8їeK(ч`#Xf$P,ߏ/g.gq󮡍 Ij2FMetoW`QjciǘDۓgdLREd*r=}iϨ^=s)b鬁1MLS5qcqVÄY9|H[7QMRƝ5tMe]Ccch(Qõpb8}g &Z;9fmýb^L]'̱exka\ۇxY >g|݇{ܹGVlf܅Mwv ⯜kB[7ӦqD?OFӍFr#crx}FӍ\6aĵkp3tFP21]|wbSl0!gkw(pdxχaq?_c#=clg8`3Rp'#od.(e-50YMA L4LW5Cͤ:qɸ]40Kmw6u}bÌ?ǏcϱϞczoK¼ֲl/]{uzu{{|u}uϵ'}=C3п9|+|o-'q/=̋y=!H4`>CI)Tvt ̤:n=}4wc0f3ٌk6809{

9ans8C {\;ƻ\wrmھ~{}ϬS1vg`&}їQO 40Fk#cnd ̡40zVz_pLq3utؗ\)dˈ=0k?ik"S:H,9:ƨюQj$~4_LUXriI4&UnV*Ѱ:ԭu@Êhg O[i+:jNӭތVm&Wlt+4L2Vd6_ٴV_ST[F+-meXQTPVMՒJ2TD* |!UъǰڱIckbfwkRhkZF~1Z\br[S웶>1Z$a bЯ9|e쿹iNtm5b+*h:LUqشҰiafiIa5 m-au|4_uX֤V*C[]\Y+ JhaX=H[5^ɤf%?V,m[X |j\ Ve\)V*Հ%3k%U5ழ~0 B[9h<0/T٪>VU{M+;K?d:˜zoe;[w]h}flsn\f.}[+wyP.MM\z8[ \׿VK*lq*2pJh\XH[~և5oջԺ5WI[smյi r"ulհkv-]͚WsklZ޻fgj>mWZI_іuJVlְ~v-TiAME֡3[w֜fd}LuZsiVE+ʳGsiR՜ҩ3K-skDD>,svKQy5_~.8<:Ʃ:2+ +w:,:ʩ|#dLElTr}[.C klSsN:'𤚥5JR;YcѪJ&[V-JnRreR&IGw$J唔IɭBG*ZVu8V lQSEVCDq1֨Щ1E"[Od눒ڡ亡T5CZ!F(]}PfB(DSN1 cHR5qS㓜8q(9PgHF{ps\5103Ngvb}a,8 fm3&059^+3u^5F5 s\50|TGu1.B:sl^F4unrtLi{ &v6T{)SYsq:vS#f}jԣA5bfc0Sik> Bj!_ bI9ЈԣNUic.TuO8\'f3Xcc&mԉk@fcYs9ߘG{hw.s<gO8 ιk8 >Gc\xO'Si8%Y8=\ ujaǙ G;i'O0qv8="Ngv!~,ؿ~Ͻ8xukI.E@u!.P<6N_㏌8Dž;A}'›)dGqu]5n":N.Tpq>m牫08~g\B\\0vY"R肮覺.a]R׌jU*i#-V!.3:bEkuUZ\HW*Y"pA-RŜkb0b _ {+K|US*h(?}FUTrG(c*U0~05~/111q0RUrUqF$hv}J\q.*UI{FPGT2nyբ駘鷸S `փEfݭkDi|V Up5BT8-UE|c,1󌉘ɘ5Uqι *UC8ϸDSժ*20%I1@%s}_80&ߥºq*U)05T]FTU*Q.```GCvpt!چyl A)ύ!+8g/e c9dVUX5jZ֫ L\縞Up_gU:ժD }R\rTuI*C5jVR,UQR\_{MgqqkϓϸdW9AEEE(e\FA) gƓʝ)k5xۛyOnZmmmnznͼfncރ{qs6xa/O^Kx ^U ,whxޥ~v{:11s6{xa!^kxeq/hy{N f0p%rEܬ \;@ 4Ӿ{U^oA-{vߢŕ2\Kx*UI{FWЏu^UeQɸ*aTx~^-oH}Z ϗ泬Hufa&3a.S-m2QFEg;1D7w |a+l>O~LY:' KNi3{;<>;=mfldŒ]fl.Uv̹Y0#g2}jKh<]rn.]^<\[{Kw*Ͷ Y5IYdli͘ʖdlFflfY٭3VrfܬVY3"Y>Ww棞trPn)33F6_dC6悢y0|'  0fr ΅ ɰd>e|JG7Ll$ud`H)rݓrI ps6'ps9c7` nN^:&yN^u^(aٵTCۥ<6sZ4r[9մy0w`5:/e(G7VTzcD7a~P\ ~zoa 1ox QB^O#bͮ5,ˢ,bY'qe1lƙ͘sK;cio펣3xNi2q5s3CԵaj83F8Fr1J3}*DBbN|0h7SCյj/-_~B}aWD9"ߛf\#ySuFFW1'E7 1bZ%Xa9VJ]X7Vu[/z,dszmZFXFR,b,R y}/3Osz,:k˰++J %Q$֋b z*Q}U)Ǖr!c_*>WXe|K/58ClQ+W|6%aϰ-|e(Oc+[fQ݅MN|nm-o_8FQ@7FHFƽk벑kkw\ a܄7[qnp9~ rVڹm~6CSJW22Uθ JCT0V&]omM_"շof]|;E.}ҿ6u+*fПPM-6F~S\E}rģ"[<;PO8xO2'xc{cIKTcPb fΙ^Yb/qsxeTW'WT^%6^ݗvܯh[y7Wk/E>ǿg|6|O1?‡r{ߣ~w0tku`Zǵyk>Ȣ,͢,Ƒx[c_rW󿦝oho[CSW22UΘ JLT0V/86}+鯈E<7 P />gƿE.}ҿ|>y HB_oU[e{mDr"GHd d;~9g'~$D0V+ U*V`=rwޙRwJ+3kegۜN=;ޝgvgy;nNawIg'68rߝ^g}uGR! ]{);d{g|7w׽w&Ŧ{u\s"w]jx3w S܍w+܁wgӽL۴wѻ̅).ݻʬ.r~ -wѻĒȝayp7 ڻ@?/zܝ^~{t-KuV^-zޣ2{/e>ojrVܥERݙ5#+lr'YU{{mw{oe臨7gr DG}z4wE;;tw?w{'{示)Myq]GE?/&IuI;w,MTҏ"iU}?s/bA:}Ds!]I̽)cϷ=ߪz owԻjG.=gn]>=UV9E0￘O>c;.aoM,|5' k}$T.rT#KNwoԛxY$Z|s1}!g_\cw}qoߪ*:'=}X9u|:+QKp" aorǭZwZ<7L۶3Bycֹ/^`_cu[ryPh(F*T (E8}{/E>kۃ6ow`6kbJ֕މwxuX+1[W7qT몈JUQE6nٌQʓK{ ׌S8aTOC盯m]Y }waFg;|u.Du.FGՉBFWMt_,5zNotwp!.:N|q1.Rp!.8sp6YhOƙcA߾rc򕪾|FĮiUYEUS)* `3& AoB1P <˥\ȥa9=kupr\_#^go )D}P(2sQcTfWG鯚~?Y& D T$1U"Q|[Q%2 1YWHPy}Eѯ7zbUqF(`>V> U*nЗ.S# `UjuCk>]10IcJPb aT6=s0~~2{cE~58igXL{˱2]**%ż׍EXH? |\lB#8uo̤;>j髖>kC-P˚ֲq-]551frOuz41y<,E"q2GX}>Uy/p!#(uja<&pDL_)=j跆qLeLSTs:M m}>i˚@x5cchFX};br?OKvsyas_`w=/^p޾b'FcqjS99`gDq{qjFQPomǍ#P01±8pʜ2Ie'Ӈ;5~8|0Z1r5vFQ7gZo 8ǩc+Qb$sF1gk:QbmO 7~'D21Mdl$;kk')Q:XSYcj?sNիok=+pri̡ok>qWX hG#p8Ѿq(}0_= uj&fڌ?[~kG[,1*jxl4aK?'s9/>JQrTRUZcR1B\R['.c]MIG?1Pxjժd4Q1%\Jz *>Fרgqnhk~$5ODoG/$Y|D;h{U(*UU`#Ɖ~bj#EA#WR$D?'0x%gXᬿ^5h@qMa-U9R^ʘQc sl>ϤwL1I跜,>۬in9+EV99ܒlr5*lsMsιN/'qӏl!i6[7n-tgn3MݕVgz3+]nӍqmɏrb&qw7ۛ nc-t10V*>ncn\<]Lm<9=3e;>D{?<򝾗kviUNƺm;oNk1DzݸWG6Fƥ6ܧr+sh4ڍBW:0FsYFEӏx/Ĕ'qf{F3MmytTazT=֦خtp<٦[6Ut*Ke"O<&HNxM}w1(fLŌc{y[O?&^%qGE|8rYb;~! EyGU9+IW9^Wo8.rӇuk:^ ^$%XBo9_4:!cyn癶mC?yާ"57>_K|Vߨowo}bm]yGQP~oH "kE^+Ǘ gM|ѦHP-_~<?wLǏV'QQ*T9TFdF]͸<e<"*<ݚ ̾(>mw(Cwc1#Y#Dw:5=`ub5K%I{מG۾(AoB1/Bos~ҞC9Mp0~#Dsg^FL}[/JQ&eN28@ǡB&Lo1_"U0˧-!`uTE>>"B7Z^Q|߈\-ǹNvN^!Q>;~89'o G*b>F0ssoy,nkĻbmawKmcwS9,4޻Mf151 1sa,mhI#o8ޝwo961Jʹ҆o,mx5i{&c j003PqChw}.K3^}d~{\ƺY #0e^rD'1wxu7^szKEsxglobx55^9f:fpUx5 L9w^T/ ^k\{`Jդ^+'e{B`"s&dLaM&cxs}ųbxN=6^P/%2*^S'L2{+0!bbm:ԣA5bccxGe\lmRәu&j-P˚Բ6/G]71Kh zQ^VWhozG`T3TQKNڿPLPGc^w}g}b'}kdž>u|1ǻ>"&Uj2ssy:-vN+~f|EX{H<G+ZQm. H,Ojcjig6 q߾÷׌o؏_f}%L@-cFMd7&a"m7'[^ڎeLcDКA[1cnXYY9yW P5~vq.?B۶KEVo7k~0Zu-|/0 답mVhڨmďD=꘏13P阆S0IH;6m'֪kWύk|'&a22FM}a(QϽTjj8nƵ?T8@>7<㗴'mI{zy~~~~n{n{f{f{V{d]hw폥7~1e\cX91cs$IGQs:ӫokf2:UxT#ǜWA`xCN9ub/z5YʼCq0s6Ryn}>ļb/Z9y~8[Iߨ_;Ns Y8ѨT=0cc XfLQzGC )&^-\HfI!לlx-)sf* DYS$vY\)wfvs?u2aFmĿ%r'F裑b{s{u7$~p$vna̼%_)N:QhANݍG#"}qx>9cu'>"zFݨ>b~ky:}Ib6ZFmHTڍHGQHĹ։.qsQ0Z<ى_u#Q梻h}9}݉mrTv}Yĵщ̯lWGAZcuN4C_@m3}qis|ٴQLL 6"F!ݨcmliL?ڇdN1/ژb,Glm0qw-|,vYoA糱=k&cxa.W!WX?s#|6yT^Hs"wGFz̺Gx]c;ls۱Rm9qS֢W7{ǚF&#575EK? Nrbf6Vsa?$ĶNj&F(U S=)b?7؛oLتxmضx]xc?cW_0~]F܄[y~'8AyOq<<zǕp|o+")"-SBU۟1Ɍg62KL7reo.]wG\屬x^|5sk._ u,_?{{|c :owŸp!.:6Ϧ?чo],=3}|%V`9a)`1a!p|\ڙٴ=KoFnS=ccu1;: slՓh+[bmfcke<^q<?3_kjԣNdYմYP zוXXXzltiԪcDύzF#m17c-`5Va%V`9ahd}Ygu7BT3PEX%vk裆|+d,x1q!-v"Z,b]i>Go3]1U gr X\XU4s`)cVbVc ֪uğiw? # sZF[]%l3uc25X7TjZXӦ8ڷ iX<7֩olG+5z*iy9Xyb^*_핼B-ں_,?@A}d0XX: :1]~[mU[=u6N߁q_q B7{3XnbLf}nk8w-֩il}:>}wѾ[xnlUws6^C{o VcVbcKnxºqڤ;gF;X=c-"OYOYg2, ZZO>a[Ƈimd ߷Cld~wMaV*uj=o÷C\8|_b7֋iϷkjJr K[ў\ }vw;g͹[[aZ!t?ڊ/Y/3߈EXV,߱\ߋy"*9⪕jjhVc; 8g!񺵐s?IcƾP-bξy~<1Ɖq 8^g4z6$_"HºX7fq DYYQUQu6j#;fV*v+f[NZnDՍQT7z.hh4z nst#a2RIt#6qt#n1*h"5 aF_4gzɑNn](]rt΍̹h4΍ą0.VӸ[kscln\-Sk.>4Ri̍Xq17L#g6bFl1F(XsWWYOY4ƼvqTXꈘHExsF薍ljuf1T7,7:eWĭ2YejUj5j>F]d(rVaj瑪~Y*VvJ i&*7>ƥl<ƢlƟq0ƛXSg1&SrIn,ɍٸ3|Eʊk>ʴItɍ,٨R4y$iIRFlF(!jŇxٸO4wb;n',s4c4q#4LrT&9c#16F^RG]2{G,1ʟ4uw"-neVYYTFThJ(Jʂȉ5q%6Jr|3ёtUkn}SަVr"GlSpuOݯ!uDZMT[i?ds_{J(w3Nc4aƽ>uz8W\2V}P"Y߇Yg!xPя~ܧU|뻛} T"U^2F۟f}f<''gr lň/Wԫ"{MJ8^v$$sH^ 39msYxWEzSyd<׌ۓzwv;v*oEuDyw@'Ĺy ܧJ&OSJ?c~'ulMg-^ϥsbq1E!~:p1 w>O3=YF PhmW9W:d},cEOkSn|Grz^;mhW*(uCA6S@wՍs.YtgeO**O; DG*MHt9ޢYwܝ`=O+` ܍lQ*O@%8ޗ@v_kVz^F!UzV х6}S**㌞A{=h=w?+@՟}c\`meRu jTJέP+B$TwYPdO*ªvS8.eq15=n s=+\].U~]UΚtS]UQNU3.2\AS*9:I7]@.$PTFf{!9ǮTWr\KE.kQ TS(H8 Eu>BKeru2Pܬs @Tʑ5+pwi骖mcfh@#!ƬM4y"Dgթjjt5-Ѕ[]E覺F$:>V'yj{٘j1CDGjTDG11uMEu覺EQDlWc1;ЋZ^fNX=L@e|"&5IS(H8 SE昮j 5SթzQ(DB4G>ka\#kg^zr!*Xrb+++űUX5X֩jCc:u$(n`F'T6r1(@v QDEW^z3ތ7s* 3s27k~r!_p@!mVX`Z֋Bd$Q|k!9Fzs ]9"֋љ|kjJtao,끞;K+?aݬUmam. w;p;nSr_ű[D|GE:rh˕M?DqNId=SOד6fuOuz[wMNu6Cݘѝu馺.9p+e6q1}6YmVuڦc:]bTwC> @*RŎ^Ž^bq0l wnj؂͸Kmb<ƝꎈyIn)D$*НjKmV[@!mPsUU9>~;zsl3l `(( ;.t} `~:CXx*:sH1]6-׻*FM1uGpv톝^XL:! [n͚CQIdd d _K.|5.{͏xtR]=ݎͮ~6{j~VNu21v10ݫZ[lZYlYXW]YVT9GO{R+YIbjTluҕO .GEDeA PþÞc;7ki7Ia[ɿu]SXQXOXMt1[ITɔ[?XkQΚ;::mVN9mY|sV=GyƹeZϪ>,̬/6EW-cV-khh3kκְG4?ϛwf\ٍՄV&9׃o/{Ŗ+-`x+49W8_;Loz90A5F޴Q;䘹SN8L%=ra^9cy0f1Rs,%)ce209 f8LɜyF:L>/daEf^%ɗ%0[PxK{Snv|K6xo-I0l>&?60Y/|(33 ~"Wz Z`!3hM15}韙G %XRhxYpG0L cqVxc9%t Ё0^s66q xi^q7`Uvh:b 3/3+*?deoYe$SymY?woӔm٣~vNOu)moƼmַJGY ;-~&:)=aM˚n}k[t;ʊ=N3|oک;ټb>zӯ-FO)j/xri~ k.zz+ZoͰ*]5cٌ<^|.h(&N8w~Ho y'ղWKVWux?KV7+Ն߱#} _(KA?UaTxt6S%EajQO*3lRռE+}56qBmzWD# Hv<%!-,rx ƯhK~Nܟg"HBp{xk{ioY s 9`E8|h9G8lͲc'w )N}OqN4O[1V+yZJV=me4s:)b'XSqLf#8k#"y{eYfo尷.ŏ]2*{q}q3`qKDt2/G-q^Ç$6 I\#&\g_ipWpqI<ͼxD# ɴR%6f\Gk7DI GMDC< M(#"w\N품F b7[}7k&[baV-jWjT'j9PbȧMrCߵXCXE,d"+ȝ=9 dp?2y%UV#k语Er!PBemcq7k3dB]vky"g*#XidZk۬.c彀L ]b%e2V+rg>9gJټju9LOD5F߾w CT]Dwߍž.<%\g69ڶ5˾|99w95}x*UQ V;*CwŒ8F2\2X+wUsjdU G^]*GAw_{<^wj'3B_r *ѫw5\ V HGcW chu1ze1ɬ[Wkz_GuVbjjz`Zq%*ЪwB A(}dF')m׉~?qЇȳB>y>rwHOE>zVR:I=?_{2G>?~v'6 D=x.NJ/:o ;[ԏхNMOß'ׂ]*Vb8x s٫|.|@_=%Έ5b0E15׈W Du0 XIq@$3f>xwHP.qJDZpLEc*Y>UᘡN:FzJYy}Dv1C]&b^!r\UsHD!1E<ӼhXH"om8:jpgq 'w8ڧ=N;Ncc9ƘшA,!ExOL9o;a^F=FHd=>פ+adg%UXMlZ yX"ڔзĬ!_-^`?k{EkXn,sI겔֠Y!D}liG\PyFAL-5?+|BO *kB^gsmBz _ z}|_KZ/}mt6:q˸u\3 q.(B!ϣ#5jگBx:ցq .tӮ%]<}|G\P 1kCΜ9n..ubClGdnZt1O6s:[ܸi0:h]m!V0=?tӗ:XׯXﯸ =8cRD&2}H#_*^Bż6KJ4,'g_!2!c{rr=2k+/?}O箓t?UOг!gnY}ƾ:W?~<]/uv/rt#{ٞ٢h49 )Z(#ڈXkċg$$/I44d\$6gO3 Ϊcu̧1 !jYlt̴jj.J(7ʌRb("VHNG(`LqkÇQȚ8%b;EQr1kI%I~G+ w/G1c*flX QF$mQ*2ܥ¨tϠHhp}b\x j# \s-EԱZ>rl26 F#fc }BKI6h$Xvƒ8"ƈF"A?mfS%&4 r_7EVYYqMj^Pi/rYL;mR#i2,7駭UvI.Gk騵v: 4>Z3L*uϋm5זiH5K\60y @+܄h@#L[mX8ŌS[b,5Rf,-ۑNG3sk2р؄G6zuזq 4#Xj,bc뙃V˼6uFDBMlgk 1-y֒3-uE(F JQrU~UЪWMא|55_ Xz:L-Fh\w(F QuG\boha[Ќ&֣m>b3Ј&kBLi(2Qb(VA :c6c6zQj#{TH&vqNSݸp?="{AQ kCKu%k ~>q߻wvu*zNm7õ!3WGvvָ~6ύD1>QD=}p8|6rld!gtqJ<}q/Qwԝ\g!h gd" D_kB.yMwӎhKE"m~3YI'lj҈cD#U*D:Rtseu@wdQ(QW'FqR=N< WáCp:ӼB5RFcaDMT+CL&GdoJST3ӼtRE[OL Q)N:4*"X:&юp0}?1)w@|';N#Ncibc81d12}'ĄZ77O[11FG#1>1\3h%so|۸؊-beèo߈ j=Skg!2VSo,a<}`LD5 )\3tTkI,4oQmfUZ[MTnvb o}U{>|~~10N2/>%b|oO|=~?'YB /4<#BHboƘPXxL#I) .4+13c"neOG|x~|+|Ͽb.mKs٨_YL̠*1]ؾ|{y>b2`302fR,TQvuM]fSm6uM]gS}6mM[hSmE;g_ q=ǣ<̣<70/~aaO#s3FQasNScSb~JO(1ُ{䷡y߆CqWGHu!t:F cl 1'}#(5cXxq,}iCC UPnu.SY#/(q4c$6;z `?\Tט.T5%tp"&G<>N$DU/Ǒ(4011ZR#őbq}3aP?%gLV2ni^$46fQ5f, ;ٞn޲;N7fsIN?[36St34egt1t/e^f]ٖAe7'2Ȥll͠͞ &sc2%3$mf͈t!eBfA ijf>6n6nctqtmNcl.ctczL?g16_*Eh\D&gy{VY6;f&Yوg" Dyf4?˰֎ֶN(060 ݜ¢H?hsL4g0^mט,C7fYل{$Ldك6sf k`í#8\%O06/0^>[`C95X-h3mh3mV3ӣ2mg3l֟k(/[_bQeKK Ahm`f%/#f,=f56#^xAz,&/ȿɷv9uA.?ܹ͙ Ε#rblN\l.\ZVt9on#ߙ?ǭ[ܶbr|1ylkr5WQk\~Zi[vZbQKRS.{ylj{SsbslG\C57[-TYjnvZlVZlFXEv@TYtY_5Gb6Gl|0pWb/nuuBLV B'+ȬJḁ̈yTɝ9SѹRnTtTCQrlT}OyOAkKyKP+#L'7l 2,ё%f+-Si/6R=WRH)TYJLT_FRd"YHGA֑4F6,JGI⹹H6AyGA[49Err!;PPb {J$^ސ3 \9BnnPh>-'9>Nx/=tfl:;n';':+Ʃ/'5'Uؾdo?f4ufܬw2mlM5eָY53jl6ͤY42hf4<>(Q+Y3͌7[濑)/K&QF^M954ril͟I4wf_f;/kf̸22Adf˄q3`lKc2][L c%6K%^g87*c|Aisvgrٲ]wF8=Kq<]|kh)¹HΆ)]Ghwhgwnx燯 y87< 1#1JVcX8Ƌ#& FL.rP-U+Վ2hꢈ1(##0\ S7pu=}-7%Z5mczÕ^1<#SLYRUjbc1JMju|1s1kg z&fkh5;ͻJLeOeOeOaOaO.h֪Zr] u.$q2*w q.K=|U`.a>nz&bLޭj\hu W_5wF9(7s0Ufb*ǘiuF'tD?_u).ĹB]I\*U)j+~WQWJn\A[q.%mКBK72V|ʘGs5Uj ]FKBkm{_kg6nXn>q:?|yBqX%A,UՎ] Vëko-a=6`ڄp^0E\hvgR-U uQ :..;֩jZͽ*Zn%ZhG- RqxбD-V #.d|]1V GKJFeQ~ 3SxZ=U9^qx1Dž%qjZvaF+tPEyرliS'j=Fl_ hVh60}ؿwx-w ~G^&kRW \ ¯s8sqW8zUq#hۿ:> ;las/ hO+՚6mhw1p( _J\q}f\p5A[~с{:w"Vgbv&vQסz=@k _{^'T[\#Ro(J}P!am1ӮAqm?/æ.}З .'qWqH}h/Ӗ~/p-z}e(>8F1q}ոJ]+D1]}ƥѾrTJa*a:*13,Ta6EˋY5Qסz|ǜ22|i e \LCP7zC? M1 1vG?/w$ }O'18T5UgYcT>M;{:\=U9P}F qbfjz&sQA_+GOu?ѹe#Ƿz0'>?ywW}؄ͨQj [VM=ka# (g(gg;D9ܞԡ':^K}׆W Ӟ!|,{bbڊmĶ<_N=|*ybȍ*rGQ EYcw|v+we18[D y1Y gO'q] d-o PчOZs=ɼr<-8u<^d.ve eP(^XV/17 x^=74)5vRUH<) S=gE>u=ԋxIV/x(?P( Tr}0P r xyh28¾|סE}/E@Wϩg3QЧ=EcxZ x&bSϫԋ\gM~q^@凛)W57~߆ {!(B1*A),C9zZ\zg>ԣ@D ~#_s+~fZO|QJoD55~h s#<6LKFc3`.,ujgKdY QdRKEbC/y6L\ GB'e-rl~gy 69 id* YNVf64hlAbnvAtf(&A)n@l@l7S !ڿ6 r b s b{nh(/ VV 6=NSǞ'~9>'$>8}=ySv{nOԣOݓsļu'M<מ3sbm9g9maٺg=g֞Wu_]MS5p>gO\7xjOY'`_dGb3׺ڙ{{guU{iɪ=Q'՞'Ωhhs Zr4Ӟb \&[Þ]ڳJ{>Ggcc;aY`õBj}l@{hO)=݋=ճx]pJ{2W\5,5|qYΞ?qxJR?rB̹'ruO:kΞ;u Nܓl$QN{fO׶:'j)=A NbOꞘiY-PZt:ٚ=Wgi{~ٳ-XCb Y{"fObObOWӮ WpeOg8u*Us0'K'J)(ȞD'DtȞ ŞS=)KTgPǞؘqI|4hŞĞ<u9Ş)J&{r{ZbOHiHpQViGsh {bV=( EbO4l"޹LƟE;o!/g YBF™2 yCy6 B8D=M(sѽ#4Yл>+zVԧA7u p(b#:8n-c7]U)7Lt.S'jEOtUU#{c a03Cq=n`܆a8Ftь1FdF<\19|U (cx]%bTTT#e7 7z 50П~KL(fnE7'S=}b_g(L)Ǹr1e٨R31i\kL)ę,BQAY9OޓI$ƨq`+Ɍ1y01bLgUbfb0s0n>n&L{3P]@uF*z OLh16W*3,Tَ9j.XDOl_J*Cs&NcH,_fjZcH]FʠL'k`>-R=K )toDwq~jcZ"`,t{3{A)xkO(}ʍC@x?L;GhzԭWIXOBU@|yFd εFxvO1OOޞݧ')>O1OOvTSl'xO5ֺO 5uϫ9gxϧ곩\y&~ϜϚ3}t%3dmUOOOrDYi2#SdõDjstgY>gB,h<.y᚞.5=8 .xz p鋥k93,>eg*觰觯ا]1O[OZSV՘eRc<_>WQ>/$\fgt9盆<ݿݤLtIWMuWI*)HE#XF&(/G6uʦ~5^=|{'gpb~>icdn#5CE|x?3"_Ư׷"U|G}ɡ$~'EyMdN9"?:~"2T2e\p_5i%ԤiPsa>7ʑF|hlKqEjOMSB|p0Q:SG$pI K*NF1ef#sblQwP12ҙE" # hO*kY~kWn]]]&wuW)QR +զ]e+UrrVuW d~ a우`å.RSY ߾ L|^͋Y]+ڱk=Ƌsu빺븺kĞ[U]UYj̮‚W*+XUe9+DO75+mj!S=jzB=):r<ݬS3xYGsꮒT2RʵiHGqLbgQVegQlMs6u m=|{,dpo1'ԓK%Uꮺq5 Er<љzU;E4T~7mMT*%"7o;Rod>OwI1^kuS?W{__Qv*cCa>7ʑF|SϹsJA_g[|G95k#5Hs;2TR7LG0c;{E:}edLdяȁƟyq菴ߩqVu%bB 5L$љ2:"z|n:(}(T+~m7Fj׎Vc08L$$T`2us07weJH}ׇ^#xn3' ޢo51g%3^WWō<^j7?{ /clq4ab.Q۪Ro&N5jZcL HoaP~~:XLWAd)h9q,Ǚ/3+܈5UjƇNcN97`,u31KU9fqfΪ1L5v}y<{/Bޟ/o."V ҂yۂ9ނނ1MF&qDLx'zhb0R|Np_όg1F{d%$N+"nkhMY)5巡mOֆ:uֆfη[ӿiG+ӊ9В9 =s[L?$MNN:iy9aT=E{r~Zlx+Td %8;g&bH:IcOG쩈= ' o:I=ݰ'ѧ)Ev';ؗ( d$ ;˨=>Hs"28pwAݳ4H99sB v'hh/'g}`OG8v<#oLۇyuw];G:;vǼny;=rwogv`;rNgw5s;_K^ZwǽؙΞV>VbϞɪ ;W~wy"==Ӽ{Õ*l ^ (|i2Zk6Or߳"qveo+zK_2NY$^qK9yWJ})>󨗩_}ͽ̋}k(\=I.65JoHEJVV6zȱ]PGS=DrqK =)ґ!bEI_6r*(zE2.VKcģSWjڮrl>k(["J`syG*":PGSSo*=d# z#id GÌ67×A2U#olss̕DJ /$J=WkuH4xU=1^i8v2K/KeeڜxF_髓ϸ# Ň")z6wG6mux7*>hE|ؼy'qC|&¸@8]}w}g QMwwA;Gww `b )0{Ga=}Q{Dƽ7y?D'u_޾_q^կJ`:3Wbvyc_k}΂ט}}Uzp~- { 5Sv;yi^:*?2G}R/-\⥆p! p>p{8^p24k8HBwt]Б:=b^( ʻr/KxB2.2:#ltUjmU;G]IHA:droz(Tr\ʿkFE(Q(Ue\~6e҈İ:;ZQNT:;ꦺ\iw} cތp21yf c|n!A~˵}Dכ8Dwe\GYʿ|d$;ߛE=RN[k+n -jKRvԹuoH:3킮莤z$G)HUi%]eP'#zY7`5D ȤξDJ 2FFg誺hvCE|HN,ȿUuKxx&^+R! d5EMU0]U:fof|osP*Ge#K> gTb:MTLdTIj"O^c<7Ǹc2W2o222҇\U'11ϘLD1 ocj30YĨ"^ߛ+Л-(&o&T1dT`&bdNvȗϗ$$#E"8颂~eRo 6cJoP3ϗN[4*ʪ*ʯbY,י:;Po`z_'at=r&RD5O~G/wmJ:mȕ}n>/"]O2BZczP-UDXQMYeYD }%X.B< >u/ﺇ1 tw.ΎNўخv~6бH-VKԃjZӨV+(۵:Nbm[)b׸rZEjz #m)Fztえ 1~P-2H+EX%R*͑2TfJ^ lb[9[.rozKb,B>>QϏǼ?ݟOiߧ~ʜ)sS̡ͧOS0>a.so;Uju< ʻQ]/ˉsgXs/( %u}IK m}|F>e\>e\>0ojP]d-rwDe<' ސ ?vD{:wӭIJC]whbwa};/vi vYgDt},Q6bwS.{Rw)ێ I|'zv:m#/;h3c?wwFF`/a}Y$4v,%J{ GA=~ه;¿΃}& dc#f⃡l|;d2<7y;&xM/C%7ي-l&lz竰+Pͽ˱Xb QB{S^܇,ϳ.S,ܛA Rb,rU^UX5XuX-x;e>F݁y^A냾bPP\O{}D@{}KL\OP=vCh`VSj갆^GZm d o#7zi `FD_/Zw^uq'Jv}T_S#VP@Q-E,w,R 38X$g?}P"V6zJJxԪ-L^ ^F%s{FLLF&a"&`<׌X3g41QEY(s$c;qxd G[khhr47c黱8&`"&1S1 ӹ3ϘIYěEYį*ʫb^TQ~6bD=6FR'c M͓Tb L'N6ckXjNiӘJ)j2*$LTxM*1r|kγ3~7911YTނe-VnS;ԝKG,uG@B~{a1A,2b"bRTcJ*v3`cZceŪKcZÈZn3FKykhjmzOw-w1$oeChy-l}^S;P*󹱆}]tTX5XUMWbwLjئ˽.7]ꝀqN)8tgp=g,Mܳ6CyP9Ma6a3jT-ng6ݪ{eQt4u*NQ'$xǽCE-flclMj3jeBVSԩ8MuƟ88ʹmڈ XuX5XuJ7VϨ&TNIDusq|:isfŞK;..vTTk;.z(2QRu(.VmTkZXO}Sb8qXq.XV:VDc8?b ƅ-TKG+Zuby/B\I[|Zq )3FVTKB]emvcU=VG\G RRцqX]Ker^\A\QmWͪ- ^mỌ8Ru }Ū ZVhrq'q}[F^}NFncuutqtVCC3VԪlFwXBʸKQf8jB]ӥ]w{Ychr=B>|U뷐sX(C9zZ\^͵}r/?P@۾5QjwJ]uXKq~ p3no|5q191[UZ-fejzP-QEUEj!q?e;a7{w nmwNuqV߷лWG=N%~|?7xX,wE绝xm71_,bl,q<:ẸϭՔQMyՔWPi%u[I=WRg]ݸWN0`^6ԊƽF|(w73Rw^ [7&܈\?O)èLcuՈo&uI}i/j#qjE jL&blnV* Tsr[y/%z/ދŌbi 㶄q\¸>x?/2rQYXXXuĨ!^ qk_K9W˜mFzlFM g[o5֨ub l ؾbu(`ZVrXF5)˷*R+hvj6H~;sb;c;2X*czB=xJ,≈j <1"(+kZcZKsvsis\mfeQj#^kfI76ww#G|CEx275e}/ |5mއ8xQ{.mx޻ߥ_ߥߣc çc >g_K|k>[b|G񿧜){{ʯب6Qͨ&굅z[s|t|%Pı!o~#`Z%]?hm6T_sT}"Vc%2VՔm:4=/IQ{M2T,hh7b@ ѴϵJfkZNmv'W6vy=xjۯCͶk+u0 [Bh@B[ DI";&5CT@GB V"BCAɄo7gfgFQ&g'؉>e^_l2lȼYg-JTY\I$[ȬʺkrYcA@Wu+~%d?쭒=V=};D@潬A(I,EUM{8{v8h>|/^8Ţ߳vnf=<dZú ߗڰbcP3!3cj876kN#o4feY ߧ cVT@PP%2b4D*!Z>&JQ ?{w9 0 j,%&OZvd OqX?Ըjl Fhi?jVNύY3KVwoqw-핝KVts.涔k;Z8s)\|5wHvؖw}?ƜmV{xj'Yf`!x(@ uE1~"K['jxxYpTWzso xc$@+B"-@XB{k% Ђ$$6 @X 6 Ɔ<%K^Wd0W*{ q_9wι=m(_bmſ0xyO~m)kϜWk=Y-K=ob)Y`J=zHŚO,Y)V_YY/gp5ˊTQVK֯_Z_YoDk1Sx >ϡ]KWIZ}6yo+y:6ox%n%藨jUGT s*eU*#Na&qW|@XG*/g%3D y"tN;|eWWou=UOyueWxgǓd,x_x퇞w,jI͓ik?Vo"~.l+w|~v} sx{W foo 28.s">_nv{{OV6|^FB/I%rKd,!b&zz_?oohiwiJ~>~.ch3GD7qoi-hw$7Y/zZ>]iEOŸ`|Ex" nYWVaV׿/?#󇍛qOM~=Vc=5&Ѵ>͋MV_ xgS-a_vo,"At.n=AcxoFkgW qoc Bu@(+^f\ntщnF h [39[lJ-S*ꈊ@:fvCLq $Ni bh{^Rp8!?Ts%\"%^bu1\b}/1KC!ss,oxڋvc}e2cr) N8wGwGȣ13~Ƥg@'ֈ|K[@\-=e6pF$0N0sqg^q5b(DbnApqV&cY픘͸f3> kEcgŋ[[(krt qo^[@-XB.$s6X ~K Kb~I/A4y9"N Rm|fvӸA{WUکPvbI#<óLd` ґUIJrGwYL";ܓ~܏VڴA="E ޣ+XeFұBmQ@"▒U]r1XEKᵶH6K.I<>Fk4D r` l,sI!ZGJtpQԼ/D Byjl$}N(ƈ6 |oqns{ps;Юk븆\k^+iIxy{y#p.rxe# #WcS+\\v|7qe%R!kd(D&-.3GiWD>cs|OvU3NuI<[pIjq8R r9S|l\HXM^-H}.;Gw/zk}gM9m߷xqܾryy sϡc܇ǯJ xNQ*$PwE=A%2 DW㯈&?|}ݱ`z8uTjW^VmQPE2q5rUW ]uLTqD4|UFp1x2Ǭ)_ !P/w}D5A䕃bU +;AU-W%0*`w̓u@jY v`w{GQwc'ĻТLO85ߥCSx;ѻo;xmwھu`7=3H,;͵{}5h[SOa42%O{7I/1!"O]& QdrOQ**JʨvԮ RG̑]u(0R ‹ kb=y^TQFQ*T(GJ駕K+&~1[D~Gq~1ɷ7zcFɓfOųFlI;hA+h _'1:[d]"OE.ֶw]Qh(#VN\G;BkhNv4a^BDM\od,^-‹Z1J_+#cvh1Z6k诵BX/\ Ȣ_hc}VAG3v;hMd2- OA.dA/k_ؽ">5qkCa~1HO $}N1L2y(B1KPJ21rkG;܎aGq8lr_f5%O/yzy %Jя#Q D)cJ b 6HA ww'XG2s QFC"9hY%_/y{p=s{ 8nz#8l{p{]؍Ntӆb4c'1w ȵn~m~[9hvcvOkF ZaсNrF8ރ#OzA kSC kmM6.[x^Ա>&QoG}7b3!HzQeZkq8g bHFƽYa4^;,vl'fЯCŠ}@W::w;!O8^cfׅMNbk>tc7:Av_ZJ+/Xa>xZ9a1N^yhNcaNܗ?8|2atu&0y7L,c:2n \;;wDmb 7c6fl%67a}ws|{Esq]d.2 <<95 |fgyޜsy:=}x}_)W: Wٸ+@T&PJ\ȊWUGhRo:c][PSZOj jǩ@vUcd1Y.ܕEsPEDhpG\CpFZ :WLod*ZT cW AApe &FTA߮D?~ӟd?eȪOor;y޽s0N}k'>rN;k8Q&ޙeyhەv䏳qG}v={A;rvǸ{l:xoGCs7Ivvjasz^[/QiTLk_5uV1ǘyFeD1F,1|j~#3JB+w)#^@(1FJD$a*f>̭y3g5pyU ~)F5/5rldtֈ"13-9"0syDqb '܎L+Adȱ\h9"Y"~ZXKnG"1Ϙ9x1ۘE_td.ybpI`.q"hpc8Q܊Xp$p}8JY(7*J FHZwS[G8^"Ѣ60<#1yx/ɧ Ƣ=U{lf-_-S+D﷈s*fιpyo۴1-K9K9Z20s(A)ʌrTҨO5jZȣՓkx u"b%ǡs3QJ մ1jXG~ǛxK1Nۢyh5"IO"O9ȝZ%nI_(}Q@K\[M uګ@,"HD'+_gSBbG\ɻqyV*VqMFȱұXLg!9EO|b{e^y%{YG/e]>Fqu22H# lRE=gkj{ QzTR5j*j^cd`c,QRmT9Ve(E ǵb~Zq4/qkYw-kt /tJG+F JǺWDbb%*!o)(yV9*f*QjԢPF7}O؍hdHލmdYFֵuЯ֨#Z=9VzC]Iu>3$rݸ .#@ n{03; $A[F[:LO3tw>35}D\ -y֜rДRohB{i1}hL_'5?Ds@KʶBk.hI=J+홣N諫#NӉ:]_K@s4CS4Nc'S*g=1J[ ZkxւrGiƌ j-wB= ٶ4:2ztD+x^!kJ)^xu4XcMm6ɓML4EKCK#iGi5i IbbQOicyM 'ֈk&ZS 55Pry5eH]xeh8Z+-MИוFh(p\+eG{O?] (̧W#Xk2x?kL>AO08:1ޣT9̵?\؏}؃؅7׶k(R:7Sv6FHql`<Ʒn`bs5u=&ԛlaϽVF*}JwbvcbC8LYu5\F.4Am*Aޯ_ۇԩnNWQd J-Ƥ[io+2zZ}4@C ֜r-Rxkl(۹VSvj[ۃ؇m 5BKMi>=44PŻPH{|S8d a'FkۉP''Q2׼\y9v?en?en?en?ʵ5k3gN>g|_2ޯ5am]Ʒ?៼_ 1h3&hfh!ZZ)_3N2cVN^?h?'FkfO̕ր:NoD}J4`B = -Zt Xk5GT~Gc;oEIi(~MۿOPsg\j|r}{Ý3Y=;={ x~.˙ w |[<ɯٹ5WsjϧrhYəE_ `́ܗ 滮\u䷢i\c`*y~\)s_-V%v+is[v^JN+|VU9*;7e|  +sg"U&SeTv*RIM|夂sLr+5rIvy`>(2={&*LeL';v&22FɬVbז&9jfeMA fNI0[28j$GT\FtCΩʑKs(~$˗V.$H|#+b&3ag8rněوf5g1LVfe"g̚L#sEd^CdYȜ75۶C*y[跡oC;BCxX{ĹETvn彮 (K=DLݕ^s0e J8DixJ:%PJ4Bŝd-EK%2Z9H$-ْbIE2+eT-dK'UX-IUf\%=Ȉ8mRAY}~bS9TMJFV]D-KmQécI^_5Q>hQ 4Q:*QgewD TG5TET}ccGGYJE}E}E|E|E|EUeT rJxL$JҖRvKо(Nӿ8-NK:IC]ppGQi0 ̑:j8EMQ/TKUCuD-j:*hy%Q)5RCTAJUЃ(O x*̯"+Җ:jh5ZGKui.եݺ_kUJ})HuNJmRK<+5JU \ɌGIA*0^?ZA9?;%ԌٌRvZѶњu`%v^뀎Z'Ql"J:]#r<~D7z7N?QJ.WTTRZ%(J~=h_c)'񹞠W R2(̛hɢ}EGՎ6"I${ZѦ+U+AͤL&o( C0F`$FqGkc3No@yc(amdK%eԯR1N$["=hO)ƫfp҆+(e9-|QřgkTvfYfF$fxtbc6bocy(|ic;m~Au+o-((Eӵm=Qk~5^Va%ԥ,_qJ2fJkIHF2(rxP+O xH,(+>JJ'1>U{x۸sQ98K{gi,s{=ܞ嚟ڟe e-c]c}gg\E%6>>?'$}/Ҍ) Ɍ1([yw]EƠ\bwoX p=i#".h Zbv()J$iɞk)T*ӆRFKR,H@{6bJiIQ$1?F2s eP6\~Թ1^'k-Q}y*yS[<%ĭ,s/Q܉{xrv'0,JyB}IZ1'_]w{J'ם]YnİVTmkhܡ݉p7Z>QY<()/**XTw?qORUcKwvn-ԥDʍy#q<{6NPc깾qeM Fi1&b6Nj ւRkڠ-i)L{JBп+ j<}ڑ:2ޑFc@F{Tim6hmicE \Fj#$:wp7MtsXEb,\Eyh.WKZkmߙ/W(^=uZX$طDt|>h\:03iS0(Lq?v1;GӏgEw$IG2g$F1QwG3cz@FHh,&c 2Ӑ٘<yl:.=+xJOY5_Yu6l`M_zLID 0>e5cU^ci,prW ^219܏LZ(.0SV030x~bzg2eWYWa+yXeYIYcx2Ƽ|S:g|y:ʺz>ƺ>>:?zW98A9xv%ӏgqhvMvl؊-LM؈ Գw-m(khs mKY_2װ+:Vc ]+V3CYܬzj7>e#6PNYO=:X%w-kx2uAYx OوMEEƵo`;&vb{WGRL/Wja+J{y:ԫu]_W2t9\O7y=/<'gEKμF9foK8fήϫr>-,YhgN@f:夙9Y&OE;Ed+1sb,bNE aSaDX'bO%[ɕ,Xb朘}>,x.,0<9f^=2چQO{Ow٧̉.4W).֕Nn٧';eObW椕}*dUSU)I*b>!eNF)'⻧ T9d9gKJIyhggr(,TY'ꩣ'SE)G!t9<{7[wv:Ǔ3<vgvytѱD;1q1gp7Ms7wW:u_-䧖اtbײLj8Sgsrv]M9[cJy';uc:icN٘6ObN'T'R*z'KrR$v ωυiςg@3yg:yYs?aHe0n[jSsZg'̙ 6œ>s'xy_2ǝp; >1~f;VV;vJ;FioM:`YGWUU6e'GdM&;`疽6kP].tHmf2& m2~9ufmOM;?=+6h?m5϶r˹c6*2l3ɹex3~Gۙ}^ a 6Y , i4G J<崶 wz8m==Ep/7h}~ ZҩCdɠ_kiei| KT @臾4fL[c4b\O̗I VO<.jiZ6j'Z Sͩ&UCUToU觫71@Htm2OYL_\:>(jSRKSN(jж?ц}x^ӨŸږ:Z.5ZOI/FZC @c:i\c%S4fN\ỸLk*Zh-VZkNd9EAd`Qc6D;oW_AouSԣtÓԯ2F@K 6GiKv5Q#m*ӴJkm7Xk5@LcTLadm&jx]!cSi}%uR?udz>ꅟuBY^D{^{A_^ UX[Eh@O[#Dߏ8z>j ǴM@K6GiKv˵Kk׌hCZ+mWHkHx}=حv򦶃vr5hK-SS\3>ϊsf,K"MMKe\\8}tyHk,0'Z314;/<\L>_IԵP[yџ{%ߝ@2']$^v2L-K!P; 1z=Fh1>c>& J27di1{P pHFc xmM&dm S1~vbM{Cd3lڬmy؄lްlOhۘH SqXŜbn\#10(C0YԥdRu0qmްlm36a#V)9TXZCV,M&z)#-۷w{D;y{k7uC;w|``7܉w;6;.gݵE}1)ۻd{w+6aNmkvf͹Ӎ?Nw׍umog?}}ܫ=/yWpmhL5rjTܑٙ6α Uه{%QWG7;wmݠ o_^~_EEtf/ny<ж]^vsf][= ==ߣ{2{/f`fu!P}S=S|xwMk.gZd핂{$/AUݑ#b2]ԧ?cۀ7g5<Qu5\UTӪz2&c,F[FYFj#0"-Qq:})jgoWlQ3GTsYk )<#_koeeeNl:4li5u5swncc /^i#[GNSQKƜ)y~Vb^VkkXgYO] \Ҵz6G=f#xj{VGx|,+X.j3'FHcjP 6kb=ZRN٣޴#l-h oyTGQ>)x4Ӛ.(DԹM×p_2|Q1gYaNx p'ppGxarPO[is&hn~~֔ruv=0y:cqI[83~xrGH"]KQEc4ASQӧhI\'8e9q|ګWVו44b)$NhǵcO9*0#6jy9=ˇv#t ,jO<ŧ,>|A9KZ||f4uSPHi@k5{d )0+ܻp3Y`zGi^` &*0 X( P `dLd :buC[igO~go^s}W.OS"K@&0C0a8F`ύxM)?f3:2wׄc}ETчq[CCO3 D~Wrͣ\mBYLm1]jSOLbws\`ee$#T4ޘn ,<2 " aeE氞fk~6Ò-2=ө5X ֲb*Xt1z]30S%з5x^-ОY1Ȓ!dZ!1qxLL /LLo$D˄Y!k0`6fi3bȎbgtKNc-x1ޓ-&Lm6[<8hehm6R'?'_`_`}]dWzxgx91@srm'3= ˞L,BXl eLôsemkf.Yؠm&mE*7<ߘMLfOLDmOD׶`36crZ޷)5:kOYź[z[Z[:[:[:[`^k0&O$N[83O9Ks"1e"1 e8 #b)QA10c9̘#Q9*&п uB;i1Nc10*C1Z8-2Oɠ I`"eO)Srz]C҇|>e\>em(X>|}hr&"Lk㴱b >闒ce Zy>ǹ$O-y_~s|=+_sOyL]eee>g֗7 W:bu)яKK_yWe^w}O@?Џ\\1۷/3 _39UeYڒESLH+bgWLF%2Rʞ51'[N7s˯ع?RʥT&&_2ʍʋҳ;rɌY 1f:Lfd2"uى%h;NRK ˈ0 0 su|3yUy֎Y? G^"ͽ(Q=^(=u{K=?1o7bqt;n߲bx^TfPd4ݎQtA#&b-OЛқ;sq7pƼaŶ#vڎWGU0u(uYNf-qصZ;R6i;m"~ڎ<~dD"ǹEGD{_59H#CDr3ȭ5`DiMt֎X;Gu| Լ[#jW8'c&&jb&j>#cvstpfdT32i"~2="Ji"&i"~ю6F2uW+8͋)7b©``nb/ rq*\8{Tplh F_nQ?;◕#g|vώ^dToxF8-G[y]lŎj_hB#;m IT _''vrhͿճa߻bWc'T.M|B^bզÔ(U%_%SfJ֡T|l@F*ٔRUZd{JuW.ꚯ(TJT|O*A|Ja*9RUr&dJMR2oBw2;->yNj@ov( %EK%IX.禄 뜿&lq &lsHJN8#|O9rs9|Ys9s,3wpu/m+) տhǞz2FFLxDl Og }~:Qxa~9ϯo3AݔO=Ph _}_f1R4ېN3kK~_1L_~F_4!ENxNxM Ymo,sky|ǍbsnOy6{7JʽU^5˹GG:ʇR9)Cd1IZNI@qaNC`~% &f"Ct ?ыJO@btSt/藫x>?I]%Rf֒S"]NYk@Ӷ2ȤJPb(μ(%Z)1~($S+6i~>֋=Eiу]%EwN_\xN)A_eF/D:g"@>A@&f|YWqTrhz)tÓGyr+t.݅:fg@gљ>\E l:!2%QBChS޼Y/ۏN?GhՏc.2}csއWzs=^к)*u=AOP8=NjYYˢ|;3XY3Ӯ2~ OCEg16\ttFh#1 Eg X&NbLtq&$9SS^>~H2Ȥ,m0Y{֢2XXhHØaPGœ f@ @?GPї21 D*i5z2^dw%g1} =OI8tXm Fkޯ 2Q0F;ho(ڭ)?c -bOWY G[\mE<)P\̣|,`,Oδtϲv]E"s eB/SeX.s%VukV['9 F)O9ue|O>㞢ep:؇{vcew!s0cB&2AH@G.1 dPOE`ps0}V迒)Q1?F;臾޼_酞:\ߝ~(=Wݢz3.][߅qq\J\ v.Yfnw|!ȓiГ.>c;x[<\.h9Xz[zYzZzxq"=E{.3.Z.Ys{SǖO臯~z|| z-QX\#>>Gc'S1В|eh,ޫ ֆOPF)=]q yrP֛2 FsFd:sj:~p;˷e1L|c34W7X2δ҃J2-Yb>s{rD.Ss|ߕ{~d/ճ#z9y&[/3;;Zgt"tv  %gy&g]F1^9bu8EbKQ^DibELDD쨙%?*'Vǎѳ`,vjf&ffbe&NfbdXo+&fa&fb`v+qȘwq.-V3k8hX(2FLˎt.KT+k ƳXȸUtjcSd\*.,2ѫ_.jTT^"R vLʎE8?q';[)V)2`tɎ,$Q$;z+rˋ萉 shO|}+syMCiF!0>0CFh(Z}ViO#尨E;1 )88G=4Zck yj9XS9[y>ӆuІ{yvi;-oz&l<ۜZIlu:t:FI[A,铱z+?' FnG~GGG1QguQ3FӶ2~?cg&`"cV&a2hSiGe ϛLNgإvf=ث~g}w!qI{ o4PY3)\/eیm6/1b,LjY NN05c1Iʾ8#Sv<} Ӗk@.hE%wRYyyٳyE9 0NeiYV?9 u\8|G;h;w|o|e>W%cW>?)S|s#|H>(Qﻴ.CЇw;I>~P9u}L]'T\r+N$N8v_2̳8@{ >u#q+Ḍ?b~dN8v$$$$j/p+W8'~-ZxsWո x-xB,d;QGaO\AOJ4ϾD*\JFcn\~C{&K<xCkxW‸ZJ+D-PHsq$'! s>I<.'oߊ߉Shgs8/~MEϯ%ҮN|[\H8Z!+ޥ=KՏܯg\멘-&ˍ Ad|-pV;^+ovO+gG17fKbKDC^nxyWŋ#v&Zg"u:;:GHّ`͏-flD"j[y1W+zOx./~fev:Y0;f]1.;uXV|gYd,˕b_^~+g+vOK52Q.;e"[9#Z"Y(O2Ѫx#UF2ۥCZbZųDz~8cWyYoŁHLHEF(Կ6KFO(OĪq*;>sbSvL*z,*qD(;y8h2ѥ葥="=+3-'EƑҡ+č"?FdbCv<ȏqV'g' d S'/Gw"#;&c9vJ+Eo^\)cx9u1q` ǏDĞ2ћ^Dg2v$ƏœJ1$~TďG8H D6 0Q #""W&GAB Bؑ;"G÷wX;hy7;x{ʨ +M>zk?s}?h=b]ϝwI _X}oqC誄 /$L:! Z Růheǩ ׈-Yz^Uw w'K;D;;D۵۴[[7&Q!Fܠ]C](jejIђ=2OJSRn?-h%`;p'N(C1KQK) QvܦݪݢݬDFQD+bZQT+f~B7ōM [qv;r^ڰ' Y [$SR ŵ$$#((gyR^YTx{B)Ƥfv܆[-͖nA7(RXznMrvv(G]wX..2F%iטǹ ^Dօ~Q4BAVVR+% JQ[IZ0JXPD+/UܪݴX jߗϚROdu>d_}ל\ǸvWLB%OYQYUZ5zP]JM6uxЦQ[$S>RMD[I몆\*UQOɄG>bZKaOEQmH,JKUV]jQ^4O!QW֊hE6-_"ihW]UժhEaQ)BQUeQP*AK_WM{-R"RKbdKuYE(/h+UsTCUʨDy֘ kO)ǺTʢ Re*VRƟCORQ_7FOS۝>`'BoaE? D:zx !Y1!;P=vPo(8¼RQe).AUӆkkDI@m~^]ceQ"HJPCF咑Tы2Jo+}K[)8TII{IU&suR2 Rļ*̱2PBꇾZ^Wz"_)xb(.z^ϵ5r#(?FPHI[hw#OqɘӑMeṳ.e΢ٴ3~Ϧٴ?g3D*ʠ,erbHfm(1Lզ`x=D?w_qg<ۃQ)ǘˢ s$#)AhQ2IHF RX((.e&k2OѦ[)8TMٴM{ٴMU6suR3әKeLe)]Dm2NJQƣCq.)P"Rʿy(A:vSuQ.aPߟ!R2T0r{8Nh'qJ{K;^9cW)b_j &\Baeu, Ns-O$Pע :VR($$z R2-+nhux~n[DYwP+q&# Q %QyVE(>׭\ fܤݨ)\G]b(8Kh%eyE k(p?m~BVL+N1b(uD*J_mŴ(JA9=LD9Q02Z*s%B"S㹿~WĿnO''ޫݓ9+㮔\rx)Vl]\]OJ˝f,喕32V-9"g,ɜ1bE#`vdLd 3[-;-{e2W&keU&K3Pv)V*,RW\ݻCNVn%zy"29! @&I;Cm.ZvduL36&[c3~&ϼDfZrϰߏR%v-eH gWLfdTL6dQI@$z&$3 V3 0 00u32 ~,l" 5(er~#v?Ŋڗ&Bo"vD^Ad&۱w;nb&u?xK&nbbv܎qcJ+BܣerDȹ#GʋFDMdDM4܏أ!o}/mǺMێq5̙6~wnQ+EMDnݾRT;v|_66kWĬ㟹2sECEMt;ZTDsf_)m&z\~߹R:Z]ä4V7ȸ`@@I4NUTL'FյVU]20fDQ}_ppؗf4:qD#{{NSO?=V:Rܭ :5Hk)A->HϹR熉s>is3QzȖOٴIwrYZ+WS왩(5Kc7?5vc7-ABRa= V Jo+p@gKωUFa gf7_71WeތWvJvޒ7ճ\d*U9lLcU2Xo]UYUg=i- RX=ۏ{t>owuYY˲c;ڡvc ;rmYt{6svGM}pGzSm]ƞg;1}SW޵3_=doR{ڏ>ޓ(h[QҶ *WsjeJ_ jiגxy d[kGՙ~iPٕ5!?lP#lDC *oǬ[q^ՇTZiA]49A/)~*2|4B!1 ŬTҠ2;FZ#0!TŏN tF~BTL-ʠV>s *{ԗZT4|wtFZOП *{0:ٌ}([~ѼJ7?OAML&[ 5N}hF *P ޝ 򹦬A *%4TMs4ӠJDezT>H34 *Pr_SAoOCIpWcAC *1y7=݃2,0H *C"tAe%h~e)3Π *+ZJP$uJjgPYEmNimP9.9:ˠr4ƐmP<@_%@_Xc/ָFL_L=stʚFt}|Ͼε/4k|K4~g RP/ [_QC@ Q@;hO}?)BE")¸0>?ǘ1v!1!F!%P)kiʡ$#T2ºْO*J-X!Lĸ$/$8J"RWBhGmG _(',q;, bvE/{-2BkͥK5i.LfdҤJu>0 ?&XHHIMeRn6u#zƮzq 1W7%\kuky5t:{txxm;Ve@[Wݶh-i>۠6v9J%(^Bs{Thc \ XR19EuHע ƺD:%̺D.hC謹 :IYt\6hR Jʠ* % ɮ*BGĪп3Ka|s%1G!q"sK]kz=)\ym=VO`X1Y"W'o ;;ўppA,1HPP!!a]a8&xnRO+τޚX5Vz1=1֞kO'1:dY 8*υp;dYsJHq\ˡ Jb0BRm9X~W#P+B[T|Tk{/ΑXփ=i"`WQa~yDCzHQ)&`nB)y}mn!{~YCٓH0|FfbA8EX"GػGcOp@ acpN{1CEx I/+01?%cBsZa5^kBhGW5Nz ^u , aE@Ta%P ex^$Q'T%S_'_^ƳJ6%_eh{)|Kp |K6yRnt.O&e*mmvcYvG;I3n=n}:Vʬ݌ZϤ9ts3dFbo>gXOdXOD؛7WO~Wnҫz&z'nr;U)7Y78jwk*$V*ՓW՛ ɪzxFTԴT%*WC8dTLYifNTFn.*uO=l{M\ɧJ=UکMjzL`M%#Z ;o,4i%wb]0қS|RnҲlcē1ff*?Tz9a~RדB 4PnWiiyܣ=ģܼMem*g3|ւ935ohn~fzVtF|T&0X+QtDKT5d+wMMTbTsD*[&QtMT$7=S#=-ғ"oB&C* HO}T&;a-ɕ鍞8LGx  g^Fff3nLg.*gQ7WS%&͹ǛD%%*!QJDTR7&zM<IJ8T'C*ГW`9dL#[e$ Ce*PyEaFߧz 6gO ^_Zn[H<~ZkETg/6N{M~ *+hmz{5T{A1[ *O]kPeP>T~bOmD>LT~a/_i#f>%}fyp_AygNپo *yfԪ|v+_K |g1}./]hPw}Ae;_Ae_;Aeg_G*ʮk|] *}ݛ5gfbw@u(1`!r n+"0 8NxV]hW~}_ҤIV-5 6hjK-,'.VI^ae(t* fC*S/]x#n@_ =ĭ#`|9<|D#.?6͒<8l=ȿzEj`U]"ZwLSaVV=&я/ _c?`^d%2Jُ' d r-2692L"/s}_ $-&:Puoc.os[km4MT#\Es2p.0iSOpLj;L?y7H޽C"TRuZSGq%maɝe%ma&u[[P [ږe̥-Zm xnϴW1s>G,|a Yy4:ceoL|a)?3-ԟ%|@ٌ_.%ָAVq>k6)\RdJl3JVg*F=nH,!x+zx]fLTE[o&zKL~B29dKC[V8CjNNZ;m#͜oz=quүU R'@|Q8Slo-*,j DsbIGY+R(M?wG*Ofx*~ 죦o*0P3qwyg=ԙYǺw&R7Q9y)c=ҷ^e" ׶ (0   00bCorelDRAW &CorelDraw.Graphic.70CorelDRAW0bCorelDRAW &CorelDraw.Graphic.70CorelDRAW0bCorelDRAW &CorelDraw.Graphic.70CorelDRAW:0bCorelDRAW &CorelDraw.Graphic.70CorelDRAW0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAWr0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAWo0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAW0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAW/ 00DTimes New Roman$b$b0b0TbTb 0DArialNew Roman$b$b0b0TbTb 0" DCourier Newman$b$b0b0TbTb 010DSymbol Newman$b$b0b0TbTb 0 @ @`  @n?" dd@  @@``  ~ p      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTVWXYZ[\]^`abdefilnorstuvwy_dhqrltu  _ghqcjkx{}|~mpzp}|~`2$"xܓ1oTz2$ļ&&s+/Tz2$rp9)`z2$|@C}IRիW z2$v:TEjmz2$M15}韙G %;h+z2$(@ uE1xfcz2$r n+" zS ~1?@8  g4:d:d`b 0Xbf\ Tppp@ g4*d*d`b 0Xb, ppp@  <4!d!d$b$b\ <4BdBd$b$bf\ B?, Printing LISA 98 (c) 1997-1998 Patrick Powell http://www.astart.comO =i ,Managing Network Printers and Print Spoolers--$Patrick Powell papowell@astart.com Astart Technologies, 9475 Chesapeake Dr., Suite D San Diego, CA 92123 619-874-6543 http://www.astart.com ~ZX%Part 1- Printer Hardware and Firmware.Printer Mechanisms Host/Printer Connections Serial, Parallel, Network Configuration and Problems Network Printer Support Print Job Formats for Vintage Printers Print Job Formats for Modern Printers Page Description Languages PostScript and PCL Job Control and Format Languages PostScript, PCL, and PJL ~,Mh!,Mh!Part 2-BSD Print SpoolersPrint Spooler Basics Print Spooler Client-Server Structure BSD, SVR4, Novell, MS RFC1179 BSD Print Spoolers LPRng Printing Process Printcap Files Filters Printcap Examples 6EUEU>m  ,Part 3 - Managing Print Spooler Operations Management Facilities lpc, printcap Adding New Printers checkpc (lprng) Installation replacing print spooling system Diagnostics Load Sharing and Printer Pools Bounce Queues Routing Host Specific Printcap Entries~ ` `b ~ Part 4 - Horrible Problems8Permissions Security and Authentication Accounting SNMP 8-Part 5 - Multi-platform Printing and GatewayskNetwork Print Spooling Drivers and Print Spoolers Printer Gateways Microsoft Print Spooler Windows 95 WslprePart 5 - Cont d zNT LPR Support Novell Print Spooler PCNFS (Sun Microsystems) PCNFSD LPR Support Samba Samba LPR Support Desperate Measures, ReferencesPostScript Tutorial and Cookbook, Adobe Systems, Addison-Wesley 1985 PostScript Language Program Design, Adobe Systems, Addison-Wesley 1985 PostScript Language Reference Manual, 2nd Edition, Adobe Systems, Inc. Addison-Wesley, 1990 Portable Document Format Reference Manual, Adobe Systems, Inc. Addison-Wesley, 1993 Web Site for Adobe and more documents: http://www.adobe.com/supportservice/devrelations/technotes.html d@>y  -SoftwareLPRng ftp site: unless otherwise indicated, all software can be obtained from the ftp://ftp.astart.com/LPRng site. See the README and INDEX file GNU software can be obtained from many mirror sites. Try ftp://ftp.uu.net/pub/gnu and look for MIRRORS PostScript Utilities psutil31.tgz More PostScript Utilities psutilmore2.tgz GhostScript GNU GhostView GNU Adobe Acrobat PDF viewer/tools www.adobe.com LPRng LPRng distribution FILTERS_LPRng collection of filters, patched for LPRng XiB( 1'#T%Part 1 Printer Hardware and Firmware&&.Basic Printer OperationA Printer is a peripheral device, usually attached to a host computer The host computer transfer print files to the printer over the communication channelPrinter Communication ChannelsPrinter must be connected to source of print jobs Channel determines the rate at which text (characters) are delivered to printer Also determines the reliability Also determines the availability of error messages and diagnostics Simple and Cheap is not always best Serial PortSlowest and most error prone Older printers had a very slow transfer speed (9600 bits/second) due to the real time requirements of handling paper Newer ones can go up to 115 Kbps Data transmissions errors VERY common Printer does not have a large buffer to store input so flow control is absolutely required Hardware (RTS/CTS) flow control almost mandatory Software flow control (DCON/DCOFF or CTRL-S/CTRL-Q) can be used over networks Watch out for buffering in network!%Advantages of Serial Port Error and Status messages available from printer Most systems have multiple serial ports, can attach multiple printers Can be put on terminal server (watch out for flow control, enable RTS/CTS) Cables can be up to 50 feet long Cheat and they can be up to 200 feet long Disadvantages of Serial PortVery very slow Did I mention flow control problems? This is a major headache Errors in data transmissions can have interesting effects Note: Software Flow Control Headaches Some software flow control tries to accommodate errors by restarting transmission when ANY character is received from the other end after a time-out period. When you get a printer error, the printer sends CTRL-S to stop job and then later reports status, you lose rest of job. (Headache #27)$%% Parallel Port - AdvantagesTransfers data 8 bits at a time Flow control implicit because it uses a hardware handshake for data transfer. Very low error rate on data transfer Parallel Port - DisadvantagesHas limited bi-directional capability Out of Paper, Error indication Bi-directional support limited on most interfaces May not be able to indicate error condition when flow control enabled Surprisingly low throughput due to interrupt per byte on most PCs and workstations Games played at OS level to improve this Real pain is getting status back using bi-directional mode Real cheap folks can even use print sharing boxes You get what you pay for Don t call me when they lock up& 2FS)m9& 2FS)m9> 7} Network InterfaceUsually supports various protocols IPX (Novell and Microsoft) NETBUI (Microsoft) TCP/IP (LPD + RAW Connection) Printer builders are (or did not use to be) network aware Has changed tremendously over last couple of years Configuration of interface is now usually pretty simple once you understand the various options Front panel configuration is really quite simple HP Jetdirect has BOOTP/DHCP configuration 6#L)#L)o   Network DisadvantagesAnybody can print on the printer Leads to the mysterious print job from nowhere In spite of being on the network, only one person can actively use the printer at a time Yes, yes, I know what the documentation says and I am telling you what REALLY happens Multiple users can/will/have locked up the interface (this is a known problem with HP Jetdirect interfaces) Multiple users trying different protocols can/will/have locked up the interface When the printer dies there is usually no handy way to reset it without powering it downl!/ZV!ZVU !Network AdvantagesVery high throughput, and has built in flow control Very low transfer error rate Ethernet has CRC Higher level protocols also do checking at transport level Can be configured using BOOTP/DHCP Status can be obtained by using SMTP on most systems If DHCP working, can even reboot printer Printer sharing becomes very simple, in principle For shared system resource, may actually be the cheapest interface as it does not require host for support (see LPR/LPD later for details)ZQLX*QLX*"Hybrid Solutions6Print Server Boxes Has network interface, supports parallel port and serial ports for printers and/or modems I have used several different ones with various levels of success Lantronics - works, configuration tricky, good functionality Rose - works, configuration simple, limited functionality Dumb Network Interface (LPserver UNIX/NT/W95) Extremely stupid program that turns your PC into a TCP/IP raw socket connection to the printers serial (bi-directional) or parallel (unidirectional) port This has its place when doing accounting, very tricky printer operationsZw.w.> vy#EHP Jetdirect Configuration Similar in principle to most printer network interfaces Front Panel Configuration Enable basic network protocols IPX/Novell Print Spooler DCP/Microsoft TCP/IP Set IP address, netmask, syslog server Enable BOOTP/DHCP configuration BOOTP/DHCP Configuration BOOTP/DHCP supplies IP configuration information Specifies a TFTP server and file that has detailed printer configuration information~R.' R.' ,$FJetdirect Configuration File # Administration Info name: picasso location: 1st floor, south wall contact: Phil, ext 1234 # Only allow connections from network/netmask or host allow: 10.0.0.0 255.0.0.0 allow: 15.1.2.3 idle-timeout: 120 syslog-facility: local2 # SNMP Configuration get-community-name: blue set-community-name: yellow trap-community-name: red trap-dest: 15.1.2.3 trap-dest: 15.2.3.4 authentication-trap: on` N5Ut5,Ey#%GJetdirect Restrictions The  allow configuration parameter enables you to restrict access to the printer from the specified network/subnetmask addresses This is essential to controlling access to your printer You need to use the BOOTP/DHCP to set the IP address, gateway, and syslog server Note: Setting up the BOOTP/DHCP server can be tricky. You will need to either have a DHCP server on each subnet, run a  forwarder process on a host on the subnet, or have your router forward requests to a server. (Hint: if you have Cisco router, use the  ip helper command to specify the DHCP server address.) bm r-a7&|Direct ConnectionsTCP/IP Port 9100 on the Jetdirect card is a direct connection to the print engine This is a bi-directional connection, and error messages will be written on it Other network support cards have similar facilities; if they do not, then DON T USE THE CARD Usually only one connection at a time can be active; this can cause problems if trying to share the printer among several different hosts, :,'ISNMP Simple Network Management Protocol provides a common interface to obtaining information about the status of network devices. An  agent process resides in the network device, spies on the activities of the device, and provides information when queried. Agents can also generate messages (traps) when a specific activity is detected A  manager process queries agents for status Agents can also  write information to the network device when requested by a manager. This allows configuration management to be done by a manager.(HSNMP To The Rescue (Maybe)6The SNMP standards include a Printer MIB In principle, you can use a simple SNMP manager to query the values of the Printer MIB. These are, to put it mildly, very basic. In practice, most vendors have extended the MIB to provide more detailed information about the error conditions, status, etc..... Unfortunately, most print spoolers do not use the SNMP facilities to query printers The common SNMP managers such as HP Openlook, SunNetManager, etc..... have the common printer MIBs already provided. If you are an administrator, please learn more about managers7Z7> "R)UVPrint Job Formats and Page Description Languages OR What Do You Send To The Printer?WW*Print Job Data FormatsCoherent and organized approach to this subject is impossible due to historical development Will give a rapid, functional, biased, opinionated, subjective, etc.... view of the subject Remember: each manufacturer tries to distinguish their product in the market Remember: THERE ARE NO STANDARDS Actually, this is a lie. There are standards. Nobody follows them :-) Remember: The printer with the largest market share becomes the standard Thus: Every 3 years there is a new standard.@'Hv'HO'+(Vintage Printers (Impact, Daisy, etc...)*Fixed size characters (Fixed width/height fonts) Character set at the whim/market demand of the manufacturers Page dimensions based on paper sizes 8.5 x 11 inches (letter), 11 x 15 (computer listing) some larger size (legal) whacko A4/A3 sizes (Europe) De Facto Standard 132 columns and 66 lines Unless it was 60 lines (margin at top? bottom) Or 80 columns with 66 line??? And don t forget metric sizes...Hjj,Printing Text on AntiquesSend characters, print characters. Simple? Wrong. You forgot about INTERNATIONALIZATION $ are simple, try typesetting French, etc.... How about EBCDIIC? (Don t laugh) Tabs? Support for tabs? Why? Smaller text files, good for limited file systems Harder to change to other manufacturer s printer De Facto: tabs are at 8 positions Market forces demand features Italics, true bolds, Condensed Fonts Need to have escape sequences to enable these THERE ARE NO STANDARDS3V@j3+@ '-Advice on Antiques(There are a lot of them out there - they were built for abuse, they run and run and run Get the manuals, make 3 copies, and save them for the next admin - they will probably still be in use when you retire Most new impact printers are extremely simple to use Very few features Modeled on major (antique) market dominators If you have to print multiple copies on forms, this is your ONLY choice, so make sure you choose wisely Daisy wheels make best multiple copy impressions Dot matrix are faster, wear out Keep a spare in the back room for parts6??.PlottersOK, these are not printers, but they turned out to inspire the next step in Page Description Languages Original plotters were Analog, hooked to instrumentation. Drew nice curves on EXPENSIVE green paper Market developed for plotters which could be attached to  smart instruments or computers which simulated analog plotters Drew backgrounds, labels, etc... HP (and others) developed several Plotter Control Languages Had embedded commands for drawing textl*z!<'*z!<'/Raster Output DevicesOriginally electrostatic plotters, would draw a plot a line at a time Needed to convert Gerber (or HP or IBM& ) plotter stuff to raster format Insight that they could also produce hard copy of text using various fonts TROFF meets VARIAN to produce $$$ of revenue as graduate students burn up 100s of rolls of expensive electrostatic paper0 XerographyXerography works by whacking charges onto a drum The whacking is done by high intensity tightly focused light (laser?) Generated by scanning original mechanically OR BY SHINING A LASER ONTO THE PAPER AND TURNING THE LIGHT ON AND OFF UNDER COMPUTER CONTROL VOILA! The Laser Printer6vv1PostScript OriginsDeveloped originally as part of 1975-1980 research into computer graphics Origins in computer picture generation Text, fonts, etc..., were wedged into the language, trying to import concepts of typography into the programming language Model used was to have dumb programs generate PostScript, and have a smart PostScript converter do rasterization Adobe produced first PostScript Language definition, and the first PostScript printersMX2PostScript Description*PostScript is a Stack Based Programming Language You don t want to know. You REALLY don t want to know. If you want to know, you are warped. Or a Computer Science Whacko. PostScript files are programs. A typesetting program is a Program Generator that produces programs for another computer that runs to produce your output. Are you starting to understand why you have problems with printing PostScript?3PostScript Printer ControlSince PostScript is a program running on the printer, why not give it the ability to control the type of paper, paper trays to be used, duplex printing, etc...? We do this by having the PostScript program set values of various system dictionary variables (I told you, you REALLY don t want to know). After the job finishes, you should really revert to a known state of these system dictionary variables Right? Wrong! You may want to set these to be permanent for all jobs So we have a password needed. Guess what the password is? Right. You guessed it! (Answer: no password)4PostScript DisadvantagesSince it is a programming language, it comes in different versions This is mandatory, and is Bernard s Law: Never trust Version 1. It requires memory to store the program, to hold temporary results, and to do rasterization, font conversions, etc.... Thus we encounter Booth s Law: You never have enough memory for a graphics program. There is always a bug in the compiler/interpreter Even correct PostScript code does not always work correctly when the interpreter has errorslDAyU2\DAyU2\5BPostScript Disadvantages (Cont d)qSince you are running a program, the page generation can be hideously slow if you are doing fancy graphics operations such as scaling, rotation, etc... of large bit mapped graphics and fills When a problem is encountered, you have limited diagnostic capability. Ghostscript Is Your Friend - Use It Watch out for PostScript Interpreter Version/Level/Revision Problems ,jj  _6PostScript Advantages$Incredibly portable across different vendors printers for text/picture generation Not due to the language, but due to Adobe selling the code for the Interpreter at such a low price that everybody used it (Not true any more, so we are seeing some interesting PostScript bugs). Now everybody needs to be Adobe Bug Compatible Remember: THERE ARE NO STANDARDS Previewers can be built that will give you an exact idea of what your PostScript will look like De Facto the standard, most portable way to generate documents for printing6E"E"f 7PostScript Books`PostScript Tutorial and Cookbook The Blue Book Very easy to read Use Ghostscript and learn about PostScript programming PostScript Language Reference Manual 2nd Edition Almost incomprehensible; it IS a standard, after all Appendixes are VERY helpful You can get a copy from Adobe s web site www.adobe.com`!W%!W%D 8Embedded PostScript (EPS) Self contained PostScript files that produce a graphical object when executed See PostScript Reference Manual Appendix H Version 3.0 is most common Almost all graphical object editors import and/or export EPS Some Web browsers even have extensions that display it${{9+PostScript Document Structuring Conventions,,PostScript Reference Manual, Appendix G Version 3 is most generally used Specifies how PostScript should be used for document generation Prologue sets up overall characteristics of document, such as fonts, subroutines, etc... Body consists of set of independent pages Each page can be removed/duplicated/inserted in document without altering printing Standard also provides guidelines for information about documentH(!@(!@: Tools For PostScriptAssumes Document Structure Version 3 PS Utilities Version 3.1 (psutil3.tar.gz) Set of UNIX tools for massaging PostScript file psnup will print N virtual pages per real page psselect will select pages to be printed psrev will reverse page order PS fixer (psmoreutils2.tar.gz) merges pages of two documents, good for putting a background on a document Selectively places/orients individual pages or sets of pages on a document desperation tool made from psutils, cannot live without itPOZZZZOtK1)  4;!:Tools for PostScript (Cont d)Several tools available that allow you to add documentation control to PostScript files For example, you can have the first page fed from a special tray, and then remainder from another tray These are usually commercially available, but freeware/shareware versions have been spotted. Some commercial print spoolers incorporate this functionality into their Print Spooler filters (see later) See the LPRng ftp/web site for pointers6X0(X0(<"Binary Communications Protocol"PostScript language specification states that the program must be in printable ASCII characters or a limited set of control characters However, you can embed inline binary data into a PostScript file using very tricky methods Some control characters normally cause a PostScript Interpreter to end execution, send status, or may actually be ignored. The Binary Communications Protocol escapes these control characters. If you have character C needing escape you send: 0x01 C ^ 0x40 e.g. - 0x01 (^A) -> 0x01  A or 0x01 0x41$,,=#%Tagged Binary Communications ProtocolWarning If you have a file with embedded BCP escapes and you redo the BCP escapes, then you will destroy the escaping This leads to the Tagged BCP protocol We add a ^A M sequence (this is an escaped CR character) to indicate that we are doing BCP and have put in the escapes When we scan the file and detect ^A M we do not add more escapes This is one of the leading causes of problems printing PostScript files with binary images produced by some graphics programsZo&~o&~>9*Postscript Printer Description Files (PPD)7You can use PostScript to manage and control various printer operations What variables/operations/values do you need? Each PostScript printer should have a PostScript Printer Description file that contains the various PostScript manageable options and how to set them Example: HP4MP - setting manual feed *ManualFeed True: "1 dict dup /ManualFeed true put setpagedevice" *ManualFeed False: "1 dict dup /ManualFeed false put setpagedevice" The contents include the PostScript needed to set the required operations Easy to extract from the PPD file and send to printersjHZ.ZZZZH.2        ?: PPD WarningsEach printer has a possibly different set of commands, so check the PPD file for the printers Manufacturers are not required to provide PPD files, so you may have to dig around for them. There are many extensions to the basic PPD capabilities, some of which are very specific to a particular printer. WARNING: I have discovered discrepancies between the PPD, manual, and actual operation. RTFM, and try it out. WARNING: some options interact, and will lock up printers. PPD and documents do not cover this@$HP PCLHewlett Packard developed a line of laser printers and wanted to sell them to the various printing markets They also did not like paying Adobe royalties and licensing fees They invented the Printer Command Language (PCL) Version 1, Version 2, Version 3, Version 4... Currently we have Version 5 and rumors of 6 PCL is NOT a programming language, it is a Printer Command Language It specifies where on a page to draw lines, glyphs, and does it very well.6ZZA;PCL ReferencesPCL 5 Printer Language Technical Reference Surprisingly readable, but BORING, repetitive, and written in a horribly verbose manner. Read and memorize Section 3.8 Resetting the Print Environment \]E and \]%-12345X are your friends, and will help you keep your sanity Note that this sequence, when sent in a PCL file, will terminate job printing and may have some surprising consequences Read Chapter 4 - PCL Job Control Commands This will explain most of the printer control functionsl+Y*8+Y*8B% PCL EvolutionOriginal versions of PCL did not support downloadable fonts. This was supposed to be a feature - you would buy font cards and plug them in. User feedback (flames) convinced HP that they needed to support downloadable fonts Then we had raster graphics support added Then we had page structuring support added It now can make the printer sing, dance, and do just about anything (as long as you know the correct PCL commands)C&PCL DisadvantagesOlder PCL printers do not have downloadable font support You may (again) need lots of memory for fonts, images, etc.... (There s no such thing as a free lunch) It uses control characters (ESC) for many of the commands, making it difficult to fix/mangle/edit PCL files without special editors capable of handling long lines, control characters, etc....D'PCL Advantages0Simple simple to generate Slightly smaller files than PostScript Very much faster page generation, as there is little to do except read the input and copy BitBlits to the graphics memory Even has commands to do the printer mechanism commands such as bin selections, etc...., built into the language now. E>Mystery PCL CommandsSince each printer has a different set of capabilities, you need to have PCL commands to operate the printer There should be a  PCL Printer Description file for each printer, right? ANSWER: no Each printer should have a document providing a complete list of the PCL commands supported, right? ANSWER: ummm& right& but only the development group has that information, the printer is not in development any more, & Situation is getting better, but documentation is still weak point for specialized operationsZ dz^ dz^.F< Quick TestFIf you are using PostScript and TBCP, what is the end TBCP sequence? ANSWER: \]%-12345X If you want to make sure that your PostScript job gets printed, even if the previous job did not end with the EOJ string, what should you put into your file? ANSWER: \]%-12345X before the PostScript If you are trying to decide if a file is PostScript or PJL or text, you might try looking for %!PS as the first characters, right? ANSWER: Yes, No, Maybe? Sigh& It all depends on how smart/stupid/weird the writers of the PostScript generation program are...rE)E)G?&Quick Test (Cont d) $How can you tell if you have PCL or PostScript? Ummm& throw it at the printer and if it works, it was PostScript GhostScript can be modified to disregard the various PCL sequences. This makes life much easier when trying to preview files produced on MS/Apple based programs60A0A,/< H(Portable Document Format (PDF)PDF is basically PostScript s version of PCL It is very simple All the time consuming PostScript operations are disallowed There are some minor HyperText things thrown in to provide some previewer help Concept was to generate your document in PostScript or PCL, run it through a PDF converter, and you get PDF. To print, you expand the PDF into the more verbose PostScript, add the Job Structuring Conventions, and you are done Adobe now sells PDF viewers and translators...6--, ! I@ Printing PDFlVery few (none?) printers will accept PDF files You need to preprocess them into PostScript Ghostscript should be able to do this Whoops its cookies on many PDF files You can extend GhostScript to handle PDF files now with reasonable success, but you will be violating some patents, restrictions on encryption, etc etc. Look at the GhostScript web site for details6\K\K>[ O  J)Portable Job LanguageNow printers have to support PCL, PostScript, TEXT, and who knows what. Documents need to be printed using different papers, formats, orientations, etc.... Need a higher level language to control this type of operation, overriding (perhaps) the operations in the document Portable Job Language was intended to do this Most important feature is ^]-012345XPJL EOF This causes a functional reset of the printer Cannot be ignored, escaped, hidden, etc.... Restores sanity to the printing world$kkK* PJL FeatureslProvides ways to specify the Page Description Language PJL Select Postscript Provides ways to specify the orientation (if the PDL does not override it) Basically, provides a way of overriding the PDL requests And most important: Provides a standard set of error messages to be returned IRREGARDLESS of the PCL This last feature is worth the pain and effort of PJLl7Q67 (6L+PJL DisadvantagesNot all PJL features are supported in all printers Printer vendors are very closed mouthed about what they support. There is no  PJL Printer Document standard for PJL HP does not provide details on all of the error messages, leaving it up to implementers to discover that there are a whole new set of messages concerned with various printer operations Try getting a paper jam in a multi-bin feed printer and see what messages you get Different releases of printer EPROMs support different PJL sets HPXXSi are notoriously different from other HPs And of course - different vendors have different messagesdcPRP@P0P:PcR@0:>%;MA PPD Meets PJLVIf a printer supports PostScript, there is usually a PPD file for the printer. HP has very nicely put in the various PJL and PostScript sequences needed to perform the various printer control functions in their distributed PPD files. This information is not documented of course& but handy to know NVPart 2 Print SpoolersO/Print Spooler BasicsUsers create jobs (print files) They use a print client to send job to a print server or spooler The spooler then transfers jobs to a printer Multiple users (clients) can transfer jobs to a server A server can transfer multiple jobs to printersZP4"Common Print Spooler Architectures+LPR/BSD UNIX variants include PLP, LPRng RFC1179 documents client/server protocol TCP/IP network based LP/SVR4 UNIX Proprietary client/server protocol Novell Netware Semi-proprietary protocol IPX Network based (or IPX over TCP) Microsoft SMB Basics documented, but details are not NetBUI, IPX based Z #>: Z #>:>"ut Q6LPR (BSD/RFC1179) Architecturelpd is the print daemon listens on port TCP port 515 for requests from lpr clients uses printcap for configuration lprm (job removal), lpq (job status) communicate over port 515 jobs stored in spool queue on server host6[i[iZC RR7LP (SVR4) Architecturelpsched is the print daemon listens on /dev/printer (FIFO) uses /etc.../lp/* files for configuration lpstat, lpadmin, accept, enable used to control operation jobs stored in spool queue on server host6IdIdZ!US8 Novell Print Server ArchitectureFile Server is the print daemon files placed on server and requests made for printing from clients uses database for configuration printing done by server process on file server or other host management done using admintool or other facility jobs stored in spool queue on file server< ZcZZ c =TBNT Print Server ArchitectureServer is the print daemon files placed on server and requests made for printing from clients uses registry for configuration printing done by server process on file server or other host management done using control panel jobs stored in spool queue on file server<ZcZZcUC ObservationArchitectures are almost identical Should be easy to understand, right? WRONG You can set up the various pieces easily, but the problems start when you want to do more than just fling files at printers Accounting Restricting access Error logging and recoveryHH}9H}9VWRFC 1179 Printer ProtocolW5RFC1179Documented the original BSD print spooler network protocol Incomplete, inconsistent, and open to abuse& I mean implementation inconsistencies Only common, non-proprietary, open standard available today This situation may change as the IETF has a Internet Printing Protocol (IPP) working group making progress towards a new and sensible standard6o?XXBasic ConceptsLPD server listens on TCP/IP port 515 for connections from client programs (LPR, LPQ, LPRM, LPC) and other LPD servers Connections originate from port 721 - 731 to server (Privileged Port in old TCP/IP network software). Clients send requests, get confirmation and/or status in return Request can be: (LPR) transfer job (LPQ) get queue status (LPRM) remove job (LPC LPRng Extensions) queue control$.c.cpYY Print Job%control file contains information about the job submitter and the way the job is to be processed by the print server names the data files for the job one or more data files control file and data files are transferred in binary form from client to server server must interpret contents of filesH h( h(ZZPrint Job FilesFile names have defined format cf X nnn hostname - control file cfA001patrick cfA002patrick.astart.com X is a letter indicating job priority nnn is a  job sequence number hostname is the name of the host originating the job Data file names should have same format df X nnn hostname dfA001patrick dfB001patrick the X is a sequence identifier Order that files should be sent in is not defined Most network printers ignore the control files and just print data files, treating each as an independent jobP"P)PzP(PPPPP"'z(t8*x[[Control File FormatASCII printable characters, line ending with \n Example: Hastart4.astart.com Host name Proot User name (banner) J(stdin) Job title CA Job class Lroot User name (billing) fdfA458astart4.astart.com Data file N(stdin) Data file name UdfA458astart4.astart.com Unlink data file Lines starting with upper case letters are information Lines starting with lower case letters are data files93 m9    mB(<6/\\Data File FormatEach data file in the control file is identified by a line starting with a lower case letter. This letter indicates the  format of the data file and is a hint to the server on how it should be printed. The U lines in the control file were originally used to indicate that the spooler should remove the data files after printing. By default, most spoolers do this by default. $|{Control File BotchesAs you see, the format of the control files is trivial It is amazing that so many implementations get it wrong& Use non-ASCII characters (UNICODE) Use CR/LF as end of line indication Exceed maximum line lengths Give each job the same control file/data file name Give data files names like dfA371jobs.dat - based on name of file |p(p,]]Job Transfer ProtocolClient sends a line of the form: \002printer\n Server responds with \000 Client sends control file transfer request \002cfXnnnhostname length\n Server responds with \000 Client sends length bytes of control file, then \000 Server responds with \000 Client sends data file transfer request \003dfXnnnhostname length\n Server responds with \000 Client sends length bytes of data file, then \000 Server responds with \000 Repeat sending data files until all done.!  E   L  )! ,1(.)^^Protocol HeadachesSome clients decide to send data files first, then control file Some clients do not send data files in same order as listed in control file Some clients send \n\r or \r\n instead of \n Some clients put non-ASCII characters in the control file Some clients do not use correct names for data files Some clients put in non-present data files Now, nobody would put in a line like: f/etc.../password or U/etc.../password in the control file, would they? Hmm...S&Q &+__Printer Status (LPQ)RFC1179 uses the following protocol to get printer status Client sends \003printername [keys]\n short? format \004printername [keys]\n alternate? \009printername [keys]\n LPRng verbose Server responds with status, then closes the connection There is no definition of what the status format must be. Every LPD server returns a different one Keys are used to refine the status, e.g. - select a job No definition of what information is searched for...pGu G   ``Remove Job (LPRM)\RFC1179 uses the following protocol to remove printer job Client sends \005printername user[keys]\n Server responds with status, then closes the connection There is no definition of what the returned status format must be. Every LPD server returns a different one Keys are used to select a job No definition of what information is searched for...:HHaaStart Printer (LPC)Amazingly, RFC1179 only has one command to control a printer. Client sends \001printername\n Server responds with \000 and starts the printerLK1KbbLPC (Not Defined by RFC1179) In addition to the standard functions defined by RFC1179, there is need for some sort of administration control. This is not part of RFC1179, and is usually implemented by the LPC program. Thus, there is no way to remotely manage a printer using RFC1179, leading to the use of SNMP...ccBSD Print Spoolersdd Why LPD/BSD?kUses RFC1179 All other protocols are proprietary It is trivial to implement over a network, and allows any TCP/IP based protocol to provide print services Other protocols can be gatewayed to RFC1179 based printers/spoolers with very little effort Least common denominator in multiprotocol printing Available on all UNIX, Microsoft, Apple, etc... etc... platforms$l , WLeD Why LPRng?LPRng is a descendant of the LPD/BSD family of spoolers It provides administrative control over printing operations It has incredible flexibility, logging, debugging You pay for this by not having a simple plug and play system for non-trivial setups You need to RTFM quite a bit From the users viewpoint, LPRng strongly resembles the LPR/BSD print spoolers The architecture is similar, but not identical to original BSD<ZZZ$,oLP and LPSTAT EmulationLPRng simulates a large subset of the SVR4 LP and LPSTAT command functionality You can fine tune this emulation to be more vendor specificfeNetwork Based AdministrationAdministration of printers and print queues is a major problem in large system administration is management of print queues On most SVR4 and BSD print systems you must log in as root, execute multiple different commands, and perhaps even delete or edit files by hand LPRng extends RFC1179 and provides a LPC command as well as the LPQ, LPRM, and LPR support There is even a strongly authenticated version using Kerberos, PGP, or SSL available (compile time option).,  /gfLPRng Security LPRng eliminates many of the security loopholes present in the original BSD code and design Many vendors have shipped LPD distributions with various security problems Remember - LPR/BSD clients run SUID root, allowing users to play games with stack overflows, etc LPR/BSD runs filters as root, and some filters are shell scripts with such things as exec $* Exploiting this to gain root permissions is left as an exercise for the student:_P_P$_PhgLPD/BSD Details Spooler Operation}lpd is the print daemon listens on port TCP port 515 for requests from lpr clients uses printcap for configuration jobs stored in spool queue on server host as control file with user information and list of data files to be printed data files containing information to be printed LPD/BSD requires LPD server to run on same host as client LPRng allows server to be on different hostZ[-xf[-xfHC 'ii /etc/printcapDatabase used to control printer operations Based on the termcap format, #parallel attached DUMB printer pr1|dumb:\ :lp=/dev/lp:sh@:sb:mx#1000:\ :sd=/var/spool/lpd:\ :of=/usr/libexec/of:\ :if=/usr/libexec/if The first part of the printcap is the primary (reference) name and the printer aliases. Following entries are either keywords and values, flags (:sb: set sb flag on, :sh@: sets sh flag off), or numerical values (:mx#1000: or :fx#0x13:)J J !  ^8C t " jjImportant keywordsNlp = the local printing device rm=remote host, rp=remote printer used when jobs are to be forwarded to another host using RFC1179 job transfer sd=spool directory where the jobs are stored sh - suppress (no) headers (banners) when printing locally sb - short (1 line) banner instead of long one mx # - maximum job size (0 is unlimited)ZANAN~ ]*8,'klLightweight printcap files LPRng eliminates much of the overhead of the BSD printcap files #simple printer entry lp|pr:lp=pr@host Client programs only need to know printer name and host running LPD server $@'LR+ TSimplified FormatLp2:server :sd=/var/lpd/lp2 :lp=lp2.astart.com%9100 :if=/usr/libexec/hpif :of=/usr/libexec/hpof No \ at ends of lines Tags can be more than 2 characters lp now can specify remote printer and host, as well as port The form host%port opens a connection to a port on the remote device - allowing direct access to printer (for PJL status reporting) server flags entry as used only by lpd serverNb'Zb~ r(  :New Printcap GuidlinesPut connection or other information used by all LPRng programs in global printcap entry Put server only information in printcap entry AFTER the general one lp:lp=lp@lprng.astart.com # lpd only lp:lp=/dev/lp :sd=/usr/local/spool/lp ...0QP0 % ln LPR Client~Takes a list of files, or input from STDIN, and generates a control file, transfers the control file and data files to the LPD server WARNING BSD/LPR used to write control and data files to the spool directory, requiring SUID ROOT permissions LPRng uses a network connection, and takes extreme precautions when reading files; by default, LPRng s LPR runs as a user program The dreaded LPR -r (remove after printing) is present, but has been tamed6ee,ZfFormatsWhen a file is submitted for printing, LPR makes a copy of the file and sends it to the LPD server Before printing the file, additional processing on the file may be needed GIF file may need to be rasterized Different types of files may need different types of processing The processing is specified with a format indicator LPR default format is f (text format?) LPRng allows explicit format specification lpr -F x# #c)3> %Formats and FiltersFilters are used to process files before sending to the printer Filters are specified in the printcap file as xf entries where x is the format if=/usr/local/lib/filter/ifhp vf=/usr/local/lib/filter/ifhp Some printers require some form of initialization to be done at the start and end of a job The of filter is used to do this processing as well as process banners or job separators generated by the lpd server More on filters later L<n<b] 7mo Binary FilesSome files do not require any modification before being sent to the printer (binary files) The l(literal) format is used to indicate such files Too many people confused 1 and l so LPR uses -b (binary) to specify literal format lpr -b /tmp/binaryfile Just to make life miserable, some PC based LPR clients decided to use v format in the control file for binary files& Sigh& The if filter is used to process literal files, but is invoked with a -c option /usr/local/filter/ifhp -c _M3F8@P !Destination Printer Specification$The LPR -Ppr option explicitly specifies the destination printer If not specified, the PRINTER environment variable sets the default printer; if there is no PRINTER environment variable, the first one in the printcap file is used Note that in LPD/BSD that the LPR client always transfers the job to the local LPD server, which stores it in the spool queue This led to the horrible  set symbolic link and  remove after printing options which have been exploited in the past to do horrible things (but not by your users, right?).,  ;nkLightweight ClientsThe standard BSD implementation requires the printing clients to transfer jobs to a LPD server running on the local host. The local LPD server then will transfer jobs to remote hosts LPRng implements lightweight clients, which will simply transfer jobs directly to remote hosts, eliminating the need for a server running on the local host. This also reduces file space requirements on the local host. If the destination is a printer that implements RFC1179, then you never have to store the print files=p~LPRng Printer Name Conventions'lpr -Ppr@host printcap file is not searched, network connection and default values are used lpr -Ppr printcap file searched for entry lpr PRINTER environment variable used as printer name if no PRINTER environment variable, use first entry in printcap if no printcap, use default printer value ~N !N ! E j  Extensions^If the printcap lp entry is lp=pr@host, this corresponds to or :rm=host:rp=pr: use RFC1179 protocol to transfer files For total abuse, you can use lpr -Phost%port/direct This opens a TCP/IP connection to the remote port on the host and transfers the input files directly You really should use netcat if you want to do this: nc -d host -p port filesP'P'  "ILPR Client and FiltersuBy default, LPR simply copies files to the LPD server The lpr_filter option requests LPR to run the filters on the files before sending to server Allows localized processing and system depending hacks to be done to jobs before sending them to the spooler Very handy when you have vintage software whose output needs to be massaged, and uses hardwired paths to executables $v: 2:9qhFilters and Job ProcessingWhen a job is selected for printing, the LPD server examines the control file for data file format information fdfA001astart4 format f The printcap information is checked for a format f filter program The data file is piped into the filter program and the output is then sent to the printer device If the printer is connected via a simple TCP/IP connection to a port, I.e. - HP Jetdirect port 9100, then LPD/BSD requires special filters to open connection LPRng does this using lp=host%port, simplifying operation and filters tremendously@pZZZpP  D;sm$Basic Filters - Text TransformationsdA filter is given a print file on STDIN and produces output for the print device on STDOUT The most basic filter action is to translate LF to CF/LF combinations; most antique (vintage) printers require this Then we need to expand tabs Then we better look for sequences of \b (backspaces) and overstrikes, and replace them with the right control sequencestv$LPRng s lpf filter$ Extremely simple filter that will do LF to CF/LF expansion, tab expansion, and most simple printing operations such as inserting NULLS after page eject NULLS? yes, sometimes you need to do this to pause long enough for the printer to do a form feed& Sigh When used as the OF filter, will expand a short banner string (single line) into a very nice full page banner This type of thing is used less and less these days, but periodically there are requests for this antique.6jjuqPostScript Printer FiltersMany times PostScript printers are used to print text files; the IF filter should detect a non-PostScript file, and invoke a Text to PostScript translator Some printers stack paper in back to front order; the IF filter should try to do page reversal If the destination printer has PJL support, you better insert the various PJL/PCL strings to reset the printer and put it into PostScript mode. Did I mention getting page counts? And accounting? and storing this in an accounting file? And checking that the person has permission to print?vr LPRng s psfilter  This filter tries to handle most common PostScript printer problems it sends reset sequences it gets status information and produces error messages it even logs messages returned during job printing, such as the PostScript emulators error messages it gets the value of the page counter from the printer It is incredibly paranoid about file formats and tries really hard to make sure that the job being sent is PostScript Yes, it supports Tagged Binary Communications Protocol(DZZDws APSFILTERftp://sunsite.unc.edu /pub/Linux/system/printing/aps-491.tgz author Andreas Klemm andreas@knobel.gun.de co-author Thomas Bueschgens sledge@hammer.oche.de Very nice package that valiantly tries to determine the format of the input files (uses UNIX file utility) and then passes them through the appropriate filters. One of the nice features of this package is that it will even use GhostScript to produce output for a non-PostScript printer Strongly recommended for those desperate situations where run time and file space is no object but  User Proofing is 0F(F     (  LPRng APSFILTER Shameless copy of original APSFILTER, but done in Perl Closes some minor security loopholes and deals with error conditions better Available on the LPRng web site,2] xt&LPRng s ifhp Filter$ Designed to handle HP printers (PCL/PJL) Totally shameless rip-off of the very good JetAdmin filter set produced for Solaris/SunOS The wide variety of HP printers makes it difficult to have a single filter automatically handle all situations, but the ifhp filter tries hard Various flags and options allow it to handle all known HP printers that have published PPD files with PJL information (I hope!) Switches between PCL and Postscript, has a text to PostScript converter ,S HP Printer ProblemsDifferent implementations of printer firmware have different bugs Strange (to HP) combinations of duplex, reverse, and landscape can cause catestrophic failure and require power up initialization Power save mode sometimes requires physical intervention when it turns on TCP/IP connections are left open and do not terminate correctly - you cannot connect to printer and need to power up Other manufacturers have equally nasty problems Simple Parallel Port Printer]# parallel port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp lp=/dev/pr if=/usr/libexec/lpf The sh suppresses banners, mx#0 allows unlimited size files. The printer device (/dev/pr) is opened write only by default. The lpf filter will do LF to CF/LF translation, expand tabs, etc.... This is about as simple as you can make a printcap entry<d dR" 6)g %PS, PJL, or PCL Parallel Port Printer# parallel port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp lp=/dev/pr # For PostScript printer use psfilter package if=/usr/libexec/psif -Tstatus=off # For PJL, PCL printer use CTI-ifhp package if=/usr/libexec/ifhp -Tstatus=off The status=off flag suppresses the filter from getting printer status, as the parallel port is write only The psif and ifhp filters will do Text to PostScript or PCL conversion, and will detect PostScript or PCL files See the psfilter and CTI-ifhp documentation for detailsN O.#,#p" $rf  Simple Serial Port Printerd# serial port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp :rw:lp=/dev/ttya :sy=9600 -echo -crmod -raw \ -oddp -evenp pass8 cbreak ixon if=/usr/libexec/lpf The rw flag opens the serial port read/write The sy (stty) option sets the characteristics of the serial line Any error messages from the printer will be passed to the LPD server for action*      *#PS, PJL, or PCL Serial Port Printer# serial port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp :rw:lp=/dev/ttya :sy=9600 -echo -crmod -raw \ -oddp -evenp pass8 cbreak ixon # For PostScript printer use psfilter package if=/usr/libexec/psif # For PJL, PCL printer use CTI-ifhp package if=/usr/libexec/ifhp Since the serial port is Read/Write, the filters can query the printer for status and use this to control various operationsN }.,}      ~$PS, PJL, or PCL Network Port Printer# serial port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp :rw:lp=prname%9100 # For PostScript printer use psfilter package if=/usr/libexec/psif # For PJL, PCL printer use CTI-ifhp package if=/usr/libexec/ifhp The LPD server will open a connection to port 9100 on the printer, and the filters will direct their output to this port This is extremely effective for network based printersN U.,( "  yu)Part 3 Managing Print Spooler Operations**zPrinters, Queues, and Status,All jobs handled by LPD are placed in a print queue, and then sent to a printer Queue State enabled - accepting jobs disabled - not accepting jobs Printer (Destination) State running - actively printing job idle - waiting for job to print stopped - administratively stopped from printingH\9|\9|{ LPQ - StatusYThe LPQ command is used to request status about a printer or print queue status LPD/BSD has two forms of status - short and long LPRng adds verbose (which is REALLY verbose!) When a LPD server gets a status request, it replies with status for the local queue, and then if the queue is forwarded to a remote printer will forward the LPQ request. If the printer is local to the LPD server, the server will also report printer activity You can restrict the status to only selected jobs by adding keys to the request command. These usually are the user, job id, or host from which the jobs originated.ZZZ Short Status  Long Status Verbose Status Status ReportsMajor weakness of print spoolers is lack of diagnostic information and job status LPRng assumed that most users would require information allowing them to fix paper jams, etc LPRng based filters generate status reports which are kept in a file in the spool queue. This information can be viewed using the lpq command The lpd server keeps a log file of status information as well This log file is also viewed using the lpq commandbRW} ]Alternative Status ReportsSeveral CGI scripts have been developed which allow you to get LPQ status via a web browser See the LPRng web site for examples You can write some very simple Perl Scripts that can open a connection to the LPD server and get status information. See the LPRng web site for examples Z\$u$\$u$>d5Y|LPQ and SecurityOne of the major flaws in the RFC1179 protocol is that there is no way to identify the originator of a LPQ request. Thus, there is usually no restriction on the data or information returned. Since LPQ returns the host and user which submitted a job, this is a very good way to find the names of machines and hosts to probe when doing hacking attacks Firewalls should disable port 515 for this reason in both directions, as you do not want your folks hacking other systems, right?}LPRM - Job RemovalPThe LPRM program generates an RFC1179 job removal request lprm -Plp 179 (remove job with ID 179) lprm -Plp john (remove FIRST job with user name john) lprm -Plp all (remove ALL jobs - LPRng only) WARNING most LPD/BSD systems will accept any request from any system to delete a job. The wimpy  requests must originate from port 721-731 restriction can be circumvented with a PC and a simple hacking program. Sigh& $:-~LPC - LPD/BSD Printer ControlThe LPD/BSD system usually provides a LPC program that is used to control the LPD activities. Traditionally it does this by reading and writing files in the spool queue, and changing permissions on directories The details of this are too ugly for public discussion. You really don t want to know. This architecture means that you must log onto the server as ROOT to control the print queues LPC - LPRng VersionLPRng extended the RFC1179 protocol to add additional commands for LPD server administration The LPC program generates the appropriate request and sends it to the server The server will then carry out the command WARNING The observant and suitably paranoid administrator will have noted the horrible security loophole that this has opened Don t fret - we have fixed this$mLPC - Basic Commandsstart - enable queue for printing and start unspooling jobs stop - disable unspooling of jobs enable - allow jobs to be spooled disable - prevent jobs from being spooled status - show print spool queue status This is simply same as LPQ, but different format ,,  LPC - LPRng Additional Commandsabort - kill off the filters doing printing and don t restart printing Used when a job gets hung up, the filter is unable to process it, or there is something very strange Job can then be removed using LPRM up - short for enable + start down - short for disable + abort restart (BSD) or kill (LPRng) - kill off the filter and then restart it:GG',LPC - LPRng Job Control )move - move job to another print queue lpc -Plp move lp2 john move jobs in the lp queue to the lp2 queue which were submitted by user john redirect - redirect all newly spooled jobs to another print queue lpc -Plp redirect lp2 jobs submitted to the lp queue will be placed in the lp2 queue X''A'!|%(LPC - LPRng Server Control&lpc reread the lpd server will reread the printcap and configuration information files lpc -Ppr printcap display the printcap information that the server has for printer pr lpc -Ppr debug 1,database,receive set real time debugging information for printer lpc -Ppr debug off terminates debuggingz LD"X LD"X  %  /I&Print Queue Job Priority and Selection<This is explicitly undefined by RFC1179 Most print queues run on a first come, first serve basis LPRng Extensions lpr -cxray will give job priority X (A is lowest, Z is highest), and put it in the xray class lpc class xray restricts printing to only jobs in class xray lpc class off allows all classes to be printed Brr a I* ,LPC TOPQ CommandThe LPC topq (top of queue) command will put a selected job at the top of the queue lpc -Plp topq john will put the first job of user john at the head of the list $U?>IDAdding A Printer !Adding A Printer - Printer ChecksMake sure printer works You would be surprised at how many problems during installation are traced to non-working printers Check out network connection by using diagnostics or direct connection via lpr lpr -Praw@ipaddr /tmp/log General debugging level is 1, and network operation tracing is enabledDc'Gd&F>dLServer Operation TracingFor all operations except job reception and printing, trace output is sent to STDERR Example: lpd -D1,network -F 2>/tmp/log The -F flag causes the server to stay in the foreground, and allows the server to be easily terminatedD^g_f,]l!Debugging Spooling and Unspooling When a spooling or unspooling operation is carried out by the LPD server, the printcap entry is checked for debugging flags (db) and log file (lf) entries lp:sd=/usr/spool/lp:lf=log :db=1,receive,network:max_log#1000 If the log file exists, log and trace information is appended to it When the log file exceeds the max_log (Kbyte) limit, it is truncated to the min_log size This technique allows tracing of job flows through the system in a simple and easy to follow manner^?~ ? 1 &  Fixing ProblemsLMany of the times you will discover that LPRng balks at accepting a job from some other spooling software due to non-compliance with RFC1179 or what can be considered a security risk You can force LPRng to accept the job, but it will insist on  sanitizing it before using it To do this, set the fix_bad_job value in the /etc/lpd.conf file to true. # fix bad control files and data file names # fix_bad_job@ fix_bad_job (_H_HP)}VInstalling and Setting Up LPRng  Installing LPRng Get the source code from a distribution site ftp://ftp.astart.com/pub/LPRng/ Get the distribution (LPRng-version.tgz) Get the filters as well (LPRng_filters-version.tgz) You will need ANSI C compiler, GNU Make, GNU Zip (gzip) Uncompress and tar the distribution gunzip -c LPRng-*.tgz |tar xf - Configure, compile, and install the code (see documentation for details configure; make clean all install Install the default configuration and permissions files make default -Z}Z\Z ZHZ"Z8Z Z-\ H"8 7  4%LPRng's checkpc Program(  NThe checkpc program reads the printcap files and checks the system spool directories for consistency with the printcap information When invoked by root, checkpc -f will change permissions and create the required files and directories It can also be used to remove old files and truncate log files. See the checkpc man page for details6O b G "Setting Up LPRng on BSD Systems +You must first stop the current LPD ps -axu | grep lp get the LPD PID kill pid Run checkpc to fix the permissions, etc... checkpc -f Edit the rc.local or other rc file to start LPRng lpd if [ -f /etc.../printcap ] ; then echo -n ' lpd' ; /usr/local/bin/lpd; fi; Start lpd and test the system lpd$.+ 6L$+ 6L#    Setting Up LPRng on Solaris/SVR4 The default printing system on Solaris is the lp print spooler. You must first disable this, as well as the various network print services lpshut; nlsadmin -r lpd tcp; nlsadmin -r lp tcp; Check the /etc.../printcap file, then run checkpc to fix the various system files checkpc -f Next, modify the rc files to start lpd instead of lpsched Reboot the system Check the system using lpq, lpr, etc....1R u.\1 8 u-[    ) LP System EmulationDWhen the various LPRng clients are invoked using the LP system names, they will emulate the lp system behavior. This can be done by making symbolic links to the various files: ln -s lpr lp; ln -s lprm remove; ln -s lpq lpstat; This allows programs which require the LP print system to directly use the LPR print facilities:3`3`FR bLoad Sharing and Printer PoolsPrinter Pools and Load Sharing}The LPRng software can do load sharing between a set of printers by having a master spool queue and a set of servers printersuLoad Sharing Details@The jobs in the master queue are sorted by priority When a slave printer is idle, the top priority job in the master queue will be moved to the I dleslave printer queue Example Printcap Entries: master:ss=serv1,serv2 :sd=/var/spool/lpd/master serv1:sv=master :sd=/var/spool/lpd:lp=/dev/pr1:... serv2:sv=master :sd=/var/spool/lpd:lp=/dev/pr2:& Jobs can also be spooled directly to the slave printer queues as well < GGL    N:Load Sharing Details (Cont d)The LPD server tries to use slave printers in round robin order If a slave queue is disabled, no new jobs will be placed in it Slave printers must not perform spooling or load balancing will not work correctly Bounce Queues Bounce Queues and FiltersSometimes it is necessary to perform filtering actions on jobs and then send the job to another print queue This is common when dealing with network printers, or when you want to have special actions performed by a special queue Reasons for BQ Use{#do 2 pages per page up # WRONG Method pr2up:lp=realpr@host:if=ps2up #real printer realpr:lp=pr%9100:sd=/sd:if=ifhp The pr2up printer simply passes 'f' format jobs forwards all job to the real printer, and does not pass the data files through the ps2up filter LPR will look at the pr2up entry and decide to send jobs directly to realpr, so they will never get put into the queue(tt- -Correct BQ Use#do 2 pages per page up # CORRECT Method pr2up:lp=pr2up@host:if=ps2up :sd=/sd/pr2up:bq=realpr@host #real printer realpr:lp=pr%9100:sd=/sd/realpr:if=ifhp The pr2up printer now has an entry that will force the LPR program to send the job to the pr2up queue LPR now look at the pr2up entry, sees the bq entry, and will pass the data files through the ps2up filter The output of the filter will then be sent to the realpr queue(/ oAlternate BQ Printcap #clients see this pr2up:lp=pr2up@host realpr:lp=realpr@host #lpd server adds this pr2up:server:if=ps2up :sd=/sd/pr2up:bq=realpr@host realpr:server:lp=pr%9100:sd=/sd/realpr :if=ifhp (  * Update Data File FormatsAfter the data file has been modified, the original format may be incorrect Example: you want to change the original f format to l so that no further modifications are made The new format entry does this pr2up:server:if=ps2up :sd=/sd/pr2up:bq=realpr@host :new_format=flvl New format entries are pairs of characters; the first is the original format and the second is the new format after processing Note that the control file is changed irregardless if there is a filter for the particular format LFFbEditing Control Files@A bounce queue can also be used to modify a control file. This is commonly needed when either the format of the control file is unsuitable for the destination printer, or when data file formats need to be modified as a result of filtering prgif:lp=realpr@host:sd=/sd:rf=/gif2ps :edit_cf=/usr/lib/cf_editor The cf_editor filter is given the control file and can edit as well. Note that this can include actions such as removing job files. This horrible kludge is needed when dealing with vintage software that produces control files incompatible with newer network printers:D D   Routing Jobs to Spool Queues RoutingISometimes it is necessary to dynamically decide the spool queue to use based on information in either the control file or the format of data files For example: large, medium or small jobs For example: special color processing This is not the same a load sharing, which makes decisions based only on the availability of printers IRouting Filterelp:lp@host lp:server:sd=/var/spool/lp :router=/usr/lib/filter/router The LPD server will put jobs into the spool queue When unspooling them, it will pass the control file through the routing filter The output of the routing filter (on its STDOUT) will be the new printer name You can combine Bounce Queues and Routing, but the results are not predictable(F F ~W Host Specific Printcap Entries  Host Specific Printcap Entry UThe oh (only for this host) entry restricts which host will use a printcap entry Example: lp:oh=*.astart.com,130.191.163.0/24 :lp=lp1@server lp:oh=*.sdsu.edu,130.10.0.0/24 :lp=lp2@server Values are lists of GLOB expressions or IP address and mask values If the host name or address matches, then the printcap entry can be used by the hostLZcTcB   ~ IPart 4 Horrible Problems Permissions Authentication Accounting DefaultsJJ PermissionsyAdministrators may need to restrict access to various printing facilities for policy or financial reasons Most print spooler systems have some sort of mechanism for restricting access Problem with RFC1179 based systems is sparse information available to make decisions You have the endpoint of a connection You have a request type Some requests have a user name, others do not$ m mLPRng Permissions File%Based on Packet Filter concept File contains a list of ACCEPT or REJECT entries Requests are test against entries until a match is found If the result is ACCEPT then the operation is allowed If the result is REJECT then the operation is not allowed You can also put in default ACCEPT or REJECTExample1# Accept LPR requests only from 10 sn ACCEPT SERVICE=R REMOTEIP=10.0.0.0/8 REJECT SERVICE=R # Alternative to the above DEFAULT ACCEPT REJECT SERVICE=R NOT REMOTEIP=10.0.0.0/10 # Example 2O# Let only root and admin on server # have LPC control permissions ACCEPT SERVICE=C SERVER REMOTEUSER=root,admin REJECT SERVICE=C # LPRM Remove requests only from # same host and user as spooled them ACCEPT SERVICE=M SAMEHOST SAMEUSER # Allow test on tester to remove files ACCEPT SERVICE=M REMOTEUSER=test REMOTEHOST=tester.astart.comPPEAuthenticationRFC1179 does not provide any authentication methods Since it is trivial to forge network level packets, etc, this can be a major problem LPRng solution was to provide a general purpose method of adding authentication Currently, PGP and Kerberos authentication is supported,]  AuthenticatorA connection is established, a special AUTH request is sent Part of the AUTH request is the supported authentication methods The reply contains the chosen method to be used Each end of the connection starts an authenticator program which will then perform the various authentication and/or encryption to be done The authenticator programs accept information from the LPRng programs, encapsulate it, and then transfer it to the other endp@ Example: PGPTo use PGP authentication, each LPRng server will require a secret key, and each LPRng user will need the public key of the server Each user will need to provide his public key to the server as well Each RFC1179 request and/or reply is signed with the appropriate key, identifying it as originating from the correct endpoint The use of a Public Key Server to supply user and/or LPRng server keys greatly simplifies this operation If you are really paranoid, you can also encrypt all requests and data file transfers >!,$Forcing AuthenticationThe permissions file is used to specify that some form of authentication must be done ACCEPT SERVICE=R,M,C AUTH=pgp,kerberos REJECT SERVICE=R,M,C NOT AUTH 0VFVE,p  AccountingPDon t spend dollars on counting pennies Levels of accounting Numbers of jobs submitted completed correctly? Incorrectly? Numbers of pages used how do you find this out Z="="Job Level AccountingzPrintcap af entry specifies the accounting file If the af file exists, then a message is written to it at the start and successful conclusion of each job Multiple starts with no conclusion indicates job failure or somebody waited until next to last (blank) page came out and turned off printer& Students& I mean users& become very sophisticated at this type of thing. Less common today given large numbers of printers usually available for use$$ -Page Level AccountingUsually required by some sort of administrative bean-counter who also counts individual paperclips and pencils Also, may be useful when costs need to be allocated on a project or account basis Most Laser based printers usually have a page counter built into them, so that you can determine the number of pages it has printed for servicing purposes If this pagecounter is accessible via the network interface, then you can get the value before and after a job, and determine the exact number of pagesf Accessing Page CountersThere are no standard methods to do this Each model of printer appears to have a different method Even documented methods may not work on printers High throughput printers try to do job buffering, and do not report the correct page count You need to wait for TRUE END OF JOB This really slows down operation Page Counters Lie Usually value is stored in EEPROM on printer You may get the stored EEPROM value, not currentl)j\F^)j\F^Configuration and Defaults The /etc/lpd.conf file can be used to set values of configuration variables Example: # fix bad control file information fix_bad_job The file format is identical to the printcap file, but you do not need the leading colons (:) and each entry must be on a single lineVV/ E/>  YConfiguration VariablesNThere are zillions of configuration variables used by LPRng. Most of these were created in order to configure LPRng operation to be compatible with very strange spooling systems or very nasty hardware Unless you have problems, you can safely ignore them. Really. If you have problems, then you better start reading the LPRng HOWTO.>63,Part 5 Multi-platform Printing and Gateways--Print Job Preparation>When a print job is generated, the information must be in a format suitable for the destination printer. There are two general approaches to the problem: Do the conversion as late as possible in the translation process (late binding) Do the conversion as early as possible in the translation process (early binding) Late binding is favourable to the print generation program, as it does not need to know what specific printer will be used Early binding allows the print generation program to take advantage of any special options that are available on a particular printer <ZZZL LP / LPD Uses Late BindingXThe LP and LPD/BSD print spoolers were designed to use the late binding model The data file format indications were supposed to indicate the type of file and the type of conversion needed The use of PostScript and PCL largely eliminated this need Most UNIX programs cannot take advantage of printer capabilities that are well known to the user YZYLPR -Z OptionsJLPR overcomes some of these problems by allowing users to specify options that are passed to the print filters lpr -Zupperbin,duplex filename The convention is that filters will scan the -Z options for values and use these values to control printing Since these are printer specific, the options can be different for each printer :oo,o  MS Windows/MAC Use Early BindingSystem configuration information records the available printers, I.e. those which have graphic to printer format converters available to the user  Early Binding and Job Generation In order to print, the program needs to select a system configured printer Configuration information includes the format of information to be supplied to a translation program The translation program Additional parameters for the translation program The information to be printed is processed by the translation program The output of the program is then sent either directly to a hardware device via another program or put in a spool file Spool files are then transferred either to the hardware device or a network printer6LLDriversIn the MS Windows environment, Printer drivers are actually several things Graphic to print file conversion programs print file to hardware interface programs This model leads to much confusion, as when errors occur it is difficult to determine if the problem is in the graphic to file conversion part of the driver or in the file to hardware device part of the driver This is even more painful when you do network printing, as now you have to distribute drivers to all printer users6LTGLTGNetwork PrintingIf you plan to print to a non-MS based print spool, choose a print driver which is as vanilla as possible Try to generate PostScript or PCL Try to generate PostScript Level 2 or 3 and the Document Structure Standards$jojo Win95 and LPR/BSD Print SpoolersWhile Win95 does not directly support LPD print spooling, there are several shareware packages that provide this. Recommended: Windows LPR Spooler Version 4.1 Author: Susanne Heil, EDV-Vertieb ftp://ftp.astart.com/LPRng/wlprs41.exe Implements a very good LPR/BSD RFC1179 compliant print server (LPR) and status monitor (LPQ) Uses Winsock interface, and works with Microsoft, FTP, and several other TCP/IP Winsock.dll:sx(b  AlternativesThere are other print spoolers available, as well as some that operate with DOS See the LPRng FTP site ftp://ftp.astart.com/LPRng/WINDOWS Other utilities are there as well:g#"g"#>X+Why Use Wslpr?If you want to have a printer pool, you will be using LPRng or some other UNIX based server (unless you have big bucks) It runs over TCP/IP and can go through firewalls 6pWhy Not JetDirect? Be aware that using different network protocols and the JetDirect software has resulted in locking up most HP printers Most of the software testing was done in situations where there as little if any non-MS network activity, it appears 8  Windows 98Has support for RFC1179 Unfortunately, it appears to have problems when connections time out and periodically jobs just vanished There may be a new version out Real Soon Now Stick with 3rd Party Spoolers!Windows NT Server LPR/BSD GatewayWindows NT supports remote LPR printers. It will translate print jobs spooled to an NT server by creating a control file and sends the data file Unfortunately, there are some minor problems with control file format, missing fields, etc., but these are easily handled by most LPD servers NT also ACCEPTS print jobs for printing This means you can use an NT server for gatewaying to the NT printer environment Beware that there are security holes lurking in all of this q \ LPRng on NTLPRng is being ported to NT Will support the basic LPRng functionality Filters are very messy under NT cannot easily fork processes memory leaks in NT DLLs H+ 7+ 7$-eNovell Printer LPD/BSD Gateways Novell also supports a LPD/BSD gateway facility If your Novell server has TCP/IP support installed and active, you can set up a print queue on a Novell server that will forward jobs to a LPD/BSD server. You can also send jobs to the Novell server and it will print them Samba, SMB, and LPD/BSD GatewaysIf you do not want to install Wslpr on you Win95 system, or do not want to set up an NT Server, the Samba system provides an alternative Samba implements SMB over NetBUI and IPX While it is usually used for file servers, Samba also supports a very nice LPD/BSD gateway facility While not being personally familiar with the details of Samba, reports have indicated that it was trivial to set up and get working "It worked first time, out of the box." Kurt Reynolds<,/ PCNFS and PCNFSD Gateways to LPDPCNFS by Sun Microsystems implements NFS (Network File System) support for PCs. As part of this support, Sun provides source code for the PCNFSD server which is used to authenticate PCs and provide print services via the host that the server runs on. The LPRng distribution has a set of patches for the PCNFSD distribution that interface the PCNFSD server to the LPRng print faciltities. They also close a couple of minor security problems involving unchecked string lengths and stack overflows>f nApple and LPR/BSDThere are several products for Apple NFS support that use the PCNFSD facilities to provide print services. Several people have demonstrated this working very well LP (Solaris) to LPD PrintingSolaris 2.5 System Administration Guide, Vol 2 Chapter 48, Example - Adding Access to a Remote Printer - remote@host # define remote system as BSD, and force connection to be dropped when idle, 1 minute timeout between connections lpsystem -t bsd -T 0 -R 1 host # printer is the lp name for the print queue lpadmin -p printer -s host!remote \ -T unknown -I any accept printer; enable printer lpadmin -p printer -D "remote printer" lpadmin -d printer # make default printervfj p+     (   N##Monitoring Printer Status with SNMP"One of the weak points of distributed print spooling is monitoring printers for error conditions One of the benefits of network printers and the TCP/IP protocol is that you can query them for status from several different locations One of the nasty problems is that most printers do not provide good status indications The good news is that printer vendors are now implementing SNMP agents in most of their printers that allow SNMP managers to easily access printer status The bad news is that each vendor has different extensions and facilities Perl, Tcl/TK to the Rescue?$8I have seen several nice printer monitoring systems developed using Perl and Tcl/TK. Unfortunately, the most elegant ones depended on commercial SNMP managers such as OpenView and SunNetManager. Several interested parties have been working on a simple shareware monitor; watch the LPRng ftp site for detailsbCZ XWhere Do I Get Help?1. If it is a commercial system, try your vendors support group 2. comp.peripherals.printers news group has a large number of discussions about printers and spoolers 3. lprng@iona.ie mail list for LPRng related issues Send mail to lprng-request@iona.ie with subscribe in the body 4. AStArt Technologies provides commercial support for LPRng and offers network consulting and management services. (Shameless plug) Good Luck!4   -VSummaryIf you are going to run printers in a multiplatform environment, you will need to deal with the issues of gateways The LPD/BSD facilities, as defined by RFC1179, provide a common platform for use. While by no stretch of the imagination can this be regarded as an optimal solution, it works well in an environment with a large number of printers which need central mangement and control,%;  Questions and Answers/W  ` ̙33` 3` 3333f` 999MMM` f` f3` 3>?" dd@ z?" dd@  " @ `"  n?" ddV %%KKppPR    @ ` ` p>> (    Zvgֳgֳ ?@ v T Click to edit Master title style! !:  T4 vgֳgֳ ?p v RClick to edit Master text styles Second level Third level Fourth level Fifth level!     S   ` vgֳgֳ ?` v =*   ` vgֳgֳ ?  v ?*   `T!vgֳgֳ ?  v ?*j"  BG 1?|j"  BG 1?44LH  0޽h? ? ̙33  DataTemplate.pot  H@` (     Zʖgֳgֳ ?p  T Click to edit Master title style! !  T˖gֳgֳ ? `    W#Click to edit Master subtitle style$ $   `̖gֳgֳ ?`  =*   `d̖gֳgֳ ?   ?*   `̖gֳgֳ ?   ?*j"  BG 1?|j"  BG 1?44LH  0޽h? ? ̙33 0 @n(    TǖjJjJ ?s%   y* V%%KKpp  TDȖjJjJ ? 0%  {* V%%KKppp  01 ?JD  :  TȖm m  ? K!  RClick to edit Master text styles Second level Third level Fourth level Fifth level!     S  ZɖjJjJ ?s   y* V%%KKpp  ZdɖjJjJ ? 0  {* V%%KKppH  0jB? ? ̙33 00(  H  0jB ? ̙33t $P(     f$͖gֳgֳ ?      f͖gֳgֳ ? H`   H  0޽h ? ̙33t  $p( =z$Jz@>    fΖgֳgֳ ?@      fϖgֳgֳ ?p  H  0޽h ? ̙33  t(  tl t C Ж@   l t C 4qp  H t 0޽h ? ̙33  ( w l  C Tr@   l  C r   H  0޽h ? ̙33  x( $@ xl x C t@   l x C tp  H x 0޽h ? ̙33t  $$( ֳ $ $  fsgֳgֳ ?@    $  ftsgֳgֳ ?0P  H $ 0޽h ? ̙33t  $((  ( (  fsgֳgֳ ?@    (  f4tgֳgֳ ?p  H ( 0޽h ? ̙33t  $0(  0 0  fTugֳgֳ ?@    0  fugֳgֳ ?p  H 0 0޽h ? ̙33t  $4( ֳ 4 4  ftvgֳgֳ ?@    4  fvgֳgֳ ?p  H 4 0޽h ? ̙33  bZ8(  8 8  fwgֳgֳ ?    H 8 0޽h ? ̙33b   <(  < <  fTxgֳgֳ ?@    <  fxgֳgֳ ?0    <0 NA ? ?moh $ 00 H < 0޽h ? ̙33t  $@(  @ @  fygֳgֳ ?@    @  ftygֳgֳ ?p  H @ 0޽h ? ̙33t  $ D(  D D  fzgֳgֳ ?@    D  fzgֳgֳ ?p  H D 0޽h ? ̙33t  $0H(  H H  f|gֳgֳ ?@    H  ft|gֳgֳ ?p  H H 0޽h ? ̙33t  $@L(  L L  f͖gֳgֳ ?@    L  fgֳgֳ ?p  H L 0޽h ? ̙33t  $PP(  P P  fgֳgֳ ?@    P  fDgֳgֳ ?p  H P 0޽h ? ̙33t  $`T(  T T  fgֳgֳ ?@    T  fgֳgֳ ?p  H T 0޽h ? ̙33t  $pX(  X X  f$gֳgֳ ?@    X  fgֳgֳ ?p  H X 0޽h ? ̙33t  $\(  \ \  fgֳgֳ ?@    \  fgֳgֳ ?p  H \ 0޽h ? ̙33t  $`(  ` `  fdgֳgֳ ?@    `  fgֳgֳ ?p  H ` 0޽h ? ̙33t  $d(  d d  fgֳgֳ ?@    d  fgֳgֳ ?p  H d 0޽h ? ̙33t  $h(  h h  fgֳgֳ ?@    h  f$gֳgֳ ?p  H h 0޽h ? ̙33t  $l(  l l  fgֳgֳ ?@    l  fDgֳgֳ ?p  H l 0޽h ? ̙33t  $p(  p p  fgֳgֳ ?@    p  fqgֳgֳ ?p  H p 0޽h ? ̙33t  $t(  t t  fmgֳgֳ ?@    t  f4ngֳgֳ ?p  H t 0޽h ? ̙33t  $x(  x x  fngֳgֳ ?@    x  fTogֳgֳ ?p  H x 0޽h ? ̙33t  $|(  | |  fogֳgֳ ?@    |  fpgֳgֳ ?p  H | 0޽h ? ̙33  bZ(     fpgֳgֳ ?   H  0޽h ? ̙33t  $ ( zzH4    f4qgֳgֳ ?@      fqgֳgֳ ?p  H  0޽h ? ̙33t  $0(     fsgֳgֳ ?@      ftsgֳgֳ ?p  H  0޽h ? ̙33t  $@(     f4tgֳgֳ ?@      ftgֳgֳ ?p  H  0޽h ? ̙33t  $P(     fTugֳgֳ ?@      fugֳgֳ ?p  H  0޽h ? ̙33t  $`(     fvgֳgֳ ?@      f4wgֳgֳ ?p  H  0޽h ? ̙33t  $p( Og    fgֳgֳ ?@      f$gֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      fDgֳgֳ ?p  H  0޽h ? ̙33t  $( TQg    fdgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $(     fdgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $( w0w00 d    fgֳgֳ ?@      fDgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      fdgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      fDgֳgֳ ?p  H  0޽h ? ̙33t  $(     fdgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $( X    fԪgֳgֳ ?@      f4gֳgֳ ?p  H  0޽h ? ̙33t  $ (     f4gֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $0(     fgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $@( ֵg    f4gֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $P(     fgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $`(     fgֳgֳ ?@      fTgֳgֳ ?p  H  0޽h ? ̙33t  $p(     ftgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      ftgֳgֳ ?p  H  0޽h ? ̙33t  $(     f4gֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      ftgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      fTgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      f4gֳgֳ ?p  H  0޽h ? ̙33t  $(     fgֳgֳ ?@      ftgֳgֳ ?p  H  0޽h ? ̙33t  $(     flgֳgֳ ?@      f$mgֳgֳ ?p  H  0޽h ? ̙33t  $(     fmgֳgֳ ?@      fDngֳgֳ ?p  H  0޽h ? ̙33t  $(     fdogֳgֳ ?@      fogֳgֳ ?p  H  0޽h ? ̙33t  $ (     fDqgֳgֳ ?@      fqgֳgֳ ?p  H  0޽h ? ̙33t  $0(     frgֳgֳ ?@      f$sgֳgֳ ?p  H  0޽h ? ̙33t  $@ (       fsgֳgֳ ?@       fsgֳgֳ ?p  H   0޽h ? ̙33t  $P(     fdugֳgֳ ?@      fugֳgֳ ?p  H  0޽h ? ̙33  bZ`(     f$vgֳgֳ ?    H  0޽h ? ̙33b   p(     fvgֳgֳ ?@      fvgֳgֳ ?0    0 NA ? ?h $ 00 H  0޽h ? ̙33t  $(     fwgֳgֳ ?@      fxgֳgֳ ?p  H  0޽h ? ̙33b    (       fgֳgֳ ?@       fTgֳgֳ ?@     0 NA ? ?p h $ 00 H   0޽h ? ̙33b   $(  $ $  ftgֳgֳ ?@    $  fgֳgֳ ?@    $0 NA ? ?p  h $ 00 H $ 0޽h ? ̙33b   ((  ( (  fgֳgֳ ?@    (  fTgֳgֳ ? `   (0 NA  ? ?EF h  $ 00 H ( 0޽h ? ̙33b   ,(  , ,  fgֳgֳ ?@    ,  ftgֳgֳ ? `   ,0 NA  ? ?_& h  $ 00 H , 0޽h ? ̙33t  $0(  0 0  f4gֳgֳ ?@    0  fgֳgֳ ?p  H 0 0޽h ? ̙33  bZ4( t 4 4  fgֳgֳ ?p    H 4 0޽h ? ̙33t  $8( g 8 8  fTgֳgֳ ?@    8  fgֳgֳ ?p  H 8 0޽h ? ̙33t  $<(  < <  fgֳgֳ ?@    <  fgֳgֳ ?p  H < 0޽h ? ̙33t  $@(  @ @  fgֳgֳ ?@    @  fgֳgֳ ?p  H @ 0޽h ? ̙33t  $ D(  D D  fgֳgֳ ?@    D  f4gֳgֳ ?p  H D 0޽h ? ̙33t  $0H(  H H  ftgֳgֳ ?@    H  fgֳgֳ ?p  H H 0޽h ? ̙33t  $@L( 8zzz L L  fgֳgֳ ?@    L  fgֳgֳ ?p  H L 0޽h ? ̙33  P( `) l  C t@   l  C p  H  0޽h ? ̙33t  $`P( Tg P P  fgֳgֳ ?@    P  fgֳgֳ ?p  H P 0޽h ? ̙33t  $pT(  T T  ftgֳgֳ ?@    T  fgֳgֳ ?p  H T 0޽h ? ̙33t  $X( X X X  fgֳgֳ ?@    X  ftgֳgֳ ?p  H X 0޽h ? ̙33t  $\( g \ \  fgֳgֳ ?@    \  fTgֳgֳ ?p  H \ 0޽h ? ̙33t  $`(  ` `  fdʰgֳgֳ ?@    `  fʰgֳgֳ ?p  H ` 0޽h ? ̙33t  $d(  d d  f˰gֳgֳ ?@    d  fD̰gֳgֳ ?p  H d 0޽h ? ̙33  bZh(  h h  fdͰgֳgֳ ?0P    H h 0޽h ? ̙33t  $l( X l l  fͰgֳgֳ ?@    l  f$ΰgֳgֳ ?p  H l 0޽h ? ̙33t  $p(  p p  fΰgֳgֳ ?@    p  fDϰgֳgֳ ?p  H p 0޽h ? ̙33  (  l  C ϰ@   l  C аp  H  0޽h ? ̙33t  $t(  t t  fdаgֳgֳ ?@    t  fаgֳgֳ ?p  H t 0޽h ? ̙33t  $x(  x x  fҰgֳgֳ ?@    x  fӰgֳgֳ ?p  H x 0޽h ? ̙33b    |(  | |  f$԰gֳgֳ ?@    |  f԰gֳgֳ ?@    |0 NA ? ?p h $ 00 H | 0޽h ? ̙33  0( TjUk l  C ԰@   l  C Dհp  H  0޽h ? ̙33t  $@(     fgֳgֳ ?@      fDYgֳgֳ ?p  H  0޽h ? ̙33t  $P(     fZgֳgֳ ?@      f$[gֳgֳ ?p  H  0޽h ? ̙33t  $`(     f[gֳgֳ ?@      fD\gֳgֳ ?p  H  0޽h ? ̙33  p(  l  C \@   l  C ]p  H  0޽h ? ̙33  (  l  C d]@   l  C ]p  H  0޽h ? ̙33t  $(     f^gֳgֳ ?@      f^gֳgֳ ?p  H  0޽h ? ̙33  (  l  C `@   l  C d`p  H  0޽h ? ̙33  (  l  C a@   l  C ap  H  0޽h ? ̙33t  $(     fDbgֳgֳ ?@      fbgֳgֳ ?p  H  0޽h ? ̙33  0(( w   # ldcgֳgֳ ?@     # lcgֳgֳ ?p  H  0޽h ? ̙33t  $( X    fdgֳgֳ ?@      fdgֳgֳ ?p  H  0޽h ? ̙33t  $(     f$gֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33  ( r  l  C @   l  C dp  H  0޽h ? ̙33  (  l  C @   l  C p  H  0޽h ? ̙33t  $ (     fDgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $0(     fdgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $@(     fgֳgֳ ?@      fDgֳgֳ ?p  H  0޽h ? ̙33t  $P(     fgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $`(     fgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33t  $p(     fDgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33  ( r  l  C d@   l  C p  H  0޽h ? ̙33t  $(     fdZgֳgֳ ?@      f䏸gֳgֳ ?p  H  0޽h ? ̙33  ( 8t l  C d@   l  C đp  H  0޽h ? ̙33  0(( w   # l䒸gֳgֳ ?@     # lDgֳgֳ ?p  H  0޽h ? ̙33  0(( zd    # lgֳgֳ ?@     # lgֳgֳ ?p  H  0޽h ? ̙33  0(( w   # l$gֳgֳ ?@     # lgֳgֳ ?p  H  0޽h ? ̙33  0(( z#   # lDgֳgֳ ?@     # lgֳgֳ ?p  H  0޽h ? ̙33  0(( zT   # ldgֳgֳ ?@     # lėgֳgֳ ?p  H  0޽h ? ̙33  bZ(     fgֳgֳ ?    H  0޽h ? ̙33t  $( w0w00 d    f䘸gֳgֳ ?@      fDgֳgֳ ?p  H  0޽h ? ̙33t  $ (     fgֳgֳ ?@      fdgֳgֳ ?p  H  0޽h ? ̙33  0(  l  C Ě@   l  C $p  H  0޽h ? ̙33  @$(  r  S @   r  S p  H  0޽h ? ̙33  P$(  r  S D@   r  S Dp  H  0޽h ? ̙33  `( w l  C E@   l  C Fp  H  0޽h ? ̙33  p(  l  C F@   l  C $Gp  H  0޽h ? ̙33t  $(     fGgֳgֳ ?@      fGgֳgֳ ?p  H  0޽h ? ̙33t  $(     fDHgֳgֳ ?@      fHgֳgֳ ?p  H  0޽h ? ̙33t  $(     fJgֳgֳ ?@      fJgֳgֳ ?p  H  0޽h ? ̙33  ( 2 l  C dL@   l  C Lp  H  0޽h ? ̙33t  $( .2     fMgֳgֳ ?@      fDNgֳgֳ ?p  H  0޽h ? ̙33  0(( r    # ldOgֳgֳ ?@     # lOgֳgֳ ?p  H  0޽h ? ̙33  ( x l  C $P@   l  C p  H  0޽h ? ̙33t  $( ĺg    fĹgֳgֳ ?@      fŹgֳgֳ ?p  H  0޽h ? ̙33t  $ (       f4ƹgֳgֳ ?@      fƹgֳgֳ ?p  H  0޽h ? ̙33   (  l  C ȹ@   l  C tȹp  H  0޽h ? ̙33  $( 8 r  S ȹ p  r  S 4ɹ  `    H  0޽h ? ̙33  0  (   l   C ɹ@   l   C ɹp  H   0޽h ? ̙33  @ $(  $l $ C ʹ@   l $ C ˹p  H $ 0޽h ? ̙33 P 0( w x  c $t˹ p  x  c $˹  `    H  0޽h ? ̙33  ` (  l  C 4̹@   l  C ̹p  H  0޽h ? ̙33  p (  l  C ̹@   l  C T͹p  H  0޽h ? ̙33   (  l  C ι@   l  C tιp  H  0޽h ? ̙33    $( /?  r   S 4Ϲ@   r   S Ϲp  H   0޽h ? ̙33   (  l  C ^@   l  C T_p  H  0޽h ? ̙33  P ( }@m@ l  C @   l  C p  H  0޽h ? ̙33  bZ (     ft`gֳgֳ ?|    H  0޽h ? ̙33t  $ ( g    f`gֳgֳ ?@      f4agֳgֳ ?p  H  0޽h ? ̙33t  $ ( 侼g    fbgֳgֳ ?@      fcgֳgֳ ?p  H  0޽h ? ̙33t  $ (     fcgֳgֳ ?@      f4dgֳgֳ ?p  H  0޽h ? ̙33t  $ (     fTegֳgֳ ?@      fegֳgֳ ?p  H  0޽h ? ̙33t  $ (     f4ggֳgֳ ?@      fggֳgֳ ?p  H  0޽h ? ̙33  bZ (     fThgֳgֳ ?    H  0޽h ? ̙33t  $ (     fhgֳgֳ ?@      figֳgֳ ?p  H  0޽h ? ̙33  0 ((  (l ( C `@   l ( C ip  H ( 0޽h ? ̙33  @ ,(  ,l , C 4j@   l , C jp  H , 0޽h ? ̙33  <( w <l < C $2 p  l < C 2  `    H < 0޽h ? ̙33t  $P (     fTĹgֳgֳ ?@      fgֳgֳ ?p  H  0޽h ? ̙33  ` 4(  4l 4 C t@   l 4 C p  H 4 0޽h ? ̙33  p 8(  8l 8 C @   l 8 C p  H 8 0޽h ? ̙33   @$( A< @r @ S d4@   r @ S 4p  H @ 0޽h ? ̙33   D(   Dl D C 8@   l D C 6p  H D 0޽h ? ̙33t  $ (     fƖgֳgֳ ?@      fdƖgֳgֳ ?p  H  0޽h ? ̙33  H(   Hl H C 5 p  l H C 3  `    H H 0޽h ? ̙33t  $  (       fgֳgֳ ?@       fgֳgֳ ?p  H   0޽h ? ̙33   L(  Ll L C @   l L C Tp  H L 0޽h ? ̙33 0 P(  Pl P C  p  l P C 4  `    H P 0޽h ? ̙33  @ T( r  Tl T C @   l T C Ԫp  H T 0޽h ? ̙33  bZ ((  ( (  fgֳgֳ ?P    H ( 0޽h ? ̙33  ` \( _ \l \ C t@   l \ C ԧp  H \ 0޽h ? ̙33  p `(  `l ` C t@   l ` C ԰p  H ` 0޽h ? ̙33   d( (6"4 dl d C 4@   l d C D3p  H d 0޽h ? ̙33   l$(  lr l S @   r l S D p  H l 0޽h ? ̙33   p( r  pl p C "@   l p C d!p  H p 0޽h ? ̙33   t(  tl t C $@   l t C d$p  H t 0޽h ? ̙33   x(  xl x C %@   l x C D&p  H x 0޽h ? ̙33   |(  |l | C (@   l | C $(p  H | 0޽h ? ̙33   h(  hl h C D9@   l h C 4p  H h 0޽h ? ̙33   ( ljPl̻ l  C d'@   l  C D)p  H  0޽h ? ̙33   ( ̙33 l  C *@   l  C d*p  H  0޽h ? ̙33   ( c0** l  C +@   l  C p  H  0޽h ? ̙33   $( 5# r  S @   r  S p  H  0޽h ? ̙33  @ ( 4t l  C @   l  C p  H  0޽h ? ̙33  h`P X( r  X X # ltgֳgֳ ?P    H X 0޽h ? ̙33t  $ ,(  |t4 , ,  fTgֳgֳ ?@    ,  fgֳgֳ ?p  H , 0޽h ? ̙33t  $ 0(  0 0  fgֳgֳ ?@    0  f4gֳgֳ ?p  H 0 0޽h ? ̙33  ` (  l  C @   l  C 4p  H  0޽h ? ̙33b    4( X 4 4  fgֳgֳ ?@    4  fgֳgֳ ?P    40 NA ? ?h $ 00 H 4 0޽h ? ̙33  p (  l  C 4@   l  C  p  H  0޽h ? ̙33t  $ 8(  8 8  fgֳgֳ ?@    8  f gֳgֳ ?p  H 8 0޽h ? ̙33t  $ <(  < <  f gֳgֳ ?@    <  f gֳgֳ ?p  H < 0޽h ? ̙33t  $ @(  @ @  fT gֳgֳ ?@    @  f gֳgֳ ?p  H @ 0޽h ? ̙33   ( @N@ l  C 4@   l  C p  H  0޽h ? ̙33t  $ D(  D D  fğgֳgֳ ?@    D  f$gֳgֳ ?p  H D 0޽h ? ̙33   ( T`< l  C T@   l  C p  H  0޽h ? ̙33   ( , l  C d@   l  C p  H  0޽h ? ̙33t  $ H(  H H  fgֳgֳ ?@    H  fgֳgֳ ?p  H H 0޽h ? ̙33   (  l  C  @   l  C t p  H  0޽h ? ̙33t  $0 L(  L L  fgֳgֳ ?@    L  fgֳgֳ ?p  H L 0޽h ? ̙33t  $@ P(  P P  fdgֳgֳ ?@    P  fĢgֳgֳ ?p  H P 0޽h ? ̙33t  $P T(  T T  fgֳgֳ ?@    T  fDgֳgֳ ?p  H T 0޽h ? ̙33t  $` X(  X X  fĥgֳgֳ ?@    X  f$gֳgֳ ?p  H X 0޽h ? ̙33t  $ p(  p p  f/gֳgֳ ?@    p  f/gֳgֳ ?p  H p 0޽h ? ̙33t  $p \(  \ \  fgֳgֳ ?@    \  fgֳgֳ ?p  H \ 0޽h ? ̙33t  $ `(  ` `  fgֳgֳ ?@    `  fgֳgֳ ?p  H ` 0޽h ? ̙33t  $ d(  d d  fĨgֳgֳ ?@    d  f$gֳgֳ ?p  H d 0޽h ? ̙33t  $ h(  h h  fDgֳgֳ ?@    h  f.gֳgֳ ?p  H h 0޽h ? ̙33  bZ l(  l l  f$/gֳgֳ ?0    H l 0޽h ? ̙33D 0  ( ֳ    H1 ?ID      f$5m m  ? K!    H  0jB ? ̙33Hx |ս3H!$B@kEDB*!F@J" $MD(RM""_ilRQKRD!M{?gfgI6 F>vs~1̜0г[F~щk?bT_a\F E| Cm"ІhĀ. PtHA"z@/p9HW$@o\ }׃n&0A`0nC0m ),p Fv0|?`<&;$p n0 ~}`3,f9`.y`P A(% `X Rxju/?76F^"|n%O̿ø[(׌ xkyڣ]&)P2_ sQ*nxފ/Fh Sγx̂<[>ϩbU/˽ mF-JΫ oq ׯF߹v|_[] **Z wlyDucy":II\?}Lr UKr8Kr|ȱ:I|^*r)vr<VUgQx<AXւ)`X~ 6F k<*"x *o@ ~ ~^?[`;ء'۠W{G>? x}|/Q  pW d?9@=/h hl "q?>/Sܟ/8K"e7ߠ;H=@Op.I Wdp"sp5ׂ~:\o&0A`0nC0E[p}{e͛k`"` > ~Ɓ;x0Lw*r&)`*L{tp"e6 f0s9^<0*r E` x,A)#5kK x>ڦ,\W;P;'TS8˵`֜H+nfmVEK?=%<,O Wa<͍D{yF{$C\peMfN [Aq ksk }wCs 9nZWUq:N筅 d.ϟ.!ǻt=z]՜}Q9]]d' `3ɷ\'v"Cu l#xN^Ći0aPUs OƯ+t^s,}yJQRˈ]}Q}bN%y9.m)wOȲmgŻ˧XaʲkYAFL=qh[P;<>J>JZ~<Z]ULg 'ݑZԳվ.`*-En?[ Pz~"]6h[P=٢zS^-҅U-> q}˳.fQۺeyvj-!.t yuG5wj:NoqP8gԩTfu ~֜3a8=:3t>hH狣N,uӑəj2ɕW.>^'3SٿI'Y'٨c|X悪rm.x}yu y/O O\^cOҪ_Y!_I?Ϸ왤6޿[x qřS[gO{]$U>b+YJ>Mdħ4zҺGmWziM_,'6[=ٛG16]WuKgt #'t ]#w;ב{^W77 K1E2\Ir~yh3ʐ&=?ޟr~U *U +A9hx5J~voښk[s|@'E]c5xƿ채\ZUԞjV~3@Oc8 ueljkjKw&7qqؽR[ÚזJvWmP^΢ٳ]+{^﹪|#7Efp wۤ|E@z]ȅ%9Y@S/hL7QI_(S6VS3'K72a).2UXC(]r*w|iY*=h݁ouLa6«/>QTk_ӂ[U5[m֨?kyq֖=c3[tQ\+G4B㈶>PD6@xt 3t~%XzŢu-PO'J۞b*V+w Q1Xz3{'ؚ6 qzͺԋۅ0Ⳃ ~j ag]C>9`hqCC>MJ۞1*w|GOL'|="V(BљYΪ{Ίiub3梻ʷ0›Nqь.14ZN4_Q+Du~11X7x2oqߪChkjѐ2 CC>]۞Q*8]/EG^A Hy#eĸOUb`_˯@;uQڑcz\utuYd C]~{=n{nT UshJIpj7ԋ[>Ub:Я<-,j?QBK;;e(^s4iT1WlhЬ>TT1ae8R<-1%\ v uۥK"bEnB,¾X ׼_V)>^LcG6+V g%!ԍ՝lQj C]>yX3KTyEO;eoo ^vfs*Z,!ı_xDaßm~yB b4>pN˱iODryזe2Ts!ZYS|zwQ2Uzoc@i^tO^&acEYG⬬Tڂw^0e|#B]-Ӟ|= zsY|b&Q}S((;'r4j=~_O"vI+.ad{arq uMf9ߵoG6[j7axRC}ɹ^TiXlO:n#В|b١t١}i5 ࠃgbj&РOuq›?W*K Ŗ6JŬʬ}Qb[@xtkE)ֶb=tŖL.iO1.FqSlWC9nL6QM-nEPBu?uΜ ʯ /pR] b?dgb!S4dfw] ?⤊&56;|Nڟ$#fÿw?I{+"4fxr_v!صA'3|WW/v;^;A!gܝvWyEi;m.-=fGȮŶvWT>cJ^鲓{=Kb8;vt3|64L1^QdKj^>--$׽dӥ}F3|}5H`HFeI{~@e{0+u8ps3ґfz427'qz>M/<_yżhjY^E-):ݮٲ[A^mfC+rXNuبQ}zZiT91|xYyL{:/ER=n{:?^zЬ{:Zom}Vڪv}O'Ze/GځR;c${Lښ23|''mYvmB$]Z 6݃#h͹Nw/Щfr'="?ߩҺ/H;Lqxi+Tolq$=evx@I(2>6K3|_ vH766NKޔvfx<Ul5*lr^FxN V+r۴ؾ .V/Vsr7F-]J_LmvS(6JrQm#?A>(R MbH/6Fn͎Ϙ飳Wu7OիxcN[{/ȣhm>Xڞci*]%Z}@cYҝ5|>ҽ!OwEtc}t'^ZψophR-XWZ?V-S+''g8'OrQS+\E%(%oha-o'5rcP#+VlRƞ;k{mkg%};Y(.~<;}ީ1w텼sh6SSdQ?AK"*?'=9aV7?wYژ^Q  (V *o{___|ufㄚlC4-nG]WN(┲8axUGK{%z$u^/Sywa#ٮ+8U~A`21B|Bٮk]MC-GNJxJ(ut"N YlMW`NAٮ+xE={'7Ǒ]WNw(tc.sbz(vqח o8f9T)#q\oP:W7)Tf}k,>o"bvIjeɥw9W |^_b+~|rTk>/=ޒmh"*aGwXÖ/ފEn$b uG&=JS/(j WUDE):ƒر98cP1/qP̃ ]۱SD,y+oGyyֱHO\Iv>/>'b:P*DȵG&;חX_E4&Bc$vAHv>/$E1ޫ+]x5riJ"bkDgɒ +"0,ŒyO)PkȢ^^v4ykq{8% IÉlוE>Eb].1p):oוS"NȻ%+^PvpqER$N Q|KD㍱v]9MTĩ$֏xKcpbkOE+yG3(lb")tt?. EO։K;R#]'뷊,qz8ss&|2I5@s9*]|^_bO*"1wÆ E*KSb-GĸN}rl)>Տ}PʠʴOu~dO&Gpc%qJ}ؙ u}Ȅڇ]WNY85C%i]WNSqڗG'S1vK7lוTErtfv]9MSihY5 x(u唭S(ℹ5|lu8](ړ0&ORʿ3ÓJbX0/?N9 5%6K! WlKl"bwH 2aPC%6GA1tlԀ+ۡX' EX~;u~1M观+WDH_"1VV("Vs˾!(֎]gl"bщ1)(H]]ۈ|KWYcOoVF,9ro^qpU&|-DZKm_$Ʊm,9%ue޹jKn~e?;Pln*rkfY2bK4|8ƧPSri/3%Yc2]u\VMܲqjj(2]"o7WĊ,yS CklRߤϜ 8]ȧ)>v~%:$k4o}[ոyet;VѫHӲ,Y%)Xw%oYI씖xs~VhmNܧnqڨq)K?w|-[;^_70ġk){ȧO'/Gλ,)YrE{Sbd!{ z|eVSC+/Ҭq]ogC@] |o(ȧ4~ٕp[r^C$?L Î&'ILSʒ["P nxO<)҇Vbcyi%g8zE>=I>--4g {<=Yi {"ی 4i! Jnjdza|o*iWO {|b=аs {<ɷgfJw8MQ=y(S(r~ܰm=}SnPӥɧv rnQ.iEq㸧um[]ըg&;Ԉ@3=О{Z1~6{°s1g8h=)Ds77tM;CmI|!{6|>ְPg֝8{,֛JAQ 1z4)r~Fd8;3DO{|:!"[fC4X@^fI|3`8yawBC):*szbC%](|`BCyv_iՋ ߢfawP>)NRӼߐOtYȉ-C4*x+\7wꝧ>ZMGVb"iC[ZδdG <۹pRE>=aOW+]D>f!V߮I7l^[ `MMסVJy5Ѽzy>)NVDOzj.1PG ߢ9y{r {%uY`)]GVR"RT[ {3t"S!gh^G|dq8aiMLvS5׶6CŁZZjS,q3E>=A>mwC̰$+za^Z)u!-U@EDV {ywR74렇VzkVB)@NqIO|<݃%; ےp-/^ҔAo zj6]W=eIN5+"774q3]E>]"b=?+PWϊZ E_5B)oRiHA3Ѵ*Z3gfv"Y&ôUp͊|j _)ѴwW—Eq +7b4a@G5̝%Gj)UL:kghj馽k3=E>Mʊ\91:bQYu엖O|)[2QS9OuX))vu@ ʂ՜b=6L{g8i".-mb Y|i!j FjuqzO]mq @-iaAG ނZh+M_g8|zC>s VԻ1/Ĝc-w+3ĸ}\YC?1Pk{;kO{}Zz{=%*',cRc-~j:YST:qO>Br80@k tM1UO[tiN@,MF>\q_m-s4}TO?_!quzdptp>x{kuW[t4mi4Žl)˸/y$ 林s8e܋YoBl͟+<$ǁOq_ӏ\{wm9ɧktFoT7yiodYS{+F9RjQ]{)TFN(ÜxY2aa<24~LÃrO鱓ƧMeoyE2x}(-Ƨ RiQVvnеpx0ǚ_#M_OR?O3|m|(Жq?=#}y3O`~'O~tR8rJOOi˙N>]sIO1VaL Q[,1'Aߌ\o)|L5W:)>}]Fi˙N> Hӂi t|]_@[cY6<ߓeRs"{ߗ ײU?#"O}-g:t}@v0O5:YԮ% "_}tFTϟ'~۾`]FaI4]h20vLqyp0 άgL9ɧҴѴ&-Xf.{5tͺ9V/Ür<3`E9)`? "}-g:tk@nA4>yB.>hS-~-ó{o}ҪœrAD;Y;XOƜ~1yMߖ3|4a4Z,c^,!`T}!w]F1+¼ak`a?.uB"|_0;cEmi:hZ0簌xxxMDM1|6!ƥ;˜f׹1|\s~˸vp_fܗL'~ۿM)׿ea'4QbN?OP03xʟ7(#|0OMO1L'nHӺFӃ/-8>[f&~&.7O B|MXsG{x̕p>90;?F^Ob[tGi:hz,$P˸dž-w8w񞐪S*^8 q,Kז˘x0@`}m9ɧDMdh. k+8+1?6qሦ-oqn˵z5M16 <3i˙N>$ MM$mc9j/ܷR/q>#od:s֝BGp8*3W.׾/<`˙N>xbyg_ qرO!; Pܬ'fy{);҃1Ob>-o[ti[tam[--⼼xpRJx9sEHMTw1OF80H`%C1W[_OwNiG늡UtS>JG?}?Vnƞe {M\婙4GmCqt}TX{&_C .7x/=ߟ{V|?etZ.\ָWcߴsRT?2X9(T.CDe3( 6ҽ:Tj 60TƝܧTWswQF^ֶ\zV+<=UToYrGg3|y.w*u`칍:c_ڡoLy@4~)rrÊsjmx/^+?x,x/1Y}9}T׶#aTR(-4hZ0,>q |9#bbێ!yA~$ |bxs)̕O\%?F_:'9K=ihiZas=8g},L}2r~]FEn®Isg#a=`a0uGkL'~s[*Ω\f81z_=K>8_̏o\%Nj]%ǃ1#7e0 9ӽiZtr#-o>_} v^ :0f35rh_8{_Hegh;:rOsiwys3D=71{++ms\}4U1TX=MM9ɧt^ѴZ%ZC)+ݶj=Z&^G.uY~}o.I\L'~?k'fji˴Xi[wƞ.X5|kaï7:h\F_W/SsR1똶ӯҴ˗iǴF^VwhxVi u5#>xPkrC^<9#?]Ƶx5-g:4]G4ֈOݦ5ޑfO{9]k>v[{7wֈ`g-}#n|EkS[t4-g4VI bܖ>kPxc}]^ n5n2m?]Ƶx:+?^4i tgEO%ӺE۪Gb1|y0ӉM$~9xڳ`O{(5xj˙N>=9Eө+c,r鎩1n믟v)1 8Nn[rx05x ;gӖ3|]@V4XdcOLq<=4>ts1.$}סv؏.^#>x Frbqn+-gY܅D̆mcG;&\vX=ǵp ~1i14[Mִ1O%:ALcυ%4Ә{Oo-ܬGYc?]>8A#x&ӏOqYa)2Oe!~[77:qxl+jK_F<]vS0|֑x\ihor˕(FE3r~QQ{# eZ`F?trv-g:ǀ4-S4=81rOr/I-g:h@MGdZuST#F>Hj}<ƽihZjdTAcD^;G5Mn-mz4~,LzJxtmI9r>eM\?ďl*+?d˙N>9 MM=2|ܯvwȜkǨ3ИC~UF5I;yQg\O[x6w8S)^4鉀4#}ݣ{SoP6r0||18j[ηye<|3cUB??Ƴ~+ArO HӃDFi}ߓT<\-~3.#{کx4C_8r|0> q-g:d@/xA^/5K4xe<_ W x~Xa'>>`Ϗ?-U-g:4sM<%Ӗ<܆3-C0fqOW =yZ9s`Z,ӂ4UȵF|\MçK[ǔ#%~1㚪 wm'~tIw9)㽸+h@v(.)2OOdH<-<. K߼S<l#uHx0#rO?FGEӾEuZ-5+i X+Z#V=5ȧqO.A<|DƖ3|Ҵc ;my&oֈf951|9&Gkyn|zr> ψ5I-g:_H<"ֿOkeh#Vyk4 馡i{F|ܿBa->.Jk6K^ZK%~Yn ]Ei-rx">;.IWkMp~xRiZuh37mgӒGFƸ G8~\ǭcGWY|rx0) S0˸Ol?m9ɧi'˱Ws4AkO=.}3]"qG<#L|?F<S[ti4]h:+ȽzMNM*13ԈNs/ } sexymI]FV?L'M=#-d1j1Nn"}) zK2r)O{{'qՖ3|Z& MM|ո?iy(ǫa^5Q3G5wIb=# MˌM[Gr-(yIؔ,vLnTcsǭܖF5~T2T`˜Gʊ?m9ɧettcu-})r͠%rStJӼ$/q`/)t94)rNS[ti:mhq[Z}w@ۊ+_BzSq[ם;)tmQk%ɖ3|Z. MMM}LG#sMYm2qUJm=Ymv(q~/;٧=VZב(wB^iɢ)r,ΖV(y;-~GߓܴcgFu/9xo| `x0$kg$m9ɧf{ayFGF?cbwyW0ލƻ- 61mtw[rO^McOo}H9&xwXo9C@c,jCL'VH/SJn=W"wO$)A|2Fqlt"'"Go2#T:x?8Gr>k`.ooUQi˙N>{{mb,J&6>wz# "῞wqܬXs\Ƶp OiA1i!1rO/ HE.8NYL+$h;O? q1Qre͎xqS[tiՀ4U̽QSW+r(؏`_NOwL-rV`׍2k2It-g:Z@nZ.~Wr:-o{YެX#({Jh cN{!^"/2sq<};0(.@E׏d/;[ti4k72^F'ȯ8Տ? >O_]Ƶq 1c܋}Om9ɧiLCr-A D5ƽ[HZaHE5{&ÇK~u/Gx F<c2#aoӖ3|Z# MfJ|{(9sà}y zK lx|Og6ז3|Z+ MMuN2?>x{g=yT|ܟ.[cl[ `Y4ן;v)ge?-g:4+}#ï-Ж}ж^~x/48G>{-r<v+睰siC{ ϝWxeo~M D|lB*o;pTx"Qti4ݺ<2v~#rY_M\FLX my-tc_Gp<sY@o^`˙N>4 M', ^#_>[n c"oy//R})>8 _؟b?[ti݀4.db9R^.o"S0qQ?Z;T(g:i/,}n*C7Gtin'|a/'\dU'ΔX `O?,SpԖ3|Z? MoM+Dxg`^nO0]vFx)r>hŘ048U銺2&o. e~j˙N>< M[mMq_F8Fv}1ˆ1qgOqJO\"S0˸͖3| M+lMK& Ϛ}u1]ß]ƥ5 ~x'rn$s?Ƙ9brO;>2k r"޶#?`*cU?F~(oe\S]^ty={ydϏOӦ7|0 Mg}bT^uF8G=y箺$\Q'Sz^ʓ`<:or ]\./tm9ɧH^"2:O9<ceո]OX{>W-yE]d,q#r?t;F-g:/izbhzlbDk<*u? {.H$J=;f/}[.OY#O^)'FKȼԖ3|8 MswgF43FFxQˏď175k[g CC]ǚy]xTC[Km9ɧW=˲g۟5\*#>_|0Y@7g|x7-GceM{L'6 Hv{EӬ7#< m08lK#Nb>c?#cs"|d[9.cc{1>?=h{r+Ҵ>4wY><)>1AJI{$.}.x+I.w!ǁq맲>6}<{yӦi+tZٞic#~ǾF.}1s.?jGYSpϏd2(p<qk c%楶ӿo;m]g_ k~胑;J]1$rFxމ0_u^9g5Hu1"ն6/BfF*`8 ͱ|ۚ0~x> wGuؽf U**3r*#h!'̱| VktLfpW"ڧi,OOp|]1z蘫ͱ|g켋,ΤNx wՍ*\8" 7"v^t^}J+̽or`o\^6WCzJmn}{4B}Q*_ʛs{"{U xI(w U'^5a<;2/ RȨ]rOeLe|`4?C5|JE" '{VjT^T[x|g >0V2/e0}7^7f/x;.m'FuVA/N//= ;{]ⷢrkx<<\Qq ue`0{E2T2\oD*A:Q{ Cr'k92 +^Tb.> /{˓"^^rx; û.W85ԭtL9Dy-H?g5`5vx9xvr#le۳{=1wؘ=JT.;\w].('QaekRyϢ2BZ2]kx;Bwح52/e]Gr:"ͻt'ekYZx67,^x-wzyڀT􋨌wcOhMx*+m?C=x<Qj]f_j /w۪5 U]2P9Dy-[rܷiy5ZwT_Ukκ\Z`x;û~ /x;V_ﺌn1ՖzDy-He\h`1Uij3HvP^˃ּc *Fߊw]PnmRyhXT:=ƣ(;  cbx{ex ^P`[\1V.cGel~xyeReX a c_ix1ԃXdžP 1{ϖ{yi2} ;6 ˕G/Xp:}ųg:6^5ErlxVww]R<ڂ;|c@*o?]Tw1/wU\A,(e ;_icc/l̇ ic_MЫojcux-wzy]@*8CTƎer ».50-}x*`[<1uqmm@?YZrQ^Į2vlro Hg&T:e Cexx9e e0 3!?]&^x{#N\{e /N//H庿9OcCT[o>;cj㧩ݒq,]F18Tژ3w˝^^9D)WgEV֬(;SP.=)jB}į]n]T F[-<ꬄX (=9kxe?02m_gܓ?qOQ^Itr1r?W^zm"\(3Mr̻WڭTh>43旔œo;](__t/J.\P%2"-C'2+XsiMAT`͢hVƀ. ĐMEmdzrHTί**#jY\ٶxJ?#Ż.[8/e06]:U|)rGǧ~nr6ʟPj}_T2.x6^BW[ܡllxrﴆ7Oţ/Ϯ'uz /~ /Ϭ/^xyr9R<\adzS2ȨijOTOl"}[rTnz{2zĎkպ҉.92q`@ ~ Ce2^[) q*C WJ_;|O@*+6jYnu\6V//D22T㚊]*^|2w]FxEuob|4Տ&k93 ,CǣL,qx5]z8L.0ԀP*x"u*(߈S<$^|px9gxyBN ox0 e0r}1[w0^' ߊw]F8?Q|dx-wzyW@*wihǞq.ψ扗ǭ*Ze2j@Mx׏e /CexO.Ced@e}x\Lܛ(}\&u`x*>,@/W"^n[r\.On"No$Wn˽R_L3,sx=ePǃe0ԀPɏe /xe /Ce=x-wzyT^v Ev,IQ{w]p'P^û.x9}/c WPb/N// HW/m?7q^*&».C=Pj3> /Cex*snLr\2TڸB7R_Eeda"Kac~ ox0 /4N#4_ F)kd@];j]лWۡmPS:d3;S$^W-|Lm2OlFs;b܃BsaպkLOp[LѪ-oXi;V-z4 mva{!@EEAEm%}o2=;ʝ\.2TRE kߓ>>Gl݊[1Ӻ=D1F^cO`j?džR#rRjocRkyzR3/EtJ\ssD/~';~uMzFVDzR\R{΋7?0ZlDx0QB܋327W>P/4pZ mXEЀZaDwPk0} UnP{(- +9V]Is| kdpOѻpS4#{ 0z-2ZRMfZ~k51f=SU ;xF|קRֽߏhQ|ĨT=v(ݚ Yv(#}E=߄eG7J^4Ã2Ԃ ŋi*0[v; iߞx㵎2qOSzI)>__RQr{:jh,U)wPCJMJa5e1Lz)KHa5wAXMZˣW{hwanz2Fѫ5δr]pUOWSze|$-Q Yp+Pk:ܱKqu)nRJ{R腇%gNRj j+2ۖ,g4PN&%*J̚/7M(BF^O߁S)Hu{ʧU NxS8Yq/7q]]5J?:rԏ3w'ݝ\["4i4$D?_Q%Ǟټ¿hbw{4-BQFJu^^ ǼJc=3u=OqY\q+_ WRx`mز6|ʴaUO./)F. ,2Ǟrj2/_ 2,jѡ`v*Kv۲=YFܶt(ݶ'mvR/nT}=Ԋ?LkURN?RWW[U u]g7%PHJrLC o6‡턏>! |JI9_$%k7o '"zR?~"'L8Ap!AByq{9Pp4tB3 !'P@~uܹ? آM~MgF_q|i!د^FBNIQpTu/y52R,[@hcp8F>1#O K+fz|)U&F[M3X[gK:VqVתl"+{.9\3z]7%ĵT2; n0hkpv SЎ\nnZZK['J/fHi;ܚϞpi&Fn7&"G8Pp7oLwTUG k ~ !L( BgB娜ߩ2JxD$iQOp7t *(Fp MFMU#AuXgUYuQ|uղ7`DYIMODMЃp7.BWB:3,;qф'nwPZ\ol_e˖x_]*AAWѷ \dPPUA-x'.j>!g ل ; _$5a?{>󠺘 zcFc?wߙv>mo&2«M GEIANv#AGuV'_%R k]8V(\FeJ4.p 6bB-B E c(9@$ ㌋O&bBmBt\] }IJ]JJ]>IW@P@W@A' K ~>ToٖܲUSJ,wW_1mFU -W VbBP$EQuBkԥut{~} ss8O5 } HI]Y5 :.'PPA;ЏЂ_[™g'38l *Kq([hΥ9c%QWyjؗӈ}9j9–wPj0Q/ K=1a oSD-^y1 sV%%Nm"ZFx'zZ|Bi;h"w*&9 ELLY{jTG—MEg  c̦п͡cбs9t9x0x2HY搪oOMǽF},KJsWݦ~OxO{KӘ5+fue.s/*Ow50)~dʵ?MEc0}:zlgVަ,VPЧ}kzCaꭎP/tfGȭG7;BQݎRԓ(G<Po#KНpA7zp]%m%o y[ >Bo>>xqddxa<`Їp?7E%80z12%߁{ r$/Jm5o+-@R+d{jZ+[[[D rRJZVѓCЋp7~>/c^ W}~zg=9LnfJK'5vkv5J)iq L "'QCo"9),oQ6$ xП0a>xԃA Jjȡ d\M/xؠ 1r5WFx+VHV֯V{jZRdzѹѹT ' #GlT,A9 tS 'L6x`g &dGAANXL\  ^r0ET l?a:|Te[FnV/ '^zxj׀ ^"L'LV0^LwW3tn22 g $2 dǑI" ۃouٌ5t `  K!16Hs}k[Ji,O͓k O %N)eq|.}j2V2,' үr|MGYıU O_2=d?yJ8Jfzs˶ k!HhRJR¢jh60n$55=x?~ZqK7K$^6jޠ5\''AnZ n `o 9>\uy A賤ev)umX_-Jfɥ,ioawH`55C{|_h޽*DZWu>gg367hm|ZA%ٖ*ڥƚorjw&+Ėf:?8lV-&y͠n }`Az  AgrpV>ŠfB>CܐG?9u^_(l[ <5OέUBJy7Ց@"#9EdM!{'Y~wa|׵v΄TAGt0HߑoW+\bBn*_XM;zj-yq|{; wX eAO,:<>ȏF݌n7/]qzϕ$W_֬EVo:O $jWK[+okZXGWr~kl{ :ۓw+z<݄wtspAW; *۾em%ۖr[e-vw{jSԒM)iqܺ8$[> OCxA_ c AƆ8ҕ <@cp?]&I(Jm1ZޖRrk@^85+)%.EzV7x0ay:<`CJ% $ QU=/ d(Hx`G:<`cc#}`8 a;j00 A%mKBO:OSj8S}kA=ؑWuW+aT?`8 0}0`HM]la:mZɤVXV>Sw\StZ4a a8>eEI`` #_x8>H\ٵ@uOug00i 2Ű >%k]:Z4.5-P[Z.d,}$s8^{tZ@W30dL2x`"c;G޹u.ضB{ZƶDr5s^ mba SC0l }4‹ = U. %mZhfR+Wū3 0dSŶDTcy)(]DRr -|a5uk-}q<@kl\<| 8rB:< -|<6xO\Q]QX[D^jo 'F9k6`E`A-ńڄK \ꃺIЃ2GEc:.KE~{Z# ]ZN-QQηJզϸ؃Z52 ](jq"\lPBK2ƿ=fa!؞s?:_ƬomO͒k-^ʢJVoRQӠc3NO $0iP\M .1T/>5gE`WuR+ֈ;/"ɭkxIJM>R_3IΤ >)3lZPW%u\Dң}bÃO NW:^Zp[xMkzjXSH~G &]DVQ 1i׬k9LNFTqf'tt^y~=Ω~oUF;A08{k[MچlMZ-V^޲5lے`{(aߕiRdJ!Z6K>[۲nNt**(ېVx_h,8lKdG߱=eGdؑşarꎘNXz;v,ՎVf\O[{=VdKMH'Ўc}[aؖlGw؁+DcْܚutJLKab+8[3[-̴P[O+&d;aF#|LGN_DgmXGʻѮnHKcR *ξy02ThHduٕV #ii)Sfw^Mş-vqv#úNkY:rUeXuq.FJiF&P2 v!\jH-SZ*NXB'2)juQk\ %Cx 4hԆ0 vdhMkb$'ٲ+2Jْn]h L* +VUdžxn( тf!90 #ȮF"ݕrU2[Ykxҡ,p$hFUgo s2,⽜R")8,+s(]pfD/cHu_40p?lgJe0W22V*}07 jS ՆFѰ7ޑ! WKt S~>INI)SZ3Va}עQd$ ϣ2MeFjM{z#cHY h.s"ONLDN'_U14&IΪUa=qB4GRV2(iUˍ:VXU~d p %F#ibr÷?=:cΌtIL:ƙњŰ*xTFR+S׬%oIFh+k#c*HRdJZq1%=9 :*Jd*]s{c a4EF^x _pVZBg֑d\>r}a?.a9|XZH)(Z1/:bQ`ST&m${IXUBt2!q1Fh}a13`\_'ca edaG%c%*uUou8c${1|fV:B8douf;@jcIG3K]Kf䕉ݑ`'Tb4rLY3{Qo` r /<2fj1N-ƬcgFd wԧLJNdtzWCY:"**>;c3al@)Ml25;`8 %F-AF'$z6\ֆ+v'xjd&=cj쩱xEČ fJHrjKx@&:LRd 8֘c}>>:a: r$)L2WEq8!%G#1bφYGZj,GXY8ĀIZ 2qIG03:I%?c`<IJ$֬Ug_Y>,CׄɒZ 3uNGKw,Ld Rv"0N'H T/sӍNϓҘM)8Xr4?ښ%5#f?Uv,Ǒa Z )L d*LIԌMa)DN{L V\Xeq)XA$c+lmur9itrSw%M2L} ϖx %IëNpj3jC$r2BN%a՝N(SS:u:فYix 3ka_c( ߰|lmGutBr\z/K?yI SϖŬv:a0˒Y[c "rj3jC+Br.!߱XjvTKtg+LOz/DNtBֈ1lw=̺aR=nUѕEUg_=qCA)#YՖyP#(y wxyReH:DTq|ɟaQ0]0i $|1)EUgo=YV&O'vUGº:{-i^F{&R3 ݉LYJy+ U,{<1\ʖ|?d3StY*S}NU(~ZUd$Äu,-[@"x^%ڛx<_}@ K#-=" +e0kV4ܮ,XU} Y,$ =3|}:-ju771QG:R{WXGf']HKcO[ZCt0R3ɑl@AX9!p(#GjMQ,1Z0ԟnt򰔇[xU8{PK9+ƀAU\z(+{WEcFegH"U2k1hiy`BWo*uUg>A{S/'5KS0/+59:pW+uW:&5y[$4: u+‰]`gxGT'Tao'x2Uʡ_ 549L5"qT8hd\df48NLԻ1hIF$4iY ,^YV"gĬ\3ЄW1,O\Q_Y$0:e 8_k.в$\|E:LhM t4,yeirU8{l#L#g )KLFC(qmkWAgk6]rwKttI) ϳ4Z3VYE̅7rVY?R:e:7.r)+&2r)Hs2)q2c ;V`8xM5koRp 5FPW5`ـ}w}L\kd\a2deKzwMVZ42+컫1UJAWFXײ&J"k:O\)a\oin`n$4Gh%?[Y: ӅN1)rVak`jr\I4Fh$WO\U/RW]pGQSW 51ffo1/]݉F*Iaf/8{V̴r ܄ u]-p+p'ט%7yw3`L#G#HwkygK>kn. U*$.^ [p 8w Ju_;NrWi"2w#]/kXGL7v`»40>+^ZPXVt/+'jG՚ǘ1/4z *J~@7NQ)U*$ .>{c<{ϯ1BvgH׹<J qEр=Z#ib!>&5cf1qw$uTL3{QoOcȓ x+§ֳLX粲'ȓ< y^j$ȋZɑ-yʗ|f1hfNiTY"8{isYGn4ǔ%jf/xoK ; %R e隰CpG# RdJ5Bt9şўgottä_UdE$xrj /(Yʇh|{-j(q_08#s=NʓSP3}gdn؝뤫JSDڰJRK"GJ=^7tiGz:0dNW.t4V&Șx 2,$]Cq-E`ڝ82&D&x[q͢?GdWɌu뎘Nt&M5kachIXqkU3R2s0&d8c0ixHJLqR4LJ+Iev$ K91IJV 8~3&`laT$jvLK 8OId2 ¾}/O L1B cDoj&:b;a' R(o`XqI‰2@8^jekթe*W'p JNs8c"S"ڴ4ʗ,yHy':iXӬ4EVyxeqIC9S1bJ^ji@ĕ'p 9VWm3<)0%=K~䟛IJx*Kf(XU7ig4r9cJʜ1Nc1v;΅0HJLw!7Kt^sDI'_UU 8^9LYRpjVd:yD\cJܿBEP˓S g^~κQ Ӂi!)$2I JWuqۥ8 " c+bSED\}{ \ +rquWhJȔHk6 r9K. H+DxˬToHk{QrgKv{v':T&EkdqCJ%ԥ٬CXnv6νW򯶽O˓Sʗ|HuNLS4f*¬ Ĭgy NKC0<g LmK~|ut*cei췇0 yc߯g(ϴ࿻ŕW>FWW)HOJyC}nNt*ISU3ڵS$VՉ'xWpg9Ͽ2ŀT$%5B(Ϛ@7L:RD&Mɪ4x?Uz< OI%ԧChO8Yxu+JkL_Եȋ/sܳ)NI)y*bXql̶2$3"uwiF6̂0悸Rw>YP-dIERbZCI vgK>sY7.Q y"+0Ξqd 38#X,M\O\cm_,L)0PjŸ~ssXGL7v`V5{QoxKGC@!t>>5VdHw@O.|A ݍn݁_UUg}1Ggi/!1|sajvN ]e֐O]Oΐu# ktU =}͂dVݤ+B'1f,:\G3 Ni!P Q@%?Cw¤ K92Iua{^q,If1|]>ΎLz>Wv\y0,(.EZdB](`1;9;a' L3QUFk11Hޱx\_7鑦Y|'™p\ wc%tF谈wG/;+89>k3;ݬ#:PS'HR}~53ަMu_1kEl|ߔn8 o o o ;0|F#þ[=Oc5z)_%3^Ӊ4O8ϫxRobb~/Wc}o,w{̇Rd P>K@GL'2VRWX"$+ ,s;Cy"=CuLbFz ކwxރ>`>j Qc Ȗ5,LpY Ѭ}(HP3~Y++VPU*>{K;]Ѭ5zM%]x<}$5F![CԮک#v'f\W9+_MoU4&,Z O ER±F=4hҊP/ʱN4Q VzVqxUqUc(Y"њ'>#iKVgi"YsLZ R (UJ Sh*ző tt$&T"cU,* Z%fU:τRn#, ҒICFKf)2,l?{FNriYz]0X]}։1mUg;EPՆ|:[%TGL&DK.,|g3ig`ʥeiҺڰ0·5װZϼ3aิ | /aF&Մ|ώ߱؝)XU^Y}Q%,_[j1[GSZ&z'>?Q컲nԳ.I)`4i]yXu Z;iV<*%<,˅j3F ] =di,WR43-8ַTUfwt!iLf°=HH+gH~njH;tNn4Jk"Y&R UfwDNP%2)EUoBԆ%Zô*m ] =d×RQJ*1L[/RJU^k]qwLV]x{M c_| yKig{X.4K-/#SdJ!Y,Df:V։NpiY5sak86x$Kj:#tuvr'tA7_jȦt1LӗRILtMh)5+a0N,UNuUouK@F{6X]]4j^#/kg:Hg@W/;NӉN'ttX3VYlڷ5ϧS 7-E~O?_")mZ;Ut#@X%]aTeqG|Kemgp-8-|G#3g3T*32D#E;+V3{a]O!VQ">B$Ȕ,EbzukH*t%6,eJSd9|fCF0-RBAPbפujػ$DKvߑ`҆,D&AU.[GU~#2Tk L&r/|+`%xUiժ2L=R-5a\?[Y.gmCӉ|yHU},8{k%[XA~#Io«VWU,SmC..d"f/cV&QJ֑lTbK%gUQ3,&XY V@_XIrXےICFcEDY}XGLYRw5$zS8Pz^duЗkꐋK3gKn.u:l NEUg_^3,'5a_kǪVFwXO6plz!B#56\AfJuFuDutp}Tn*sUgO1Џ5jς/~XUBX @fCcL3+L 35:lvTdꌮUI4lL6"JjQ26&`0l6g2>!B#mFْA:F!6dXK1 fcȚBml `6%1l1sgss28^RwJmffUMPy7I-үxGin/y?[ ^,^])99DV1-'K+P]) $YMzZpF˘wʽHoր5ZB;]+=d\(>kQg>e0yJP%du⽟kI?Vd#Bw#]X'8 d6Fib(1-Zut!ӹm`UӕEUg_ 82I?R>+MeԻbGև juĄ͜K3 H֑~lTjp@ Ğ5WEq`̮0lL6 HOfU6a MȦZ-%?_؝YI)7eL0x﫚Adc׶!͂J?LM`S،l [jM$"g39edL7T6dy:lsTe6jq 1٨JP(13V&) 6w؂ڊ cDU؝(4UQ8{j! &ZpFN- 26!fruڒJʓB@1-(_׀w$)yBNR*`uIҏ+CAuaoϿ+!nV6MH$W,8ut*JdRtul%1n?=#WZd]@փa2ЁV{϶1Wfu l vHo'Z Ve}fѦ&RسR^7􁾰Y6ER@kQeK>S}X7T'ɒ%]x[ZzC/Z{'On-8Y ~:3sgsmXr%?QdS S *od&-Z_+F(HoVW嫂TjF`ĴFhñF V$\dRUjSxZ~dT6|R jU PSPT?*?W%n,cJU4&징^3~#2trī@Ԫd0Vt2]LwLo%DNLh3L.]xvK+od9RLg XI<*:g)e5fud6:]֬VDG\h:bw$ yj+AU8/fz1c%H3ezި%kZ{:IW@wՃ{do# :%OI:YUW}1҇^ZpVZUƼWҾd X^^Qs0;vt ڔ-}˺;aGYOjEV $O6 cL zXG;ѮS $2՛ ]2nE](3 MPd3؜lmedH[va ʕ|onNtdvN]qXEq֦C؄ &$?YL l D[Yڽ)8[M+[w¤H'0U*3^YYY H7/dF\V?jOSYq C4Bd!LZȧi'dsn'6$UЏ43U#} y_HK5w뫃4@=4@#4l@NBj3iO1e̚I+u vRWlYϬs*vVbxހU %R,W^cy]x>c )|X HJLk(7w}9{[θb:RL8Ҥ+ŪT?>+#&e!VQt9{?zc'}Gc'v1&Hq;|IDNwDN>wDN:2<YVAː_o~c/+RZ5%L~`-L~`/1e1N,`ǯXOt-9jz_+>~%п z}$?~"hD2yX_I`ݎ;F/-'++X+ga9YY^\^ Ɋ z"a9YQiΫ+n'hiYQޫ ފIǨ:Z"v1&KUDN>$ǘ(Uq;$z:^򈸝|qGc<"n'=שʄsxK'X{lhgI^XKחs&R{K*cy8_%cy [f~%yePY^Uǫ>*ɏdIz|;'YxWTbgo#1KڑI7 v$~1H;q;styaױ{q?y qK c DZ;I ċy0it!8f(Y/'c?7c456 BV03"͑1{u8_CȈ ð..,@%ǫ#fP9.U0볒u'oOԽ*o'?*͖㎑wkpt=+qܮ\y?]~1vùh7cܴntn.o'?u{ROI#aOit??{J uq?̋8Oy~1ZscL{R 8~/?Hc9,y??WϦ^It2ϱկWIfqWܗN 3yxwk[]''.X B)s|Pc̗~/cە@m9NI?w$ZHMuDoKmHخщʷrQoaeSRYTK{jQDT$*ΡE3(H<%IEb\';*} )ou:SޞXYagUAudv#^(/D_NxSgzNO彐:;kn{D~#fwڽ+5QOޝd+ yVUsu@_1ڤs}Ũr@-AxGq.{p{=W98u'LJ z;=Rގ7m`;vN01X5Tn Y:Gw5Cd(yVEܯx} f?S{xv77_~jg^ޫ`wԾrT1Zk1ƫZx]Zc&E5G*뒚Uա/~ 'xWO$oz8#M&)0Q15N~&_3oR~kX8딎`ג: 5aI U܇sOb<,N罓:NN/$y?Pwu(GqBQ\|L'ŵ6ZMGq䣸VlQ\k<(]|UxI k&ŵړGq䣸`Q\6(}|U⿣Qӏ>қWcS_j4^'ƨO6 >^^y}ibM}RYoTT 3Uz릞Z ?#.{&[Yu)^M9OT57qs3n/ZLyCR{SSMkۤC?6ӓ*z/H;FS[W~K棆&g[R5S&J~X-ɏ9JH~X?ɻT(z@ר=RWynH.6J]m[2y?0%Sy{J]pW8u |o[+?ߺ!yUH6!kDZϩkm[1u~;Sk{ԫXy{vJ"M4mq?vdG_zU/zU=K=ܪF[g"pY\aծ)~]jE2/5}Fjo6;uާN]fޔp38Niŝ;. Vuz%q'üޡ5} j%^|\UwcG;C~%?y)8ǬF?)tB<މY-rw n1^KCj*"o}уTUv4MQo׾Ӧ;O>N=1wذcYۋwΚV%n?zX km#U_{5l] 8xkpTg|7d2 EP*G$WEI(ns7aesCev?Q)()cG;ƮUtTp8ciT ͮ~{͆<*9ܳ9;/\[`xƘHX<EI݇xt Eň%A,!K"V V"V!V<.sR@;OB P#xQ=p{Kϟrgs/Lly~r5eL3uW@;4¾,W|ktϯIn+ }"YɯI2it0ފ{~/?_w-οbqg F9PcNڟr:VckEr*}x ^|x>Es\d|0_`<Ìxlcb-jni >H_k.Ԛݻ::ۑvue?Zop5 Wfc]#ߍ"-,iDU Bŷ9n82_BKqqN:qXcOPU{BAF?&EyAnλp|~!19C}}`S S#ڼ^E:|UkHo;3^GRҲXûoƋshM*9JT8ݍ(!=$*aBA_zYڦ0]G]S9$ VSxv<ǐCH ~50cWWIgw%E h2|:Ҕ0lLGӺNNX N g"wNqH0Z:Q{A!qrvu<ɮ(?Fy?iZ'm75:y_j#%Z1\# $ͥU+依,W[SSi Z ѵ yGn{M{sE=Oףu r"^f*֬F7I77s~Z/u;Ϝ_ ։)̜ b_ۦ)wLhg^|G6sC57T=sέ&f_cTip6fAEǛ7r,t8;33yh8ڋhϡ4%t ymc5挼nf^-#yqZē6禴<)o'z-o@uPn'~&,LQݮN߀iBՠ7&ZKKWlhAtbAZGGZ?"z=z\Gi| *OҪ1qqR ${"Wn +im126#z"* /EB]٤=QgѠ82i}{6:t!eѡ|:t*KH-A5aZ:AfwtUVA=S\5.T$G "l1N`'~.b"niʴtNS]LinY,ӭ@&vZțDk]+gDp[ciB.X=қo?_GmVi^T]: s[c!MAt)r^>[oor>w'oɺ(nH8(^쬳BΨ:*4 h+xtKPk XvC me]{l6٦m :{@^3GmScPnghSAէ*>sVO)ψѢ:wֹrZXx\}pToAȇ$䓘#惀Tl @`btf&ٲ}I'[JuҊPGjiֱh:Nvu:8ƦSq(69ᄏ۷~X9yw~o虼\Q}$tY'@3~Uo]} i 3so^DC/.....^\ |3pp9R[ˀ+o x9 Ѧq&0]Aǃ/6;]d+Ф"& ݷ{(1m5sy\ Ka}JKT:It~"@0о/k+~Իx{wTphoXf%gqt3ߝFYʕz?泎OhS昲^_#|0v ' GsyYܬh~\XmxѼwrtc/@_AێsLaغ[[>Dw !ȤY#>7 O"\P˩F4Pe 9\G[ɯA#NQ)9gc;8|x΀rSuHM.Fi:Nǥot;l6 ᬱ> 9&;2[v7tQri #eAad77HqŴ?Rp,WPr--wqjqVL2YtE0K uLRfC,f5C ;xUU ϮdhqVUZu"`61ƾ66y *s3$~6EB̦o֥u_ȥIsKĺ J ^l)=?v0_%D,gi_R>DÉ)υj,|we&ڣdU&"m>"7, Mx-F$K=E/f'k9ڌNo(Oڪ iK0"g Bd1 s"^^H7.!`}6H "ȶ#y0"pij-"46CdKq^FWJ1~MqD1f,Q3IL\.TW$--#ͱ3vU4aӞ{ŀc>ݘE('j~>?.ni}oN~BOtqQ'[?K'y ~}jUtD'[(?i S>}R#VT3hrո].%!|~I_@mn +[JO4G1KB\KOϳd D@=Mu}7S꾼$Ǘ+Kq%5a9V||iVrPc{ ئ!!G;xll&ĎUPt)Zٟ-[afMxB֚}N57Y}[ e"SDB edy=\|2jHV+{ w\~\dcNc~7>ŕN8tnK:{_p*Z,O:C& PO DΊ$:R_cNkl!5Щљ:\1:;g&}wwqST{7JbR$L.>_L T19*<&+V8aϨia*I\pvjaҔ$L/&myt&'  QƉַkIrEv YUstX|eG8΃a͙td`)NvlcSksPע>(Fإ?òwlg%]y ?=St}Zx \UU瀈( x4 *6`69LG 8MTTfm Wiq35+{L\MMٔs]oRs>/y!pf^{ڏy}q00yHpL$u'1Y?oZY`"^ T|8 B|> a cD` `bĂ80ă`@2H E霢dlrA\ ` \`&oیBPfbP怹`( *P "P k\ ~O TԂ:@=)jA4` gnA'׽bZ rƣŨmIO6m EnfU3 GN[^h)ԉc,iXe؞}lQfo5,h4Ru|-4kZ>;@ @_@I%N}OPFðILO[? bR(FpJYfwW6)ُj$G: 01~<|42QDLD̑D̗\*Z)J*I&I.?w4E% 54>-Wǻs(__Ti+ s" P ̾jDvsʡ[)e_qC V0\1paAr;.s[j+Y`hls$wT^/j63:obbGdIWnlj6YZx:Z51V`W\[g}iv(T77[MR<{qY㨯yikA|u}T#9]Tz9"^jiZc%n+MjH/h|ԣS &Y*3'P-:-fKc|?fU~ܜT_ T'^'NѓMrrKPu:jS}8ېN9mN{a'row: NU)R,<:ѸЈѢ(&1XU&4PI>U'OU$GiL1)q7{?\ȯ^;ҕU QBq^y>收$G=UqSˢKWܾؠйz2FMm.mj~߸-_Ø ٥MAц [7yg:ao] }</=lmU=#l#ml]]xO[)k\=7Űb[w߫֗9 '{Нa7SIc&˼svdm6ɻ<wi}en4yƁnyMИ*O+P}+cFsI-ϩ|LsO49uK=!syF9F?RMrd|)%~rInܩ&5N-ͥ47ҼDs|F;zT r'+ϫ#. ne]UQVYIXTWk6c]i % I?7yeRqI&28$9.YRl1knZU_h-E$Jms鋚--Se]kB+*ZJEՄjҤT)1R*WRɞPRS+DJE8uD"59h H3ܴkzqze q !u(_ٸꛛ4qLD̍teژG9JpKcS%Io߫Ūz^s}иEPEZ  ¼hěu-:h@ϕOgj9=f{֖Ե`il'wF[:JImYuͦ\\9Ŭ˵3n1zMR&0´[ =+WҺV>FZfej eNq.Pi t=dA545Rd[ e!- 答3]'}9ld&%/V;sF[̮+ 3jyXm`+`҃Y`)PKPg 4(B7Yv-CHt plX;8=E; CDRlo9RO. T6r|}-76Xͦ:6kڤ4 %'ckWyZTN=.v(}}"Gq)a}v\R` )M[u_ӳD7h/Sd]c~ҡk?7:y@wOZ;? =CO6K&hf[El3=@-iOD6mr͝b^j|כmB\O%k0{G:b2z]f*x3SN_QCp;Wz7S-i#ޜhXTV^VtN_\8mң@7swW{Իo0K'Fq!GgVV7di{ݍx <}t=W|)rk{z篧xS9i1;Ս]@xz,K=:^cvO{*gUGk ޥNNo2_z?ܽS|L:C-/r$\d&io4ҿ򆕫,bs}]VeQ%6E%ϩ hv~N'n!I!Y#xOc?[d`Etca <Ӎ[.tsO8|Ïk|W6.ϤRYھ<2Vۗ@r?{d\@|IK VNw+R?1}[ӡ+)oI uM/gh^':S:cbY'zu(9[H޳XG3}%tb`9f4G]^m |66A/Ҵɑm tLZk-*O6 36'&R\nI׾|CU'L M68Wۉ'X`\ >tׇ6dCcwb \V00wꕽ@#~um6*Y9[z/پ&9uxnJE Z,o^_#{wv|VI|C. ւ{ ցu C`#x<flcqOm˓#ES`7xπasyzi+7Uݢ!@'dbg6=z_|w> c⭲1 o} nq@oO9/p74| ZN7~$8gy=~J+=`#%QVM`4"@$c%]qcĂ80ă`@2H@: KrF0Ah`}utc7bu~?e2O@9tR}F# )s"Yv,FSY,?~\9:EC;#ۿ&lhJťVH [~[ƠDuCˍE4&ed6Uc9t+Ķ8!R_|_9 #iAyۊ[1J[߈8_t>r1fh"V}1XEbc 0zBbh>8tHoD,AN䍁0HrF`/lAlO⨎olVOiRvu>wV7,*>9X>+CtaX]ty.AD,e,]\Lx)Η#B_|!Sy+ o@9Q^|PnwMflap#|F%_/_*_'OiB(˱r'ā $ڞrPq]nf??f:#v,emTbհ\ *P|)\J k6(BSf|}+0烙gq:iEHKŠ9`.G}Q] t`m7ɲ,W{/rS;n^0K M}3̜]#~i;2gwgsFBkmѕm;1҈ۃzQ~Cо+kwL*Zb4zL5Q=sD#KC|WUy*_(EA|K%7JʋzҔF +C癹*bP4SєߍT=hl}JRAcܣOx11>8zZ5]wޯbv)`m6ol&(R>N{NBW3:ڣzfVh-\l⽱[軭؆؁x}7^тD+fm!F26N.bD{ʹ"Q76l)F=;-ϵx=0e㧫-ڈݴJ-k,lW۰Uma;ch%6цr]?sytϥc㌍m;AOp,N圠LGkbۖhh&qx؏&X3;y1lsmOI?E>M)4e3 u:CP33 448E{N1OҶ|'9Nsq>˿#WYu+Jguu3; ƾZ/JՉeb_W$\T \=#nF3{x0ƚr3*r"x ^^I⫑R*$ Ŀ:]q^Jýʈ({e"qˇIkǽ'k @u{_/ A/uMz?õ+jUB+1W{+^$nlic5@Ek#=v_Ǽ3~L?ϱ2g>N\{72QrQvId,=̼ٷ{{]ٶ{QwQ[yw1uv M/qV?~<, :lCANHKH%mμg.g~*($#8C8{rvvhQFK~ohvM=LjhL9)1(hD}S71gwRܗZgb3r,?~,+2Qrs`39#;: x3eά3#1gtsPWrΫϩcϧ˞K[3:vϤϢx>*S do ߪn÷;ԝKTw{T-u/1\y{Z2Dz=,=W=Ppg(ӶtڙN4QCWBō"Eu IVIΪUb:n5B-U~qTw&Vܫ'c=z>*}UxOLce)q_hpL}S0L{mĹYD|F3nq=usd|FAh[зBX<,y1q}\KQKUP!F[HM8M8c8v @O GeO_d~P1r L}Swǣgu|=bQ2їmT?-PQ_F.#}=t;q:r<:QU}]|8}SÔ0eWf)6%FSb5#n37ۂzVh͘j5ڣ:uFte$$2S(#P^ P~ uOS5udO?՟8)хYuBGƑmцZH?,E&æZ^>dhCjG:YFm2ч6F/Dd𼑎4ѝ8F qwګvh+R5H}@ճRc+?XM'~eePnS${7c2G}~k`CsQ0>SpN џa#1HAc i0usE&ZoՋzz722(/rөC:uINz^>웉"O!b}4:8ԯ?uu b?c0qӑI7z'z C ӏOc}k OTL^z#iH衂-wFk4}<<||);M! a =hR"y|\!Ř"زݒjIS*(P*MZ{zRĂ󩓫XB^7 TN?*KGӷz<*heP˰:z1\lPImV[VwmCv1!6}Im:Y5Z=J +x ^KWiH88jfIX Wc#6ak[ o;qv.1bx>v`Hb;rhlflRkX0V1UCfok\C/׊"я ب6aڢMdʤm}=t^7Ґ*q+cFyMب6X~Z8h_@iZlFDD;~H{~H~@@{_1NW9eK+qp<γϳ8G ,18M9)垢S$9I;NҞ$;I[O擴$}q9=Ÿ<=5i`,l=u0Qvԭul~q "*^l ^e_5b.:7Йꂮ]'] loľUux/˸x׌qAthљ6]9;GdM;RUґz_or,9>s+/.E\U, /cdfVk"gqq-~Mo $~7YTp8_?)3|#?=}}>O߾O߾O߾1c>cg|x G|O 7Bn'i_*ҐN3=+?L.Fm3L;Ǵ//>S"K )3t9}HU݉a) >VN{Tb;ҥ|_  %HA𿅓_I]zܠnT7͖-QoXoZT 02TJS-V v&\nN?⫔UUӷ嘴=!3<_'ԊqOx`w{~R}-|Hс!xlEV~ r`ApV$3\^GC䚣IMljjzJ Mnϔ^ϴb!V0[<9bO(yG!S[@ 8?4L{Js9؎xJc\a7qdI|ӈaٮj)s1[ʮPL`ٿY?{Ϟsgܙ=6Ϟųg왺虹Zl\8{.z͞uKigٵxj=fϤٳh=s̝-sgbgbg17vUd3[٬Lo+UY+wʝrgbgp׍IsP=3KsK?d'sID3D̐=#;&ٳ?3?OlOEZ#2x7;S=_zwkٟf}bg|gzʟgxܙwVǞqgrTko2Q{Wtǟgx{'՚IfgwŝmqgYΰس+gV>˚WQ{d5WΑ#s"eCbB*6;bE{\;aqTn~#znÞne0y g΢Cpg7ܙ wFlF wÞpg.Y "-,EV*Wxy)*3GaOsyDe#y{!vZCrx ;+s \Bv?|o<G.Іs<_Ή֑8C)$N8a^?8>~b-#Oc/R^C{(7M}v3fv3vv3v3v3v3v3vϻy/>qm|rCߡoC8#oRoRoF8N$N4ۜYmm;ws] zdɘy ` _3~wxxOqՋRlKS:O_E=U>3!1m]j[Fn5uhyhaiyr=M<~">~\=5Ïԝ&ƖF6R_ճ{xnSeiK-ܫSzP=D9o:zx6>(?.eԡNZ-ꊚD]A9zjqG½cm܏hB_!y\xT54W-TKusIƃі:{U-܃Qb߸n|GylpMJtc#ynbX o0b j$2"P132KV}TꫲЏȾ0)I1%4: >CP; =9ўA>$1),d>ɴ5I-,801(١1BQ"Obl@8QjZbldZ)2:Reeee$4,RkZ,P/B,|I1P(ݑ'?Ɓ1O sEOeE戾*Se,8z^0zFȠlG>U"UXj%XFA9Q 귂:"T}iKD/t,"BQ"U3U1|}hZD(cH-RURLEqyc̥lƠ1l<ω-5;_C(wmH";y-wc]󯡛B*/u<#<68>g:NQwG18*TE9'1`!I 5 Sb ˱+*ZZY3 0QE6QOc3L=-u u1±±*JߗcVUXgZ'6֫ kl&^lZ69VUjZA,WwcZVXcY+֋uub=qhcVXe(E b cXOu`*QH ic9aXo+v2F 2NzsZ>`5 yyQ[KJy?_UJWy<㫡O OVo0maCG2yiˑhcz Tt HrND$q GHW^s)х}MοM]s2U/ I/>_:U^xX 2 #1ynbdc01W~h%> ^2."^d`!a1`)ۖgh3Q8F :6y|9.cw,G)(;E(u8m0N$3NᴘG̣o:p\Tp_TW}Uq||2uqr T1XEj1.QKa(R+e"ح)=_ g|X82xl9,̶W񂺨.Q+z1zz:=lP')ugp+_%|U?[jgvw!jxO>uCq@Ahğ<şq`D}>_ qH8`(9 U^GTNjU6ӶSR-{,{Ň(/}[޳˶w<{۞=j؉m]jw=QB[}/"/_-Y>Ux1lO3Fl{Gױ[R;6pO7[wc~irUk]G⏑ߊxl-̶e(GCۮq/}}O ?*zO?]>wԳމwǎ}Nr;pnm*rM*=ߺǦwM{ޙNqqw/>_{_R޽.\[bkqi%ec ~$3H~'Tޝ/xqvr%-]-,],+]+XIgqYGž7Ž/ž'%^kb{{IM{LI{GFEwH>~>s\wrwowmwhԷƈa+;*;)(*rDh#=p_k{%{${"{!Z~{bKH\;/CL߼+ouGBeF8sBe8ˠ; ܻ MPޝ]A`ZDI|A({$J Z?xAٻ=k뮁ػܻ; /w{߀{π}{@{쯖w@ywwww+YoZ^̏^_c޻k5-uO\|NYt]^ZnfW7V7V+cWjr%j.ku"] ]Wwܱ۫+WkǮ̶W[]aou]MnjWKVG[h5 _]vsWtsly^r^ZќgsAV/owj[lu8k3uZD+Gz+Udw%ryaUѫ#UUAV۫Uؕ_o|ŬW+z핼V+xջYW+tFȵW▷ ^뮾^y믺WێV+lǖvi̊Diݕ*ZlV&5=XuY5<6[{}mRյ 7յZwEmJY{rWVfl՗vpZEY1kd^cMl1dElwkh孚]UJVȺc+2vŮueV+`կ^W[+]WX+\+N]Zk}u׼k]׸^{}]Ӻuְ_ZV˭\kj(KʯV V5xoHB_zUKIfs*2kZY+5:V{ kb֭(^]::ʭO=wu+QkQZkPGY{;uלYkl>^e[azܕI$WQ{oŨZ0fBt24Ȋ#|Z~5K v5^_z3vզZ^iWe+2하Ǭ GWY>]JwUe3:]ֺKw_SiMH[Y)wGWH~a[pW51Xf$Z`_v5ck]ᑑ^L1*גDyƊqj<&j&)je1+f}T(3hn!Z`r$CLDLӦtږN;c"icH#"=#)&Uꪺ:bT1-ukK=1$L5}ALc,,fSّ~FWՅxFg11ɘDDLxyc,x:NWgmttUIduucX8LXMdLT4LW38LN8f1>=#z>*,Տ";2@̌ 30=2HLT5%2ގc1F(#1psce|.#Vc%VXA9+(o^&KŠp b cb>yHOOF?d^b@9%ZJߗE*3JWi*}b)5`17XQבJی4.AYv|5 c,xl)$fcڮvSj'vPG YZ̾R-M [CǴc0ტR!;a3@cFL׉:eQkWŀ+b`e1( ^3x<'Yb#9bwrІ3;CߞgssgOGω$'QNuܮKq%U85N)u{r_瘾N9/z2G_E}~O79^TWxuӏzWeRn?]*M]\H˸.xx^]`_9b9y2埯gO ?xHA"s^s*޲BRJ$Q/) [1ތsJmi*]e& I֑pȤp{8!sc.fbFb2)і1)b*Ɖijal5 ey!K<ʣ|O_u-,aSˡ=9vI\(q\lRjeۻban!9{m89R9XW 8/5zfFybg՜Ȓ\5O\ys^(PbH,,5Rl)b;[P/ErU1Nbfc'[9ck.ƄcV3 5rM c2e:f"1 żDL=h$GLLwLΘy1]LlQD[Lb3t(ϑ'Ri*usVͶL1]3LKe>l1 13\[Q rhCrs̍"~0BƑPqlXBH,`y_8RQB%QgRC.r<,dc&304e,fPLX>'~e'}c/we즬]b K9W]yv %Wr>]9t)̥r\ހXXö}W%\-m1qͶPRn)҆RBJ8F%] DZZ±/a<0FJ7R21127PŘD&QIsD'QcD&ѾIsL)b*2 #cfoټ> lk 7˘F\V[YmFlzmת50V˘I,̦U\!_}PsZH9"]Duj=qڈMj3`+۰],/5>+FQ،àc׽ c 1sWcF)<^3̶ϛ2^eW;1-C1/[ccqma ^w?`(|W9P*pXEPQUL WW Kl5 1'Ubr<)TE(_2 B1ŔZB\K#"JDXv~gT# >g\}a Q'#e!eP'|Q6VBU@{r6G,bdcF`4\U}O (S ͡>,?s`,;;0-j&2~6'kg }RTVƻ2vθOɴf캟Y~Fͤ2Ys;c5&;^xl&l~Ylv3djm7.>Kߒr[YՉrՕSECܳoߖps *#3v840'E+Ž sn~)o?s~GӼo|oe3v7DYĥRSrߒˍӺ蜬{rTn FglLe+Cff ^fR{agn^ͥ+`k:fCG"ʅyP7i>|cfqV ,3n\:.9=U&2yH?g~9|+VNpzL.0:___\_<_t/^~y=7fZ;?_W\]r9EVlͿٹDy77(ٮ/뙶lvv-6fϦˤˢf旛93fdϒ! vȂedK /7Gܘ V>΅Wl+:.'>PϸwfjeBe2^~rfb3[Y-7eg>J֞RuPxJնyQZDp/:xT:z:.I*\VMAw3RF4HGefP~uA{PԿGx04K=ivl(Pwp,d.$ <)ǗgE"+E(D*}穹s38N?<3rUms%'1~~klk*WEʯp}>c];m<%XcZa12&#zsxl,,XsMK<^9W|F?1uv|q1cˏ9s|?Xq$fcc '(k*p]>Y|glSbJRgF=>NQϨg>=c3)m> c>?cm3Oq/˟=&GQf%v ά;=9fo7k|Nͳ+J5bϡDϛ$͑[ğgDęg>fßg0ҭY3fDI<Ñ88RRss=oVvus~Nu3un~͉yP?9\g?!5L3:g4,t3nD،cĵ}]j;(&׸'ڹ<;s~~0Hn 9@y[b}vU6fQn(6f }A3}9Q>;f*K⧤I]f.q.6/ssqnͿyx6'|W@P\NTFĹY87fޢ3nI I-/ܜGsgKbdɑ-ra~^+6ֳ֏C2X2WY+T-yap("'CTu˿T"U jjFM85}'`"1'R$La2BP)J/Ռi_M gO` QM'+ƨji!ᢚgX"*RmԵ3UЍb\&3Z Vmvuw<7sV-QSXMb&)j*rA^!\R|`49{ !/1cCߤƷxSWlz3&T'P(P=D5IMFe1IMLnq1G]\[o-xɘ 8&c r+:[ԢPK3Zf Q+ch"XfcPC1Pz1Fe} s<hG8*3Z}f0;N,#1R/;}!zn( ]y:۷G;7 PFkjMtLL$Ld ~,o qG4匢Q;GQQgEFSWc CҞqO&`"&ɘiq13U6%:64:.b.uK>,1.ԏ2cB=C VOZ1T~qA1xƋQ&xIj"G]W;W;c [ S0ޮvC{Oaa+`3oFl :%JZA)4,!߳Aj ֲ:QmflVl؁b$a}R!h߹+CaeՌen8$i~Η+9Orܢj۸YݤnT7z\g?k4M>Oj-,;c{:<۩&܌۪oObm-fIH\-nV7Fuz]Gl٢6cwA|]ש뫯7Fu5-UݦnWwN]m)6Y6[jva7`/>r tu3ZX{)qv jfܤnT7P/q\'vDZckiqzAݨnR7>仍)׷GVN韊~OOyHl`<8և#ʺ/7շԷwzmƔ[/?<=[,[6ꐴ;h_+~#Q]j9jliB|auk :<};HurPGp]&3{&ۅx ;]4u7=r?6BRhc&6H:N:=1vulUۤ MKK?e9,b}il xlXG<6ZO _ƀ8L?*z}9'1a|63q}4X#iHHHHHHHHHH>Blzë&J V_\/R/e췂x+*4}Gc4{ e;Rg,uK^{l,l,uC?c8ƣ i(>G?`8NM?ݵLLK$ KukFŝMqgRٓD&1 >.oŞ_UXs)#N9*GaƟ!gFwÝg<Fßg.&Yg*7OQ(g6G"QI8o^Ý0b-칊 Dy1s\1 \BƐ; 3OgfWr`ofe+^^=˯w*'C~<pW/'^^vUMk,C͝V^Ήrno9 V3gf6slpEs5v3n:6Cfc~6z}6Y2goo2vFY`7gzz^7fu݌uvܺ[7[ghf+(jg[c3NF5Qu =AͷFUܩ/usnNͅ9P?qo[lYL7sig,t3n;۫H^/6fʑ?7f,ݳ3{z3zn6╗V.qkKkݕssvU6WW*=w캺VXUW]{ۺJ $&*-+IMXt-`[PS%<ΜL&}ϳx=[obH"^‹b*Jo$Ld!2G=X~m4y&piYtE;%]Zhi$g.}jըFQ+6(S)|Σ<ˣR(a<@E IHu>9>r>|Z5Kkߏ > oXuWl^/#دHe?ӐQ1j1A?2,MOD>ߌul}4~迖id(}FJmaHOHóLԕfc1cES<-I"Se%ebBRQaTUFQsQG=Z=7Vmϣ<7Χ!bʤ!0G;FQiTXW&Fh(#HQ׆S2-KjKf$- 92 +9Fk!?h92OQ$%1|-, ζ HF'g"df#8f3D&22jrNxJ(7^+/q xiC+M f!ۘ9Fr|(b|bW¼N+e`UbX+K+7ʨ_+=O(/%y`6Yl7Ә!tQnT*K5eBfr Yl c|(k Z'9< ̕Eb,umLqk\iG[I+郶o|!F6fa&fJe#m疶M[b }B=}J[G/pޤܛ&AoX6/3W}yJ+Wb>9*̫bYXX1 bіck6m)k%XEibcP:E ٶ b.\γ:ι:zQB{Ŵ]Lg!",,2, X߱+1(f,JR1FZ9*JcYɶZ)QVJE Юgb n: B5ՠs:ԣX@Xd,%RіѮ~,O%R,3c^컶Ж26KŌ",0s̏aM _D-sWZidwvR1i <ז;bloGMN/rmrsA88T9?QT:G2;G JTըqI\ԡ 3 F=0jQjTQGQA0v(oq+;:.}s¿0.B;w %;Rw|;(ψڇz;f/x?gFIň=2iPn'T=2MgK|hDRt<2oI'~%~q|<;ZQ-_ʮɭgsi;sΞ3p, KhtF'v .}c~g+\9. e~C%Ǒ!axv CYahYFfh_x= e}v7+" e{V튬BErˬ,.vggp-b_gośN"ӶȤmZT-2QLw-HO:Nby=kMT:YYrX-L,2 r#.?GcY z]vYULd8Ӭ{ <M*hW22[=ѹms"kY1\ܱXُ=a rpwOr_pw:7ݽt'Sl䠀KxٮSnWQ+n&NpשjྫR/1Tޟv>r)J=/RckZn{kǯJMv-5yWMw>U*N:Uu6z,^x5=rSχ3lj|yq{ܽz{^꿟viw0ҏgK]| ܋q).sW+g9۞n?$rOq-7ܛGiw6utmo[ܻ݋Ǫ溥n[rTpTskD[u5םnY; 31íګw%~S1~&buhYԗIݙ!*QᎧc<;-F Q|!9l7MYI30641^MW)N}/acfb1- l˖yg|.`k,zEsx-ɦ6KLi%Eӳ)d&QJH jZ.|E(6Jؾe(9$$6S8fTQCjg30D5=gZ*VAQsPw%op9%[,gq)XʾeRnex=Wkԣ0YEԱO>c(B1}L鵆#1XVY>4>o=Qk1*9lkhEd'-ZQCꋭ::5x؇"9:1ǘ~aRd}eYo1?h"| NG(0 ٟ"(Gg7P6o÷;{L}݈E [ ۱DfPG uyn+N+n+rm^=jPVK&y}al6c+w-Me;|K7 ~C{Q2FujԯЖg3ZRC<ۍl5R^k>O2(BG˥^-v<[،MƏlmx ؿBŲ[u1m̴T@??K:?O9OD6ϵ H1ݘ%qx1h1Uckgt7z?/ӂzDn9rl0LjLK6i9}ؾ/Q\M}ZO;i?p\(PN?*JT'>y.e_pqc]s;>軯Rt!>F}f^v%0.eƥ"~{.'} ByuvtiRJ ;;&TD Z`vC+VV|{u#W>|YZ[};ZG{u^g=gGV+hDW_+c`Z?[ka11˧mZ"4MAcL;ߓn!i0R(cNf1L0.Z3FƷC 7LlOJF*Ҍt2Fc1VlAEFKMfK{ц}p;CdIduを-IA޵pn6F!iHHc%%p0!,)ƈ$EOZ1M=1B}>7؋=2>]} K;4 6wz lc ͺy/deeFAMADs秧}gK13e.|쒟Tp 9v)`N3c1X`,|8XX b;h@=8aljKc#|-fa8Om՜g^Zzc!i7d$hKE3emMbh /vyFQo?kԊƠQDU+vΨj b},%bORί6>wP%|h\c1 F=01)_%?kQAC_ZPN?g{h`%+b ޴ebGOԩvpxh1v_?n^^ڰN@ cD3I I$?nwm-M רH<4 }}B?_WGQVsO ІM-mxG_S6.b'cb4c5rgcXG +_O;io=zq=㹞qJ8oKK\['k7->5>Xx:*h=zvߥw˻Ibcm'}H#kK[OOp鷦u1V87m-cƸj3g)>1>6`hXjMW}Zhew'rn}M)5u}Mm}CЇo9?/?#6a3@݆wy$vPc#5kMh迻{m'Z9Z(;3یbl&߳w:owml 5M_Oz|{mz4%~~46؊mvvPO>F { izh7#[dhldDUkQwi.0{d4s@`? xj?JÞ)v''muO6N1N5N3Nƙ99uzvgFd4rexv؃F #" ~Hgř[ nfS,''YN℠@ =q܃h> x5WD?SpqqqkgR^;<6el`7vu+vhx43wӌSSN;Q4?F h.WW.|y].K^ܕzsOlr|#~0wA}oƏ$pq}ѮuƯl6ӶSn7;^.g+M~l7__{}FK5пeEwb:Kc/RO;#scVZ6,)r?4$<<SPJCI^G&uK0LOCrGr(MLNO8۩7Ǒ"R^;ܮĂFf~纡VYɆX?W?s g{T5IRP;lH9'}o3v-ذ3p'~'~Xz)NFIBc(qSDqG05B?@?ӿPg|^(%:2W_͜)ϙbK~ȕ'd.p:JNĩOtN&EO HR(?ryuu?bv*i" R(975"?)(! Cѓ7eCY:_-Ck)\% O4+:="8i)OOv"S;99" Ol&y@F|'fe8&ܦ9לne4XLx&3 ,](WXӧ{+{3%2k3Jl%yD'vnb%$~`G%QRc1N-ƋD%3NeL0&b1)*V//цg&jx #x ~K[ړ홒Ii`d)7d|ГsCxxmcb<2觖idQ6(Ogf_o ~x~>K91&5v=YFH?=)}xƃ]ٌlΓ\1T[䋻 TbDܥJ2lRd)Dȧl{>~ydNwPAܡŝΘinUcT3ZU;q6픹򷉺[E}-!f1/&F@ߏґw yMV&ڝ;}7$];H;DŽ(w;D!ϱdw%h{B^} d4֝ W~QiHb?p`c&Nd,Od\Od|ODO^;^yzqh{v4x`/NgYY'V\_fq^&|_f"b\>xZ& %~f87hh3:W9 7P0]yTtqhshWphWrih7}}я}kkZh6FckE:=VUb%;WpF% Υls)_JVr\mfLiomo&~nϞkWw=uk7 w@Nq=tC^hWG?u5qrpܴ8Eq]?\Kwa6F>z?9Oc#>)OQ>:D? O?N~g\- 9{}GG{D㛫E](w9tG>ߺw]vug~W'#Zнӿgʮ'CfB>y .:>bv%t_U{T|1:;wjQݿ*<ٍm}=M霩Nj s{(y T=\JdoW=@qW A=ܤIjs@ɻ=jH%WÜUJ%V#GUj%PoU-+e bRyׯ TOuYW%8u:&W#UouD%! VSܽCuS7@%oUJݮ8R{5ܦJZm<ښ@G&q1U9Oopյ6D}B;yک.Q TmU(yN]NdoJMTSk(_}Q&PW?:Ly[z=7ߩՊJޢJޮԝ6wJ5@&Pr*TJާjz%դJ>2jl%U15"OJ>QOS%C |c'/(9Ou}%GpGHU}(a3. Թ*C@LuR'%Pr:+{%GUԁ lhr(Lhڕ,!G5;jG% ̤E%si[yPeB#ܞpGs{:=*G({@$rDfŎ0ۛٳmKFEmKd33ƄۙnU(Y٤LF߫(kR>Wo9k:}yyDӏٷIMal ?C7~6}IaF`$R4cF[DզC@&Ѳb\#`$1F71AgE}_0n:4#7ztcd$)PV{REE{MrXі&#LLp~L#ÝPkh opkF%lE= =^qwn98|Αv}>~sss˟,L3p:NSq NfۓDD@]'PoOI;};QHGۤ#1Z S$Qdv NC9g,81ΥԩG;ўg$vl1HΐC;rN7N)s uh'S'J31 1c1.Ȥ|VЩvNis@1Fs?~g4`,6F&d{8vMIe˸SLSmAO-wh)cz#}H_o7ϛv3>j ML6yY3zP6@Hi c68jnB};9mƭ<#6R~dzN??$~p]])yi>b3 |5i[{!Y 29g 5Ap`}Ipcxm_myqhùF3C!AiWb<>'Fg{mAӶmϳGٌ::`208g.96?7G؟p4{1E̔g8rV fy{nhnke#]fIyf2vߓ?6gҞMf3Hmh٢?K ١=+ ٟ= Regw3;{VEu(wp,.v-ä:Ånjnk^EyX9X5j?6߲Y*4R2"v8g^=gZͲ:a3ͦbͤϢ;:GŮ%Cjs&~5kgKgJfIgHYQQ m9ѣpƹu@6{[;.Qjl7K:o(~NڼV97&y%?:L$pgv8?yW9O|pt\'ctt},r$_(x4ŦJsfzw=Nxؐ}&v<t;*%Z}|7{=}?zMfqi*}T{x \UU!G$ "<ʞYeiY9eĮ&6Mm&ޚƹL9u~1ޏsc^{o9Q sKәu5(Ink&R@0A  T `Q`(1 āxAHf0L%]2@&FQ`4c80LWD0 LS`*xl3 r@.@> ,Pfk` @P  ,bpX~ nK2` P*@%X nUf XjP j@- p hkO` 5g/fB i("F%?iV%eIX6-[+'m`PL$)g)>-):o e>Q@jO΄lЂ?)́lTO_j"G 11~|pys1HETEqY WK1W/S|}"}"F.(؂(/xjMam9KJ!fyʕ1(Hk3Z<7̹jD:KUMgt2.GU\rg)P9L;Ƌ\%X]i~6*Rjf{9$wPJ/bet լ^]S=j|rb g6RW^alO:sޮ]m9,>eeY{W۰8'UV+@y*kRngA{,)#RWF](wYf0 ;56f݈kGCTU"Ԟ钇]G=9a:Quz?Dש]JIN;EEHDBr=HC5iqHvݘ$Q{J1)btE~_(ε=LׯQq:X<\Ro8wӫ̓r;u@i=I-c k{ G}:e~þi8q/x߂v ;iDm!㨡~~_?; 8liwNDžGfYq㾁权ǻCmCjˆ8rst9e~4\{֘$\ }:Ӥ>ڻ>fw:I,Ic;t\~B ['/kpS'46O3J^E?k1s}tiQ=mK=rv F?rf;ۦAgd&0u86{|ޤ98hΡiKj@u&t+먛,^!nz]HOJj+V`fַF24߭ ]ʤ~.xGw2R7=:q.qhD 8;Ay+*- V9VUoZa.hi)DZhUꤶ^JNJβR{Sb[i- bnjJ:Z@I[j߇tA|NYx n2*OK4TYt<%k-++*+^AiftLo2uZ8lŊL֚r !Ș weO bsbfaJGX=Ϫ*w>Ek$5(i@4F4h*7!@zMNP}UZC(uÀY i)cY[Qgu,5b G;Έ2)Io yғfGY2'?eF1:(&<8͵i<ש=d5;.6ԗW1K<ޕ'gĜ1G{"MS τn9oRˠ¢(/nлXҀ`P^$k!3c@v康9T?:wae_,Ac' -1NZBo52o+W$YU;dO^74yJs[ ]|,e>>Nr%->Z|FY=;K8=SO,aVg_{D}mu9Ye OӬB*40B1>M?HJ3O6YTʉ/&5RiʟUqEo\kk_]^/6(з&?ZVVk?tD}vyEVhS\#!?8Jd ?f U&h~1=I;|sEcVqϽ_1ֽn֣vrt/HebKPc}EyƜV!YnmҒ/d9y=a$1; *9z;{۶{F==1вEj`[d4:_Cߍ:9LD{G8_369ʹF^Kѽa٭"oÂtÙpw(aE7zN.st։=17?YcD)w> oz8 yӔL1Em7?|e 4]=:;RbdigNg;LO>r)}MNʧ4B}YgF=:?bhs |mWgƨccO{g1̷gƨQd;p7ꪪmu!69?@cOSZ>ӵgO¯KO|4z\F(ν%uk⒙ f o(cW*}U~ޟνo8p0߼O?*TY+cJ͟G#ǟo!n8?ӿO1@fO3mOꕫхC,ѕ+%KWOɬlR6pR%U"W8TjD^!Wf٬q}xȣ W_E%l(o@vKP?Mo7꧰݀ޓ M~?Ml[#Q<}xO3`'x<vvKeF_3 Weltx^ny w: =+&F_z/L߀(Woc =? +$//p|w_1\Mp5cc1ROi"p~KP T¤įQJy'R8RE\9yz"yRQ"6Ք(Gxc  k,+Q,TeJKVRXF*eLfiy![KP΂sY!zQ 6vΫq݂8Y(7cl&cf B q>G~ bQ> r" /r!i @}ן_X9f-a]1L&rrb3p%,"Q> r!o @QQCvگowz>WVşI=`D?;a'z/N~j2D**q1N1%+=&""NYCşֵ@.EʓSDroY0oJ&)0x&<~?G*@ތ:6f K3t"<2) ADl\b;xB$ɀQxrhp" $D|8<ʼn| ؟x\[9qhO$$$;؊UjwsBpLDs^AlA{>1t@rPҊ!ɫbg-8̉v%Ήs@@Id0#o*HtsUhEL>W8#n"ۤ-u%HC~" "2!7$A+8W!C }0` $D:h-hp+ك+8 $ɶ'{O|qo9`[~ʖF5bv [5lJ1A9Z0y ,P3A>dz4ԙbL8 Lx<y! y <1Y[( ׂ9`.%Ȼ,ŐcԵ/vvs`n!pLJr$ْLӑIqɃ:r0Qǃnl ٨ϝI<% pL,zdb[< 9{CZ%ۋ ^?#.N2 |åQ1y`.kl/P8s04cH/cb6tXؾ](* ,, ,wTE+FM؍ MsgfgYM^y_[{f"q 76bc[۰6^;I;cI;}uE}wQ] ~l?Fflͷ[6]긍rijvPهFoKN|=mq՝z}[lV(zrՋzުO?^|.fan{^t/㾗{{g/u~_O膮b7z7y/.VQWϠ]|~=n}GOqPA>H[Ҧ m<8H{r?rpЏg?~}ߗߛ=y7vw8õnUV{g V:ԨZi %vuLU"`%RY=>"3dD.Mk5Z#:㇮1"sbV%W$J$v>ܕGUG Ұ ^Y$^U$)z"v-awǬ56hz v-: ޗH`pUZ++VV;UV|?3%#&Ső' ?x9|j|rBlv`Fi\4 BsD+1Qn-•1DU-TSũhfhZXLbQX(ڊ*,j mY0f*<:gP 16>&|\x-օ!c6b6c bsAAԳvRNfhu-TK"њxF3>6lě-ج6a#6`=qZjʯ&*b-тqh85lg8ML5'N (ob&lV[U;OL5ũhh8'sq[kc 6Mب6XZN_)hFh,?.n.q|>q\~f{q@T:H!R|e9. g =<=Np ?Oxr7z'z; b^.LNH=<63wF^:BǸR\Fz7rbQ_&ug85;0OsE7:CD% \[UͷH-VՎ%jZ&_G,qTXXD\ #::: х|oHIoe(,'rڽZtg#E~kv`e6s0'a``ygyx"}{^u]N!wy wxwz.Gtq"]߅zP_g.nbr Ϯ聞Z}hhџ6h1 }]hGv ]e\o]Jbv^Aݣu.u'e;*vRߨ#|yKsq8}{1}{np).ŸHuAgb]:#a9}8>A'$"SuCv=h!x9p~<Žz~9ۺ=H~7j_kk-ƵEV PHqW #vQsJî0Ĭ,E]V{|kq v 5A5@{jV J!*!vu`W ] wD=?χg_g?wt/p⃧Ӻy_ĝSvr;񞱏&JaG?YO}pg~#wmms}{D>MG?I?E'磶~W_S)^CGPhĩ)檅jZB(m#y^\pV 1x֡VȦ}h,E߲g} ) q:).Ni*;A Q)1~4RPP5 }&8MU34G КrFՖF;2N1;] T V6 ъ8FKՂL5ũhƼn4R )oB ;5F˩hfܫhh}lڨv4q::aUy*t滯F1uچJDQJ{Ji[)m#h1>Rm0%y;ND8O}a$q. :tk9"5wf+R ^u@G1.C""ES_w1ϗl䨡UyW($QDFq$Jij=@\U̘Yf

K|1@ zc(/*q8a(rT&2(gH#?nC_E]N9'q|/Ke#G4s+r"NqM]ԛC;2?a̗Es((DQR^7FP \I+u1Dw ^C0~0W3W3W3"5JW|1JPO*y.E )_(C.233T pR6b8eSEUD"~6Q" iU@_Up P Ynd"rF(ab1F*@\5r cd"~ȑ5u=|=j7 }i3d5(`~6Jdc#M4iHf1_y6`.|UXuFc!q\-M,fLQJ=F6T#ÑQ)"GI_ 5Wi\3NFdT6r`6{V?y?S_bEgl` c5Hc!q%Kܫׄ-f|&aa7J1RhN|O?y^X"?[ W)yO1220Pp?`|\9!s#̗;Sż_K| =!/!e Y;9'!c8riO QLik~g};Qa櫕L^72"e|iM#z}_ tcdO˒>+~ ~"Ҽr?~ ~Ʊq:QN:딈!^(^(i*]4LQJF6T#ÑN\i'xq:댟G3NF)u3ɿW=:3NSmmTkG(%^E Q(DGT {u`d򽑯 I(mhWb57+󯄽Cxmq㝩c/C"~ jk1IML5c_YLI3i,3v_}> 1G+ U`w6}= vWA|chCl&Z+2FY 3|-#F˨VS1NSS5YMgj1~mX%=Ϛ{3. tF8ErЅODFa8Uyg#_ x1"Ʈ,S00ej1s\TØR-JFE70xςGMy΃;؏}؋=؍q½e{u71I̧;hڴy>d>dvܯ;Owp=aŽ{p7tKci26B|'4vh3~x C11/Ǩqv-oӘO硂cfb0T;o"q ak hs S|RbSLߓ1q!s4%?񪽟xx D7F4W-%jŜlŜMTdLT5 )g˘Alc&uͤޙamarqj\k}3s}Ğ:\;_4wFc^e 9 :EGIQfy'8 3|-#V}TcDĹbZD3111YMrLT(;g4T#4ısIr3=s*w[(in4=EFcOSPD{ 48{3 O{Ӟ Ns'9ӛ6OlsJ=tOfĬyӞ'/[9m{Ҟ NTDNP''=5鞖$ÜIx' k;CN=ƞx N;8̹oױG{=_=X۹Ysy 'ƞMt$g33ɏK=J~18{О:wIÚ Vɜ1=_X׳ɞ+ ?Oh&{03u+:,qО4 蓅G:UXB$a)`fS͕d ?+X{6t&0Q"'cOړO IA{2О i%ݑ&%i`r'A{Z08Lp/3rϞsOٓ{'2xR/qǜӻ9=wgbٳuG.t=?gιb%>/瞕:r>nk3qٳpgouUs=%gO;8{.$\ [fo{RVtsyYι%sƭmkw-86$[mg؂kZ3k{V͞SK|FmQ9 ;v4QhKeDI<=fϰٵέ;9fϢŞCg칳Yp,~'ND=fOŞ@sO%ߚ+5&sOw=mjuf)GҩSMmLRpj=P>̉ĭk.grr~I4{ ɞ>wnbOŞ69edOŞ*"rO'T3CY,猑=_49SdsDܳC=+Jt>(g#J< l@&9O{=ٞ3=<=35N친 wO9ҙș9}{S7}s7ki$+YqOO7iz&S6?M]N;U㞨i${=ALEԩxeĜqOĞIt*&:Eo$zDr*DLi$̑N$f]<7YDdL+#xa'dfΉS0|{=ny%8fqOs2%4J#^FI=(Ι{V%829A={IoC_s7{SB g`x\Dn+9uQo>*TXŨ%|u~xJ= G`V0nbX(AUcq(E,BTa*| 5s1zfSo؟/b\KqcQ)zP "\g-xK뻔>^q3PuBGc'ocl,TUX*yݘ/:~ n}E{Wo ',Ib3" oulcխ6ܮv\o!w "z7G%.܇[ᵽj8?_\ڇ0f/_'NCݢ}#.9v{θ'4:w!: z1~ƅ:::w6qUmW"s[D͢D;|E]3!<Ľ{LǹO)!~`\QRU*T_U/u'eD}_}%1" # D3/.W[A_O51XFJ(EChcQF+p-H\0J)"Mی79n k55 #(8wу>FToGU(@Sתkd|i~JRPxE(*yU1Lr(kdYbng1R]Zu URd22c7_ qUB3 7n WS@:D};PŒhZ\7c2FW(TE\gD J뻎5WJ~FRʕbE _\5\n~"9ԓC}9ԛSf#(Q$nd|wfnQcX5NWD[D* OvLwSy}S( ,٢uo.e}EU J1)g\FRU8bV"11#Fi7R1vLM+1q31|%XEcucfQ,n(7RuEG1F1F<}TÅaXk{c4-?u2+)F2/u̓7b6-jڦq6E+b]g;JjB&q%P#68(E!f&zQ,։(U#px%]stݎ]{#nf}E1J݀1E;E>C#({z q%~8uT}'qqsss?poүx><8xi3{YygY&>Fu5ƍiqi<'h0ãx!&w#}5Fp=F2⚰5a$1q1Q=L<< 1qx{:i}4uq;J)W"d|'kqUa(F 0J1WJ\%?%_W?G=Q"^bWԫxoxS۔}ƻ{zS{8a$ 1Fw' -&.Fx "M@Eokkf|^Z\Gّz@Lc}/z 7w37qߢŵW*\+x}*\*&QD"W+e$iQB?R/P Pe?>`?ca,em\k63df0X2ˉr6_(cc;\; rA0֞'L<Ge ݬ`EgL_#e0gw6sCvA.fl.^v̹Y #g㒿yr7]l<]tn.Q^<\[{wK/m67f3j6fhuɞYͬ1-Ɏٌ̀٬vfg:9,^Y+ TbR3R})'zAy :%%ull>恂P]٬ٝ dpe[$#aIOKb)AOl$6gbs%$ɋy'/1 *Q9 77as97s9[ps 6l :_PJ{~wv#e @Og?ݿٽvݙwwwG:;5wFE#{_wo=z_$~G{v!͙մv1e?w۴u,u/c&h22GڽL_eT#"|ok>5xÉ/F)Q*?u綔2ļJ`5)M 狩ț"ɢěaLxo nϼ͸ <{Dw'I#:.d#3H|*qps8R7ShG I])o0LɔB)ěJܩğF=ӨoNFE&f-Cj3;&B1QI3ufBH0҈GǪ1V7sqH7˗IFo$>{̫bܪjjj-HT5D,=H 5׍?1~:W,}۷ 1V`ZXb֫ ۨ6ۢRVmT SHQ01SoXXj/Wi G,e]cZ}YUXͽZX ب6L%11 #[l9j] pT>B!c(^F>7X|k߯r K)DUX,cb!m##W>#WB uORo/ݸ܇]{p7߅;)(v{'u젮Ի;;/;G;g;;;wp`. w3oKb؍=>ߏ?@;H<@}Pםb8mEʧ=vEw/vb_V^[l}×>{诱[Rg*v{F}wS֗A}Fʒ߰uQ#܏Eu'`TD8x'z2Ɵ z?9vTt+Z*Ke5?W+c("eLË̷o/q}@_^Rz_sK}gçGp?ʿ/u{>f3Ìa0=ܳO`E uPo ЎړBRh`%)5q!7RϷ-~KaqC G.S@&v>'6>E [_ӗ/F2UB/JD}@Y_*KQ|Fzz" 1b,'"EO)cKG'(%Vm2UV9j(Lk'k_7ѯ>(WE9Z=oNROfBݕg]qF6J3v"]UDVbLZ Vܘaad5*]ֶsW~+Vz]m5]slqj4wfev->缾jˈbWhVf5WdY5Vbɝ `vuYgC5L)Y_%ncVB+D;'X$ZuΉɏ]k^k9W@PM$ʥ%J%qzJZ'\u]Y5ֈ^W,gݐ(_g$f-~W{S;]u^_z_^1@ >T iC??x~ƻx70L~I2y=|0q"x4!j#E 5P@woԛxIdy}}6}!X/E:7ow{?${갾s|uDF/E#Ke/G;WW~a7F`0?#dc3iq8~*x'D~N)h9&\o55Hwd82Y*9A|#*Ke"#Fh!NqNR'tw<18e#Ca6{b/{/g3g9/mŪH@y`7SG_ MF0KNqę8+_:ꚇPYˌ^=UtG7teT]q.L\B1:QH|*ψ\8S!1JQBPO(hQHV#ad# \tiK%n*uRBۇ!܋!ܗ!ܣ!ܯTe*H㾧3ҙdd!yEy\oQ8F 1K_)R-hkCȤBT$E1m 05\:T> a᫑xT"~y"zҨ7 CU:#Kd+:Tʩ3U̇/׉tTo",nprܨnR7!b|M7ĸ>Jm)Y*[娡䟩lzeFӟq||zSD~5 T剙ęiĝTkRQmn Ya) Qŵ PIyc>wuTPWuVЇ ơ1`l+ ƻ{0{b~U2*K PXE{1K ˱WTڝJOXK̵_]G}wG^<cҦ"H6_ZejZA@!m a᫑xT5҉[Fߍj b댅"XԙU?#616q_touRbHĭb-jcrƝ5v8nw('V&mTQC1 ɿ'[|D}>S}ėbJLP&jiue!7=ʩv̦Mil<?K:䵙 >U2bYӉmLSS11YMZkLLT}B]zG̬i..T /~c q:QT8Yco jHLd5S4(?C\8,EF9m)M"PΘK B\hB_O+|SݚXXs#fSQ?vfu1}Ǫ_Dm 1Z"NSSd5댉'pߌx/fyߠc*bњ{[ ڈ*VPNWgLug;~K}Jqs~1}R崷ӏr3͢賯 j-Eh!i^s1UMQ$5Q~'Z7Z1a io u&/+1>eP;5~O;|1IK}S(gM&$5QrƯxgLuNiڊ њHIjFLmtbO7z˨Ѧ26vΠ;oqŘ匃5q0樹;O|~6*UEwES1u ~+g3qNih\FF+-oN9m-3R_*eXDLbZb&;c(JQ,Sx "v=>^c뱌8߷@Ua7> <8HܱuUA oYKe/r7aYGI?-*guv3A0*GgMNLo"?s͜skv9wqmĭXZ7ܜ ssu9AN^.\l܂\[c+ʫZm37wffAɓϑ%8273Vd~h6fjςt.7e3[6eXYGȣ\n+#n^l.+:r{0~*\ٌR̔J) MAf)*e1rHQQrHLQY;Cge3?n OtV'r?ѹd: c96{c36 0,'R{%qKϨ5Sqs)6bs(Ad3+9{~IӻDg/>v~b}wwoO۽'?w{޸/hO}q;߉kYjp߻{v;ߞi%[}jGKGC=-'?2]ddbw]bg9uvw`g9vW9юrV%<1r^r|AN_4i0Ӡz 'ԓ}!tD?Ogx5P !HExA:2I,ԕM9#6оy(m?#2y-drIAy!.^_*q"?qzDEyHGHUC ^qxMxC ޠukWcx9"#⥈LGx{QC1 G/ff{c1B~N6MRC"ҼOEc/|J}Qߪ>" JHwd82Y*9A|#*Ke+#F:mHB}.g4>RٌcŒmE/)~A?c?wLǏ)?$F*R(Pi^1iF*Q񳄊p"ioD-׹qN^1;OKs1tdlc䨡?fX]`j?4X= {w7X] ػ"TawGJCb61gb0S1X1c<ۍc}~Ž)Ęij:ʈ=31 嘍9yXu QM]Nߌi{c<Oz`w5Q32ԋoxA=W7"{+?3&*,Py*"[#s'V]. }ߌo[.+|/V1>OQOW 1ǬX?,SKKT5E\o,&bV0{| {o[k7ǯ_3 XUb>s1Qfa&3PF cd|0c>߫1ShdF=w pJҷij kcKu2+O[ǬcVcֈ! !|y5W미Xe011 ªcďTc>s1U9fa&3PF_ccV?!=NLeB-?ubKV,`-UZ$Zqߍ9sT4Ac4BC5'$'b@Tz1~1~1~1~1n3n3f3f'0V'0'2.'ĝJ)3PdL;&Ӟ)k B;橴}hM6kC6oK=m--'VA<̧jR i}nSUSL, ,F8嘥fjN?fhNel4BCu e|31SD=riC]|Ρ_0anq7׎_jKYbqELPUtt13Sh1J,pTű0$pv-~ÌW]חy.3>ݞq@##s#ﳎÎˎӎߎs7S0Y\p$LTxαC121ND w!q;SOgL B{Ю. .33mxw^}>uoӑ~vN洗~8Ď]>iiyɰʡlJ=dL3%YdHI4 ˩KΡ vr&9'|HmN'%ΆDg@^un 'jTTd9ڳɝ8(\+Qίsk'w]\p Ck\G?H;H|i:Y: `w]v ]`>v'>~m}x{׾7Kown̓=״/;YήznzSgA9cܧa=`g??>'f_;ˎݿ}:qJ/ؽv]u;EF{։>'QGnSmw_ٕvwcww+cv+`7g縶]`xڝ_w7zݲnɵ;K1;9v6zWvzׅ.k`75;OlIڽ=Jg4r >;";v\߱ v)d :;5w1im/=P>b}g`}# ~`{v}#ioyd} ܽ=`.W$wP;~v;{վ fsvjߵc-ӵ<뽹#w[g;5Z^ZC5wfFvښ쮹ji%nݥG;3{ec~m]Urϑ]`*؝ٙ2##~È曡PdbRCfpEᅡ^z/1Ї[Cw.ϻ>^C{%{!3y,K.Q."^6qs?zrzc&YLL[&27zw/ܱupZx6S[5<$ Ã÷S0(|nĀpC_ =Е/Sqkbb_D.ۅ;ӎδsxcb VcVbSfb q؋%ԹDڴUJZI+ý}vDb Ũf%X˸X%3XK,z^=уk&K蟱%%o%VXXj6b7-6R6Ml-ÞrR}U?H\cu Aԝ"6('_6c0\9'YP*({s1׼?GQq#mnbq c5FEhXqc kMqܬFu7c1>^MP֤)b /EmU۹޸UFLvM;ho1[07ԍ(o\O\c$u=n$plH ]* .cjyxcsΚ++|TK[$X_cXJkl&[~چ3n64n *XJ57 ~gUs"ng\񼗯[mN\iTy1棒덅mol&l:]8U@|5jXI{UX-ʉ[N[/Oo˙+(++Vw+.eԴ+՘Xʨ,e1Zr .ӫ[V߆v"n?m)Em֨ԶZwЗiqV݊pb ތMa3>kFlfl!Vvro#6،-llU۸خ6kɍy֪5XM9cWEZo{^_QXKVz̭(w&DP=q/|c7shڋ8bY좌q/q{{uM}wSm6Wl&llUۈil'q+uJ7a??A@lϷm&l:uc VSX%2~؇j(g"o OcK$$O }ݟ徽HW7 sz[,D, '>zC񌏩Ƿ2X%>e?e}kl&[U|<9c|2>bn2ڶ6;iA嘆⭈ěŒ|݀؄5[6o[wXF;|??volϷ&l:~kjʭR+x_]oSxX5o-֩d=%c%s+ߊ;{TY?`;㟫 KqOyyvr|YL;: ق؟9?_ _sy>>r櫘0b$~ := El![@[bn'5.#m ]֥-~݁hs'> w,:`1;]X"|~w1.AR繄]9Z>z잆_i]fN짟4$7Maup5q ֻ꤬ᄵ2]ɪ4UTIP{KOg촑^ꧭz-o' uDUAtTp]N:VN02H)tRIDJY8Q'A(i0HOR>=I*Tj`2ɜNiN.Hߢ%ZgnA֦363ZyZ̤$jv33$ӹ+Q3ItR& Y_1IiNj%`יWUϢU7}Fs -뒜ʸ~NŪ'bI& X|zNZ)WnIU=ժUG˴Y,KX3,ɯVQ36vmrNtNM$N*qz˪[ZU먗>#Υ$,Jr(ɟ¹S9IgMA$dK2%',IG(2>#]ɒJD)z/ $GIZD:!:3C: HrpWَqfig4NitBNf*S4FI`z-wM˕5کBeHӔ)JeyErSHJrQtO}]ubHfN8Nlc zbϐ|YR sYJ `$ba ơaMV7yD`l۪l!d ~cQ0X`Vg==k,6Q0: %)$AdzgVR|=SĚJPfIkھAR<9NbGB*ϳ؋=Er4rKU(bokށH'!k`gc`:> F]fPP2߉P ̆มl G[k4C4a&ۄ0x d .p|'`qCԻ;/Ҕ?eM {TBEp5yA;kV%eЇFI"&I k6h,k Km%gdw{e[CYAq;PBL0t.ڳ [GbVjg9gpY{rI}1N1n㗱geWe_%W {U$I)` VX;VgeIi,JzwAkL65MZS6M&kv<~=e3\VPq@;k8&`/D2MR&c﬩0M0{k][U4K$雁ӚΦT6MfD$$囀<IXG@33הfqY`2S4fZ2\\O}G!I2߭f9C\(BYIt|>{І5yfΦTI{& U!ED}e&)l*ƦJX(b%ג˳k=zw{C&FI: 2+",a s c]1'+疫CJ܄9Tni~`Ek![}yl.a n%ck3\VPJmEFnUn}ZRf+s\ܷy0BXqT"M~HE$= |69l/Y{&?=IfvYl6ek |X"-!#O0VYZ!R!ia-<YSfJe}PІ5y[ |sI2BVPRTJYLf+s\6g BR"H,&R^XycGQ5*CgAf%I@Ys2 {fabF| 0I~˓f8]_څywa ^V8k%ڭ@{'+0 xP`.PB%U& [MJU, CBZ 4J$1s?Kg[+Ji,Jo׮XVjmbb[ZGٯ)_`?#$/ %1e#Y#5e, [[kTf)nb3^Ok6zekH N+}IKboWţέ2czֱmMl3¶f8im'yB,Z y( E(2P:_T:Vb666F:Zք`!R"I1U !yt0g?샽Xv.҆uxg}Y򌯅eRJa]h$)%]C׶Wkkp#UyȑHY [eQAv}n vsYi=qӘӘΣ}@\>z}Za ]g&]4op N x^Wqqa^A?/y cacccx9^p^seC+8?Ux pN)xބ4ށw]$ @+U^8A01'yYḙyN7؛-4Yo"s^#82,c9uZoI8^'I_&+Mcϓ|I!:>ǘ#'87zك=kу{0N=G i0X`((> XMpUC#??{pq3 HIA2VhvC\[2t.7F0Øڇ? ?Y2VVԭ,~Uh _'J-Qaopݰ%T/LuBJ]PjR 5z5 E*W*(@+UjU+}TjWt't|E]OyRǓn'5:ׯI-N&56]W5~ImLbRu0UֽRԷ%,eIKWv%5VkS_{ԛ+"%ըZ꤫L_Aҕ#I(BYs=$5H}G9AGoR)5D㺮HE'R5p$TuxHC*A"W(tuBR*A">dTAW* R]U(H5AWʁTE^5봿J&P:w_{%^y\^sh7KSwII{gBotG߉ɴr|ݼ]쿭|=Kv^LH%9\:! RqO2[zveSjI%$ZhI͕)4YȒ KrScIX')p97Hw: ywPvdJ633s+0Wɫ맭:etURUI[tT'R4T'v&+MI4%RRJNJ*)idBQ')6 $(VAB[~>ɞN * ;I$\8ӉNjpjdoW&s j^4$?̂,p9 ҵT-I&)ZegArÌ$rY쳘E+ol*zX#wݘ8ߏ1_n}߀@˳-g[-sΎZp284Ksϝ] g_wKC 9byh9h9y w72fZ lwNyi3yLA-8ef86z{ 1?pb -9NZvΉwmbRZ|8b8@ˇ pZ^ rטŖ96yNh2Y3MoyʴFn&ǹxl\wٷo5c׊1uIy Di|,f?xכ)oifۯ\g|jj⦯ b>nsGx\G.9qlڗ ~n:G]7{̤tvp)֎7~>rY]6fx|akcrg ?9]ݔۙsCxx |T՝ϽHB1yPG^< RM DLL "J"ԂF X|ֵ+k˺I]>-Ee?ܙ;L~8|?s#ދ:I209P;E;t `τAlï ͇P0a D`pHbA $Q T/鑅lrA ƀ`\ .`<.W+D0 LW5` }"P J 020 \ 0`>X*@%X` X zp`X j P nkuM  4f~nNn m1rmE9q͗ȦM{/= jt}ڍN~(AS`I3uBS?/FVoF/7,4Ru-|V>;me ׇ2N_.7 b-XKsv*;q3cޚȗZB>~櫃Q&.cq&SLŧ菔d"e"f"Ee諕1_QDWĜ f͜yƢ[HՆu뒽9/gϪ,J㺕V' kcοQ5dU#™/W4u<%@U\RvOc;8d3ɘ.w9. [\׮i)KBUu6*K-A^+ BT Aj3F&Vpt=XUT%$8$$زnl6xL&VuB9lhROjnb-Jxm(zJJX;O:k_=Qۢ,6 yõei{og~N:4 j6JPC+&Uvv(E S]> £TM̼2k<ɌİcmFy+M.Z{Uu{zS^*[==SMD>׉ NzvA yp:ڤs[i]6>]H NJՉƅ%0.31/&W>q.:ukFiqSޔq2ןfWUQ!jisg.i)t9ȫݽj>Z.S6nJeCY[8{SgٷtSV*=m_3-hzҳ+SwF8qu{~<&JGTh]Kv' x1goh7户6G8G8xXGX.a y״̗|:nCVFz;|ø]oyc˜j &Ic+J0Q'eRqYҫ},qӓcO)1n4یV[mvҴ%JV)$f^JNJyz~o1 uFD$T:ѥ$v%E-zdjy|<l.jv$-Nm /iOXk6R| RW62n{Gjˊ)NO2շX4ILI ̖jzCx*4̽p&(zS.ӟBcXTP2]{5X=ZkkwI jt3n4h15x!ԃiQ=4paG,Q7hMaRQtzaH8;M65o1YDz\=rrzyJt &=iv1$n](\@PO覆Kϥ1t3gld-=}xz]ǽmYm2!nߋ[aIE6̦3}x㲠B*/n>fg^އ}tIJÛ}Y`>L E֓ r!x>Z[g3Y#*M69~^Z tKs7+Fsn>o,MҲqi! MYv{;siOݶ\mG{nh',v#ZKʼn(.DSzlDfu&XWyzn0< K򀰔Kw=wffBEb[m,6ךl fZ0roF/A o|L O|^6pzَ7w6z _ZˊUfhmn綤,KC[~^>!ZNe$}CGiaqB~Cm7nEP Oyhڛur:\RX7<Άv,9Qf ?sbIBoq?џqD,&&&yWsɂd ٤&wD{;/zI $̅ɢ?8l;! G7zAID]_w{'ۻ.nw{!_|f &e@lo^6?]W ,W4\T[7k_YA?|]~*P*}_+lhQ_~w+|\)oЕOJ+{=)lM>p?҃! 40Q O'.x<vgO^X+x^~ }r{?eW5:9]SG%xГo~ ٿ %c}!+=q | ?SI@Ϯ; b1XW58m p~KO3xaPB g(RZ_Dyt*Q>|3'Og/QķgUGe([!h!hn8b#Y͂X6BIdJ*KS2X(إLb㕩-q [ˑMBn߄rP 6FFv;p++E)7cl.Φa\Abh>p<z.e }*b 9ѐ7r!i(Ay!(7_X9f-a]1Lr|b<Ⱦh]^͖<{7lUQԏ0q~n5DN UC jalfqdrC:ِ^cN3T|)AH!LoD9f-N=ZV8؂p׫g+i2uHR竩5Q-Uc"u:UV'w<|#]2o$򏀜p 71kWz j=o8?6:UJ0#Ry{./!x=q[ZE4ѡDد7ifHSDQ DyכNS !(כ4RB*OBKo5fFAș ! y[A+YZ~Z匲W(^6h[,o&bwr%9\]%ؤ^76C+YɒdJ2$ "dLI9ؙ@څ` d,P nm 1KL-D1D@2]2+S0w9i=4/"y@Uǃqx-:wa:s@GrK=+h els㥠R~lm=yvB.R~ oעo;8諕`!!􍃠>Em( ρHbËBÕ0` Y R 938{7v"x AHODCCÐ߁~ԁ:PwprUNH* 7[$#ndqoll Z,k !wmT<9sNfz}3G%wP~0cU×F5":uA踉gB|)sjhqDY(wڊk5\g\-k(Q1\h܃x?m?mgll;htg;(geNݶS<<[czx/);C~oý}яC;o'1vowSnM{(uCP=qc`Mvv1w1wҶ~Ng>*kN;Ȫ'X,pV8V1؃RPՉ2qW%F 1+{eʙ v_{4\wxkv|UHϝ2I*qW#+`hlѮî6Jî0WW[]%>)®)bvqgZ!X'Y$&]|-&liyFVɭՂ])4\!$:*pWɬ^_$!s$XY2gF™ۙ*g=#;qkyws$'`sQ(cgv1;o7umwՙą&Ic"sxskwNp.;ф,gѱ3sSӂ^N^XQ֪u1׵:9:=ZAuD'tV]›EWuLx861QzquE`5?[Vr qLVtEr1l9ۆ/Ak$|$_>/CW2UZluz܀&qXfbB[} e>mщ"nMq#}b Qn7mŵq UWr8ƥĽ2.K(bp1u:\C{t@Gt"]kt廚5VmשEotF'dtDG;EVG(?RApתkpJN=8R\Nˉ9HsZp/|hW1a٦3vv`'v9v=\c<" ?,#ԑQ;ZVmT[CtN.JLj;Ю=j7v1vb6/c5l^ʹrKYRх{m'TDW]mZHuqÉE;q8BG^K{)F]\ry/ g!34^NGZxqx,FG!#1"0<C113П{OG_b!no׃rO;)'|'NF/՛{G_3?qa0`FrM*A13)/sæzZYGkD_>7z:ѓKVǩ lj? ) Egclvl:Gz4abl+Cioޗ 1}+_7*[<ivn1)^SN^^v6u?e݇{q w5[,R=!A| L??qN'^?q;v]^܇cE]MݔnM U pqv&g)FQ7ݸGO6"7BJlg 3Ӹ6{pKݩzvq&2m%uh8W}s/ٽ~Gy}zR^S(;5r_D/OzOPwrPq:nmxg|?c?m{/=>h^hWeأUOڱj&XtrV'V&]ĮF i#6u^wFu]cl}qE6QzPsV#*d?pWѫ1D+UFFr{獭/od]a"vt ^k 道CW"zh]5Dv9B+;z] k{,Ez츃0gv; {0;L<xDIhELHQ2oFJyO0JP}"ݍ:#$W<EJNHNvt:ǩzK>H~NY<;_.T> PP1JQ&W跾Ro{^qTu:YT"*ʼn?'(B1JPʵF8zݩq:U@Ou(޾+m%E(D\ ,ы~7N=Uu:\ˢ F6QC9'o @b$Ysecci(1bFc ׍E ^q3I9I3)?ˤ3O_ JLts721X1F!U`$F`8a(p1w(e(E QB`Q"R('o8RFrX%6GE,15F `jCE0C\!_ߜ}9ܟK\b?r(3:0>BJ;%(EQJTZy5}g8x;)gN~|̡fc0F|l커~١%ܳkEҽXɸ]| VSjX#&E#ۑrj1rT#+zpkujZVLƷlwF.ӷ?+h5G[Bَs".ߪ+CUkԵı8UW WɽF_EYՔ-UԵWyr%k\b^[xoE/~NjOc }>e#{LdH'2ƞqKRߕ Wkp-mu"e`w@%Pލ7 kLa+s?Zu M+[w.eRDLxn=3VKKKKG22ָ~6ύߊ ]5zrqq|%(mF Q|!ldqI<5q5RWߪ+θ\d#9Eɾֆy﷡%x6H^/Y<óOŸyQM #2GRH0B P}E%26ʩG9u=s4qO((eQJbs͘[{07onGx_ύjkZnN&ηWⅽRD޷!_S?3ןSa<|¸)>c|Ƹ3c/6|/7\H Ȥ}YFE> (QbURcQ'zw|t|%ʩsbJyFJyy|4rU?7U+|I_o"B _6e9UyM~ǫ/2ҽ^1a8\TGEyG;Z6tdym#9*y #*Ge;đGDd9ŏՏq}[Ea6%LF3W99U8.Q*#: UPdxX>7R(kR'#ύRGYQq8>FS_C45:;Ǣ'Y,E0p|{[̦a6umpUϸwSqa1R,SQX5ZA֪uaL:mS۱C.{ݾ0q}k:30>z"8*Ϛz0١wwww? > < f|o;/%? _s7[b9'zao7`wK+@ykk8zF`7V_?_ 7/3>6o郍/zh_uX#پx{q.bVb5֠k2QzSv=u@]6P mu@]7P }m@[iS=m[O;׉o-qCO g!.Da)`ѷGa~ hs(Kmtmf Hv_N$ڳYz6;kjF^^x^z:&/ȿɷv8uA.?wܹDysrsȹqܸؼ8 /.䟪 fl9%f873.ȈK6a&g3ߚ՝dE?'rynsm{2&g/w-^ZlZAs5oe%%^nZs$fKլ\bs<4ӷ?Dڄ3l SfiYiP#bI/툨ܳ| )KGb6G|08+::6&fq[LVdV%_R> slNٜ\)7O*:G*Q~T\(XStSdlRlR=GzC(6rr3&7iA$kf,JT:Fj*%.e3nR\fR2l B> l0EnfQ=2Fz'!7!9H6(1rsV5!hAT.Q?ds %7_8XPr'ܜ!7_ 5rsd< `9?AY,rt{T顶!7f6q3v:9Y9n6Nc8ɽ8ɭšߐ󿕅f$ʺq3ns2mlM2kܬ56f,x4AFfe,7=o&qԛ$j$k?1f'2ee$5?W8&:&^.͡34'o&Ʋflƌ-)dȸ1AVLl6LL7f4%%:d 2VbT%sZl>K;'%UYD磴K"%ubn9,<6p6BgL5 \ΊR|ԙR5j3A 'aBdN)oXGhwhӸgZ{xS)9yjc1ZKĩKEGψeToG /:-be111sluZ׋~3zL&Lªp]h^N^mP&lv/RDPp WP Q8 v걞aB{p q112>uiI X2wZѝS_=6؄8pq!1."E|1C00#_~^c4|13|Pj֢{5XM<_*6R0#x "12|b0MbD]1p&ΠF~7z xV<+c3}klRԫjk׋v}~R`wW|=ԩk#N oU׉zuQ݄-ֈS·9n'Cwsn>܏mj;vS;.@nqz7qGVi1zm .sܫQw׺^>/FF/iY79nT7EN[FGM0<3|L}xgOSizZϋS ==ŋ4G/[Q(>N c=MS'jI׋~3zL&L߁xw-Ǜp1$0#o(WN}|n tǠFQq#EIu>-4=C09#4SçSBC3'8 / TsqF XCl+t.ۅPԧsqA5P smJQBbP*@>Svu0rO.ʥ9%~_78'$;ݹ8X~*B1JR~q2'qq"'W%~Y@}B;xq7Q^7rUJTj`9ݍ~F,|𬌟g6̤- 56N0OdL?&bBO10B̩bq,bR1DLac Shd5 i98QjTqQI!1Y(*L3 `:[@:}8lf8jWqlicG./fz0'?> /Cqm؎ةvݼo{As_a/QQTqx(Q(C9?@x>{>{XP8GQl~v.a3|Ϸ[Ķ<_xzJU 7EQGݎ]jځKT' а+we1#z/sbk}3i<ϞTqMc-oLUS011 OZs`[O:g8sx/0^T/e Q^B^dl/zV=#*o)bēkURU?F8 ē=gD u= xQ^5^@2F*UjL$LL451#Mơj5;ix{/yzV=2>ISSSbZ)zN=^:EKij^doMRیwE1.?QOgώ9e|ժFL1ƩUӈOb|LԇQAQ5gmIy1EЛi33wf8a! k1-cKؗ/W_2`<}k %v}I_郯ox" a$}{WFhG9*hW%hg5j0g5Q߄ H=&ɘB}ɘF|-t7>Rgw?0$GժJ|OT^;hEPa~nx*Ľ*fT)h<^Nha>o8V8A2>n$fX، 9Y Es(l͙ADO'w^Q63<Ԝ4X2lz'+FBpdDg45 7 :ffLxYM H51/[g$T_9ѹy s?`l'`$껤ql&pO~c'ɟvNO퉼=NcO;u?-ݞSt=1IyrǞq{&nƒ3pܻsW,۞Y3س馞I'n i}ȩ=ٞ0=I>9>Mև_ ,;9osxcw\_}y=˵g'Ɯ&sfkjsڲ8 fsY{&kb9=ks]US딄'qš|VIjLvJ,==mxϝg{\5LSOV퉪=I'48)rNEcODkSJ38մ=Ltro {vi*dpAgM=o+ggk=Pj%i=~ϞŞS<.8=kD.qWH<=O3;ݒ2BMй's\ÓDpO[S=ikxʖ_-q4Ӹltms枢,ԬYsO˒oQOiQ]q칚=K ̞v%RP쉘= ==^ O8'\=rONp:RԩTuiSp,Ş(蓣#{ZŞ!{2{"dO:S{bcKOsDx EXD|1F,|2_>!1Z"Jy"P曉ij*S(1YVccU#{Sy4L L¹86s0Wd|, E.}`Ō#_*Kb.Q{rׅ \)懫<57\M|q,T300SϘI4& ƖQx3P{QbU$9O?8[Dw/B5.m6a#6^Za Vs{Wg(j)j)s9u_N,\jyn|9絻UНsmw2dJD*C~,{Uۈ &}xxϋR9zrg3ƍy?%'qO`?>OS~CQE(PGh(mS/UP7jzBPOr(S(?O\ONȠ,~<1WdR+vax|Mx uҽ7"Ҽ7#Ɗ{zGwh9ފf,v_GL&mJ vogvog ټ;wg3twf~;`̺vYaOYΪcg̤t3Ysslٝ%31Ό87ukg`[3ÍYll٫&?km8cMBԩqvho~MygN=sع;ǴJw.iVE8g3L;\(&s"צԦSyd1+ڹuʧ~5x Ӟ>ړs1WYetƪ1ObtbxnD*ADNϩE{_DIkV:+⛈L |-rU;WLkv:[_CFy-si|mƶXdz&ҽ8GhVmmUt\)'v\jo#іeEG;RG#k&šEQӷ?j!/9~~8^TEtT\^WF*eDu22(sG4"i?kR<"_Sz^v!E^߈bUq:m 4T0PE=Aj0 oc.ň212Q(v9 ň|6@ `\3_~">^oij,ƨj!){q:N}ga!|(b$}32R*Ro7JQc&FЖq#s1":c@5g 1N'o'ƪөO CWg,Tx>qcn(<jȥO| cc%P(RŪDLb*eTޗ{1\ m(0p:tՏx}E%cכr{SuDFDsˉS*bVߨJʭU^P*j(@>59,Qe3CKtNym6 PTDTPg[e<##[.+h/;v%(F*DWy"rˡL#WI:>1qdTb2Ț"":9jc1f҆"SLsLSbL1rT*PϩyE;`cm!cW"GLzE*jK q#.%oqe\Ay*RQ]p!.،M5P}EkEWGYuYG;xZ_:g籖gg^d/O4K4Oi'zQ4..˸rE\ڝ&P$E*A)ʨQN Lq"*U..ey_XdȤ ߅6M"6×CFʓ:4=5^j|X#kRg}&LL\-RjF`2+W*/ os̪J.X]ڤ׎=]5^]5\5\%7OnlgWmvfWe1 V^ѫ`U笤=޲rW=}q_=ޓZD<-R3-G9F{ϊ1jJS@&f!91rGYyG=S>˧Խ-L{jrYm:~bO$Up5: 1Jw<J=|-~*m|z*2kۗ.^Ho8t~z[y-ʴތ?^o rTxWʴyq05HkFn' Ydz鳿P_̎Pv\jo#ad_Ea  ?_\dWFEm[Ux?֏?t@Z|/w[UŨtT\^KF*eD22(sQA=(GQ~+ޕ6']TxRq }dt.LvBGmКkUQ88G8SyXA6g!3Atњ~oh耎u]q Ǣ8G9Qq{7H%U (EhM6h+*=:3[]cu%T*FrU+XF&3)U;VzȦ]Fʥl#Os?gLg+)~sDy'8q:Yuw"r\@-"bKQXVa5b=mfb\H-y ><;Z{GGNq˼0?;?n{/+$z]&.`{/ًxA=O9b62w66zwsω:b^]/F>Wkx[=jܱ{-&cxԫx%b)u DrgԊrL-%/0"b^V0_UuTo߉E@і:?VPuXuXzlFɱqz;f{IjڴF7x:W+OzQc}zdžr&^S7zSE9v/_߸c鯔c}1>X&>QKݍjZVô9e_<_?ŷPKhQ-[],Z9j"ƅ j36a#ofl&A{ax[^k_M6&ۈ 걞:E`5VXj~ZFj qŔ`|+RF wZ-m,bbl& p!"lQ.3.a=f\*+.#el@@Bq6\F{Kiq m6.u珗X:: } }l|KVFU Z Vj=Y֢k+T-/Wˈc,%hT߶4Qc15cY'vhq,;KۏXY{lfq脎Xhvh6|5Q8{#p8'aqeFwwoog_GG2&G50vۢڣ:?.\U,-Ki2,G-]XjMuXuXzclim&k:}z[ eQJ>Z+Ps8Rb:~=ک ?oQRB7s"m;`W_:~WM?ԫXD,sc圇EXzUpůM#Nawv)'=ӈՋbbbV˹nb IO^O=[- 1ĩ99Q8{fc.y7qqRF_K})G}Q~Ա~Է/c/}ߗK;О>޼}hi96L~~H>cm $1eSiIӳ❯:SqSҝ3Ğ9#It6ߕǞS{bO@rN;ܓ{aO2O-rSFO&1w.ќ(O2We4vsvYDOd9g s/s{~{`19'@p 7g?շvw>v7>Fx?swՃ=uwyGޔSۆ w;8;vǼny;=rwoDgdxc×:QxkO÷N~ @rh3kz6ߟϫ_2P>'xy'~!S^Nԣl^~5^2.Z(˻dmo5JoDFV"{zX=^tql'YQ>jҗH\oXFϊxUתZ<&j_ u=VpGT09(B1JPʸ\<@E%VGWTPoe%E(D\p,b G#o񡏹Ob ƊEz6Tw۴71xh^QVORFއX4;kϸ@8]}w}g QMww #;#λ}gABB,_<{D7y?xy~7O W}⼪_ol_+6xu8F_}ƾ޻-x1k)q+ NN :{@E ܻ >F߅ov* e+B2%(BcÅ ! g"i1|R71XC9(c 3)?3$P_M,~n Rgaj$RQ14d krE,rʮ~hoBP?"r1TTĨtTjUqQCjGUbX#й阆ɘĵE~hqƋ9qe-q&ґw*RߛOMz8wRP*nt̐{(JRayuIRJ{Ga4"E2UuV9ȥ^FuMt5CT"r_6m1))r)UϳZϳZϳr_X˳ VJP\KFXFeQi5oo;z /gׅF|l/7RΙJ|nd;rb63<cM^=#G+1n>b }Șq!c7{aBj΢!')}JR3u:~gROiç?Ǵ#G<i<?oh|jP/1rwܝ.QrO !7d={<݌it&x5v]/v;-.KQ=ؽb\[`eLjԦ*bwVnEqwO;%EH]ݐwB]oyw8ܝ]h$D:f#p<~oDm$Hu3 a/b-g۱hN{A<Ȏհ;v^؝ c;q`wNBk`w ;vVgݕ}JdoWvUyVJ]EgjV̙8Y Gm77H&Ț׮s֮ebCuedrp5" Ү*Ѯ0X +`4Kޮ-Xk1wrT*SfjF Dɷu+"w54r9-ʼKpsCCf*t niG>=?xɯ|K.-=M/xuC=|ج;?_uw^n]n37;k֝GqgfyzWքxwa<E>EmrZ ex {CY- ex2Z<B-`7vZv܏p/]N܁qފun$qmŵ}u-<.W r7q nUvb߁;q=avrn$LVS"n΁i61=V-bY7ø^LUSx$LDdbgh}]jwhKc-QFoVa%V˱ Kkc |ϧ9g;:9g8ؚXO{З 迅XD-c }˰XXXuuX}:b'z'~=S^=㢞wX$cv]M|hrUVjM}kIv32Q:q7Rw5ZM;;8$؉A*J /T\O#!VqU)E.Ψ{zG#$$Y9 6hGb!{޹ny#H9vx{.!ǚV?շ?p/Śxq|YCsvts&LVV7Ľ5j-XzAm&{[[Hmގ|}Dz 1U"%H E id0}FGZB]^գvom/ͺ"A"J9쵕zαֱ]H gȪjPRw~7^S+O<q ̣܊<϶,b^UPvc{,r><ǽ~A9x ?_%QèSo/Og=0[ p]%-:묱f PFٟ(*S5jQcV#j٨2"Qw}EЏ:~3rG@g/5TUe$1d$xmQqTۨ <ơ6)'k8͜O3GN1v{'p\ zpGpe?皗"LԽRQk18C!d33y3O۟>ũ~qbcǘGĘWŘk9}$d})aYAA(2*Ǩ2'Ƽ|/fuGQ157TUr|6ȈŗERcGu/Ϲx4c9D=g{|a[.[ ,_3sY]! E])$%]}ޥ]qqYW̻WJLUq >k#Χw=us7tQPEVBUޚ=n]\&*5W \uRSbj_$2(\" (r_IQQQc x JM3>}ƵY[IQ@?! ץU$}q+r7.~R2Ȳg˦bod*- U1[SDݛjf:fq1IUGl5KL5CMga*~}D^츉} dnd_ir$M"77Ť[xYST45]L5KVsm*2@RZ|iR &` 5]MSS5I5RmikcyٜcU q[CQğkFQsy?7ql5}L54LL-\oL2nGyԘ8];w9>+vCy6;j Hx&b6v0VĘQFQm\sw1O9gwsY~/"뺗|w?X,£\%5c2uLŌ3bbζהQQU:j.R܍P=x~^Qg5lTك ( s/z>6ԩ*ucuƫ3!,TGX}ƣYWIQ@?! ץU$} CX~7~_udC^lWp*O|a=_w}ݱ M[yu| Ƴ8$ɘ;&R!imdTVT^ ʺ#ǚ1KyO5UI*bjgXVa{kZ__ؠ6Msmf@YjJe<ixR$"]W'v26-gr+漅`-a=_76`Dvx[V汕9z6漍vQE(v2o=uX5j5VaZb'kzXإbx2Z.N0Fd$sI2sK1smnV܍-">X9ȫ~3s+m]_Uol=`f~flRVjJr_S3d 4|-eKpN,HUD+3Qb.F38O}X}Dy5$>n]-RϕV1;r*eQ~X~^>޽Dn5TPD?! H! 8b1v=6vuv۰[nn~{ϧGq33x<3ml&-CּhfP,ȍ٬͈:eXy/ \n+7l~<9hɭpjkĖԲ)#}6Y.7)dl^flNg&,Ȳ ,)DչtM={N83f:lnflN9璢?ioI1M.ԒV) &ZSIgr璢[~QSII$.N"URk&l&c7(O$KyH#=#7 rF3FW}VVwN(HE6IdSDArM @3#$l'HddO* ;6c96plM؄MsƦiMشLsB&HtMIbq+6UܔP5Ql ŦOl]&N&)h͙\͒ Iq! 2 6mDF8Qm԰) 7~$3F=5Q1a65s3n62i]b!v:_M;ܰγzܳܯM՚]1כ|/ƌy\䎿^y~9Qg{gW\G{f{ǼeE?Gym~e׋:ԚF;~7^3e?F;ɷI>j1r?<'vO<=[pŽיsrn}/ wO0˙kuu;Q(1,DKcp89`(p\H,Ȗ1C0M1a}A@QX4 @XWp- T+P jZP 4;XAXn7p+Xu#S!YT)5FKAӺ{O{'̒R>q>a(ԏXLbOY[ gϝ-#?߆AmtV?f1,ӝn*AYeWG=> ~O`#]ڟ1ҹ6ؙǛwZ69"|oN%p͂]-5Թݴse{_ΖHvksacxv!]H=vr{-ѼeR91>e{Ir^Rp'&D4١1]+Z\f74k5沪&;WM"DSqFT3MJNJiECŒjb R΀,D֌t>iS#v-Q:[5|ZGsG/PbIbےFPZh_RgMwsl+?Ou]Jv*r8+j2^3qkunLq%)}ݾw׭>~tGuȓ{L].qkgv́#{6uWZ?qX7F:ޕ^i'$F#=C ]z(::&K zY6\zkhN/blkwX`q"-NsD]rW46rKJ0%bGĜ퍘ACֱ]WX--1/1-}ut"U..U]h7Ʊg{#kP,: =Xs>zrY]ˠsPU(ǹ>JBFܫ];F26*>Ak]9c /x'|%'LYO|0 o07Z6>6>2zi56{U_3ȹU PDl7#X1R,R,HIZ9_ aŒ|X_dYdi#_&'!3U8c S&m++m? /W8VM0ow{@??|7@?ӣߓ@! >xS_@sH@@4ݍ^*{.1wmLj}5oTW;<0)aVJy}[fRTEE`O[[;R:;&.(rce FA Ek샖`jw,jn"WRYΆ(l2]fyx6J)^p|.G(WUS y6ȭл \n:2䛎(7 cl6aNQ4z6 w1,'s<8|ȟDr# B_bΤyKuz ?O8ܱ\.?͗׌kyS4Q}4I}dQ_7o6STT:L kJ_PTFWa!{@6{ r'I{ zRo6RB0 -8>?Q>r :zF=Rg^8~--@2+E)[}7_{0w{|*U`F*ǣib҈oϷ*MeJI >spRBQDyOZ=aJR{'LDyOo)a\G-ۙyfP2Uى:YMq› QJrqZ,\{AV%4if nPr.ޢ nu2*I͹MZr5:Npgֲz2%t`u.1 )&%9XC9wqANp%Xnf nJ] 7s 2?ZQ oBf  )X'D80zq\!06cPc:>X㰎'ⱖZ>$b=O@*Hf0 C$Y(-ɁoQ)qX2, <'XψoJsmB1h/e }SDZ_yB$NS)g$:\V4ZXI\E3*MVh˱L[qxxԫZ>:~FM\f1̸o\l&fLxc)Cb#z#MľO/pz:'כxol-BFmԱQvN[Ӧm;m5n|ll|m[V-m _6D7g#6W?"֬'tt ͘=[9Jěس{F2&L35{~D]{o,fQ9FECF!3ȳJk:f#Ҍ*3 3aTYDUDy?ys {>a%buȼ=O}9A󁚟+-T%D?Cx>dVʚ tcC9@Mk!#vsOޓd?z/ zzK<Sw֛pܵ)=(WzNIxJ od^qmI<]{Okp~/gֳwK;!ugV5hMoDi{? 9\!4ct)HDt2. !3X%.RdAHB2R^׋2tt=I!jYZd"NS҉?Ҩ7]d $:c iXg`L;uŴqǴCbZ9C1,3\l`$F!c0+ F E;ESD}E[DEm/bl+ Q:#qNuf\<2V(ra! & $@@]w-i" HA*4S@|#uF [HmJM\W }LFxޏC{-rD.㫌mǕlmk8N*%x$Hk1(e'R)(P*0kf6S4mh̰$QIlc}sfXx-#yd"@܎b& e:}ɘZ9Lt5&SJ=SyVʨ\S:CH<$L֦`6MtMJAUfRLI3&Ig23W,imʘ*SɘM*8N)zJzKkp=Jy3t'%f3Sɘ$TI)E 10E(D1yHb #? Ck;2` ὡ0@6(b)h䣀 Q1_2Uk2R t84cdQwgkekô!tc bD/KoW Cb-#C,%׊y~G Z~W&5 pbu1$b E Ec&2Ř1c1rE($N!1 SW~87y("H$~P_fS6Ȧ=9+6*#iH>>()c> P"Wc`1} c/c2>*E9+}T+9֨b(ъIR_?B+ѢBOɥW)JXҾ\'2Z!@+6>2C30Y@[H܅ԳQ"_D;ӞŴk1m\5.|,8O 9 Bm>_yߝ{fW?;#;z3{cf6Lƛ3+PҭID@GYGGths<#Þ[bN|"Q%v63!33i\i]3Emg><žCLs3G:' uv_l)%L  ̬3ؠԐ?~ta+GڭWxf={O ~rI$vt+ឳk%ytQl] ɼrD^'O);uDm?E{Osg.9?U}ί<_>[>W{U5'i) :R@7n:OWu_i@Wϴn_hZ:zF&b7do V:[:Y:s nnЮp-џ4F?賒)Ǿd#%kqZ{j(_kqhEZ%s]_Z\p#enn'׭ԣFݮv_X=%)ĉ+nrhKݮqu\]_j35"%N$D3眓 \k9Wz܀97i7Tʘ(i\Qu2LtѲ/]݈tun7F z^E?z|s ?ծ"O_)?͕ G(ѮlܩW}~5v{Ήqwj8]Q_k4Ki"$q/^Σr>Ν+Q2)YZWuӺS҃z]Zыz=ez\j<:#CDҺGFlW=ѝ]wF;] EWdmLӘ:Swcƪ1֘k8R9>EK&D\W}·rvVwrH'r;qo'u;uNݪ>H "CަH?\S-R[5ڠ-~,#@b%3IԑDۓ/z?'1Ig(UF;qjC[# ZZ5#AnJ߳G:4Ȥ|dUZGi"vk ڊn+~%t2A;:y_IC*R($GI"-㮴AkZ$jN9R_AXk+rO#VqӈN]ہvt N5LtZBWtCwZ/>CG~я۟3~~g?Ƶʢ\WqUzP'׊E/ԋ |YD@MtK4ڐF[Ri:#,b)]EqLtMt}h'^ELW&@gtBGt@b{1JOV8+эqWjYey֙r"~eY< uOmų(b Zx~Wz^#KX$$K^KQ{IdmJ@62$"ْbIҴt%]KRbJ@bH ϔ2T$3NJإ!]5џ~The/m墯X!XzkOZtG7>Nb-<=G8f!Pfw~|]9|}|"E|(9W5e^b<<DZD[g˰+(8OxӢz'z7h}O~I5@ $kπR,cx 9VYQՏZ-~1\X.]EL+]'1q,іj8NY.EˤoNHG+H9W2ΕT$Rq<=*hk唅qҿ4-~ZQ }/ӟ)x'Nc8?8upnme^Nb(;6F}[w+o[hl_[>o[-ѭ\[v1Ɯz;̹w0߉}iҊmhcK^~AaQ;UNhشC,cq]<'p\<ОJ8*C8HLcv}Pb^줾J߉Z" 耎Z'uFxs"DQvs=xavq#9* d"qS:O`Nd-r}\]u-d\$ƻBVz"ߕ{mv~w>wʟO7|W#>ç2?0111R퇌퇌퇜9r |ĵG\:cO) /G| _8^O_ RF)O|Jgd ]h%>oSBQJZth]şEօ>(t~iό+EK&D\׌WHD2QR'vIJMa?=DڏxO,W$$$K)T-MKGOPҵ4-UK cbK~H="qRR{{jlϗsw_RNrW-͢&ˍ!nnZWdi]FZiC]O{$^+]k݂aFcC*|91?A*W+srm6O)h FffLBcģ QB=%[JJiO)m,e9S 7\s0WGg iB* h'?`>}32G& 5;[N?sN`-6`#F٤m[8?<)ZF_wW2(&QL}]BJhO rR)XJqU&VJęM$R!֊5ĵχ\g-)ƾ3ϵN{R6K_mc,E(9|m41<^:Ϋk_:>CWsFH98lg팲$ 5^fgo2d{LopƇdsL.{kgn^ІΚlh6ĆfaM˸&ZjuԪYNV431)6C5Q5,hQ!4i&i5MN2C&/ܥs]*M24G&tsя1g+LepPf'C3e%Sf#M>c=/?o̊g49F_kAW9R6d!ch1|eLch.fk &9oO̊G.;I034Va .g(DE4D;sh5]{\atQe k%rn1r/|j]^6+3L2^Uټ#pPFv&_gX9q9[n97;ߖg35;fjvN4/V]-4w.ofrf27OVQM2k&fi&V5}d쌙ɖٙ2/C13?^IT윘ɇ-rajh{{6sb-䞷r EKgee6732;Hs1+K30B}gkkԣ]J}^+aK ]j,c%16nE^ߍ#{nھv"ދNف6le`36aXՔQ's,!3 UԿv=+fVrZɵkj%JJɕ|V1Aߋgy;-u#pb^z߯h󯜍؄؂۱Cq-;:vSC{hNjg'?wN!~OvbR^٢m&b+mRNYC,eݩ]YhFZc4=iS<(VsN5e-JY'Ѯfѵ9&l-Vl]<̘(1Fj0v;:5n^G)1^]i!Gw Ǐ|x}wC| Z8=ym7˸Dp8w>wR>5@C8PdSmD/2q5]J3z~+^{4Z#x>*z1x[{K{^ eѐFh56 /8_ F㇣huj" ;_E}Kaw,DkAJҐ PͱJ}'р12fJ#4F܃{ƶ.W9?W'k?Pp~?xLk=P7QoרGVÖhя0 %._~F?kEӀk\/qnnM-s6p/ @[q~q#nVj=&碱R_\pw/ۮ '\g{rFܤݬ±ʭm//qwFO>&?]7M\7j7zQ_\gV4ѐ\+Uquոshܠ݈p3Z6q/c/q,ji[j(<_w_rORnn-icKI7z\qR&s9v<{7MYl'˗CBu,4(bX+J2_(*DG1Yt:3W2LuL$V/1כ>߇6}}ik_-Tٓzҟ|ޓ\(=at'Me%Ss@$J-%Z6AѢgտiI?gr^,Ֆa9Vpn''E穀ӢXI]Z&hYniՃ8=œ'X=E߀eIo sl~fs`w֟101&F_X}GC,ce6Ggbm̲2VtO,K-S+Dg,ӖbH:Ύh3g\MWs-mvycw ;}CE{2eӜ"9(:~6w/{/-sJ1[zoYe+#LTj0[i} ErKט2QMž阁U*9:96̡=sh;ީ,g%UҟJ>J̤ku(tm6S,I7*PNݞYZ%fb^3_R}9[|;[gݷյݷVrٌM|Q,_ Xgk<óxRK'.^{1|~.=]wu.`6f30ϧa*PNIě SRFe_J;JiO *J1-Ŵ~ӟbULJg -ߥ(:ԷzQ6ڱ]L4Lglg0[gs1_Eb+㲕"s9\1k8f-Sc+y6qfnԵzІ-e fڵmM&$)(rZxY|S:w߫|N}:uy)S\ϧOr}:??8ߏbԻюego%n‹؉Nm؊-ٌMH@JUxY<kcnz`ldl6a3cE8Nي-S6G$SVy@xY>W*>(鏲)[M<ɘ<ظvEvc^^Sj}X(Qzs0Mr_]Uwػ]ALGGnaI߯r^. 9{Wrv}WsяY˳Kr+eسlE_^ytϝɗyȣD%>#eZps5s5\)+ C2vlGjGnIiaϰ9arX)iՁqP:jcEg-C.Z֕J7QtNNO\mӋݮlp  (1JsuҺp_P2!%]Q$\b%2zPl?KWa6S0 B+LJQJbSG1So1m@'p&pn9OŜba1約\ʹ/z()G׍2&c a:f2? )?8s7s?zR\06DR&:#vdE{IF\Ewם1rMӦ1CIyOWhԝO#MKR($Wi_Mdm+ ʺR蛒IJYO EDD, / \[=!OjO Jr2 ɴ!T jiZxk߶o5eT?Va5ך+ӿFtѲb-]]L:)눻Gw'C9A'^c?8v/="1vo'wPAvwmgl3;NNۋ\OvxSO3.ĉ,"^Ne\bd<,UKW#TN]EN%!9byEW[l&m)i˵b$(SQ+܀UN6Z@_&pkOyדcbCjf,NFسQ8"e8Nu6%ZV&˵ ĺIZ)u[jZTіOdu@HuhCIɴ7'sʍZ#\Z;XZkQ&ZRKX5^IgҦ٢vm6b~"m1Gg{,,̷ 1*6 ZƓ6ԕ笨>8?5\J%GGӔVĊ3e5Z zmQ,eB|O'^K$" Yԡ$!QK-qbC@AbgeE{Ĉcx-AKD6sbfRN9^ "v %2Ou;j mhk)D@N]h%{&2c9T9Wq4^) =cWqPa:L# )๛k0a#\SQc17qIb_i3xgq+)qKļDK\2^`UiHG#Q:ѦȠm+8j9qxAQ%SI\(g*}Whv㔓"ԙ%jI5;[wE{{j_Z>A Og{M{"AKԒ@9^ՃuםVw*C;N1?nQcc,Ơ_|#9A]éwD!1ԟ]wdusE9%yM,%(D`,xL@ c"kgP,WuFGu<6Ÿ+<)곩QǼq;}-|Eb2JYSyrfkP M#2:\U0nRLcn3-hg6s1s,Ģ c"( -b <~ͲTvic"/ߘlulmEFN|,}eH[11ڒ/(jE1b &beITڦLLL߅ b{b6[%JDe3JA_*Tp11RLm6O/&(FT+ъe|jw EX τxViZ[ϋ|Y3bUB22FKi5p36w52SlloEۊmvmS/ߘMLeNLdmOdv`;c rmFۀ[5מn:\gkre2kub BhSF_ɣ.& e3h[~aȥݹ?~'e4G.se&c bcx1tU2'Sfb6f[C=؎mVlflF1̦_,Tb&Wf`_]/ ncȘʉY!6ڪmv E]sҷcc6ǠH+|)ڎmU.fQ@]xo _yy(GH/2dwLg*׀r4^) 9c;#9BVQ!<b1~h!s~a|18¸185y2i3*))wAӧ"X0!bqTe8}9NQ9)&ѾIuZ{E;ʱYQ!Ήr^Oe,RxJ>u^iAyYӾ|:! qb(Lj6tCÇ\-iZޡv ZV2_VFPP.%J-L -5_~c|=3sOqX_҇/nzٗ/9_rN~ɹ%}kÕ\"29Vŧߧ#?e> 19/K#e"W_S ~CЎoh7 ߵoh7oh׌לׯ9_ѧI?M~߉/",/dZ ]1LJ +{R]$|$lI91<.bVJ;+:71R+7)/eIn&3eEL6d?L#4a2&HPND_ MٌQzI;3L.#\/L+LnED?$ &`UCDDẃ 꿽_NaV#x8k{MkmZ{҃5t{Ukg­ɝ7o]s կ҅_3+srfE[ UHn Z3nYklfM[G-μu?uW"j3hE#俏QihJoɼV1]kciPhJW=.GzĬ/cu2M)gq8~-垥JzQ TylelzzUYl+|/.+}o/_O~_|#m=O9Jciټߏϻr\GO\ 哉Ld?z:R_WGٴcFTjz hL̓N]|' ^ b;ߏYնKcWX\cb;7@ME=οń3[JGFu9qN~]gsN^sn~':c߈"עPNC.Fi#lmc啡7u`D@2vhKόZ1H4 ,=B8\Q2(J!PqQiwiw"j@9!q\9ZC.Fa$r7\FB|]b0-;xOv;e\pmoFJ]aÌ@ ZKoT hƹ0 4Zh-VZkK>Z!y~H\e5|wZS.v@O^g!k"V'p:#8zӹiGqE'5/"`Pmg }oҋkPo7Zoӛ߇zPҗ<+@mkC8wCEOg6\eS^HG^W?K\0P<ڞGK};GN1>y|'NtF> [(Fw]^dsFhùӆbcb()}ӗ}݇zSgo޴7mROdc$r)'V @(C0 se(&^6qsC=9ԗC9ԟx0v9acf3| |(0T m+P^OO~ӏQmBR]HBb롐zz1c m+zq\oEOgVNyThI,z;S8SE_3M`3] ` P p@6r0G9e4Q@BzLj\{$Ee"*>r%(1Ǹe|1YW|t4r1 9"2# PH\z\%UJ2k|L])@>3) ck7u@ڤ ᵒKa5U_M1~r\6v/t/tJ{^語Wu5ϬUgV:Jn.n2Bjg:Gf7Z]:Y ^&ݿԈ*7 gV7ofV*j歒E*ݿOfM,濾y^A zrJEff̬u2F>6Z3af-̬k_5_ ^2]:Yn}&k[яWѬ[WWmW̊epխjE2}״B׳Bײ[ǪY ^׬ZUQ`^kKJY2WU~*{ZɊTt{M^2Pf^wל[ky ]eWBW%dVU${(ѰJY2+CfEȬ ogW{'z:Oxv̚NZN6@F._ꏽcV|jOUWxNڬD: Yӱrvclk5|k[{ ]wlk,ޚpkdHY 1koXf#t}ìg5 o[0koY0k`#fo^~~mKЙ3zo?֚Yza`mfWacSKysN<.8c.1KN"EKyIB"=qhϱhGY- POklMݭc2N$NprDۘ8B<0]c8-> mq%" HRqJ::CH\Qv'88N9X@*)H֒D-ĉc_9.b'1p8Wp,qrg-OҒEJ @_S9ګ5uE_3A^+}^K]cXvnZKm x߃Kd/ugOUZQcqVbPn8 .B>` |HFrI91D6ͦO~1FpĪ΂bbbb]B{JYB)RVhG){ ۙMdLTLӦk38FU=ά{9bߙ+vyup'p /4^3xgq wQ;Tr?Tfr.^Oӿ2}\f<.rʗy N3ާ5Nʾ8'&Rv"m$.Rkx6F[5C\ypN;K)q⸃8WOK|4Ώ2Sb/tMk?}98D?(Kks."/\/""b.:XDEԳSbTKcb,RmrmОԞ☧-+gUK3{g>mqAڮ?ppqMwż8omP6&m8lrxv+[釲)۹F\]΋"sfWaqq8ISe+8s΅#Eqr ǵ8&.G9ЎSrL;J3W^Nk/[N<vR;~]1opA7uI}oR[-6e,>c>G>'?Q Ɨǝ+~E_W|߿{G@Y93|JߕOEGxRM-IoRmxAwh;pܻ<.>x6F[@]3}.U+8q '\|ʸ*0>‡xϕ1گNeMw~uui:b\ խ|GWWk?8o9l|uΈ_PuB\sZ,a?3vΟs9j9""͞:Z]˿;ڿj>0)_uf]drd0JĴ d Pu/(*(YXruߺI"5=յz^|~NLXⶱ q}꺐-3!Yetz?!;G/蓭t.5n'9N$Idq ٣kıgA"'Dru.s܇Cg(/ǵK,Wpq)OA>MoKӓyypl?l1O%q[Z6ކ蓴쀣ء#HbާDWQH4$/r.IJTR霛IJ͛[liJ. jDmo $:Ci:Lc3?etr yʪ9O'ٔIϢbϡ}}&Pz<&N*mR).OzYfl)\͓Bs$=CAn͋܌HeCzf@zCzܻyN^߽_2wOާޏ|}y[{/-%9j}㶼7Ȥ:zROL+lBˇ*dE+gy N@n!V5* 6ZX֏Ɛ>@JЋCꎺmiB&9cBBVN^lǕhVh`0beFIgid0Iu ")6~/PFMX:'e$T̏i%^$f $&~П l̆4T3 mN')TMu#],  XSMR21!׾K>r0&i8 hQF KP~KJ۝R!!% Y%h j l Γt/^̥tUs,ER^ ڵ-EK, Φ:ax۱yV1Fut6^F/+zz~dۀv.lKA9ΰL6%ZGHn'C 4M ԙ62r솝k:8.#ivmRX2Kbl$;ӱcRdff7;a I}znQ`|q~se% G}ߒӭ1+{E:q$#n$$B%H2@*A:d@&dlȁQĈ˅OIq'+nuRP' 7Mat')֍ғ$^7UX0K ' F;M[5:kԖ Ym #90 sYCWEdp\1WBƍ&H)f}ߖ@J1>W/֓v %;9_s`B~eH/R6l ~?)BB21<ȅQِyK4RQ΅ԗ֛qROz.[dbY,k9s;k&ZIhӖL~G"IդiYD) 20 #k4)ɨ#T:`ŲY1=~'h,ʔ$ZE$`YI!D1gx[aR7uKg-t2:nj%^=TK{x]Q={u[M- SҩݹHrjGN\hjL;fXz.vԎrw-wg+tGK {vԮڭRT;Rmǩ$dwI*]$gw!F}'SmoK*pR{A> wrbX]}'GxwlnڝqwbܝН;,ȎXr.]wwEvQܝBI.r;;NF΅ڥP;jGBDvp; v.Apw ҝH)tM?_K틜^%*:h]U֮rvofnfzgn2t\|\z&VYI1"s=9W71$*WJ\OۧGF[-{˼[zkE)WIJUHwGJUMUQ)^~<[eik]\)VvT[%ڑ쎒lbڛ\u:|RNGYZ:JҍjWGm*vzzRӒah7}:OG~!R>gn&#Y{l9ZS졩(!EVJcOM%*`כ hzN2b L84 w_7כn\cd-ys]=Un'Uyi|VeenXzWogzΪU=[?Wlv)O\ۧ< JVDUOS'>>KMֲS7+MײQ=M2P7sL- eFmoHd&*ZFJ+~@)Y4~XF'mA<x=qxKOPmضdy:/ݰGA8DVȊ#ɓ'?&X_ɣ0~<$eD?u/pC0@"GІ!a{ rc:b`?샽v.\ A9ilG}8cQx=q҃(+=zl;aƸǺS s k%]mڶV{5?2j'#} dn9}U2}nc3f[(ğv\Ga*֍֎:֏kh &}} ~cxaL0d;yʶ -l3DލXcĿGfoZ&~.֠m.mf[`+31M6`{8NZz֣hc[סПu:s=l{e6F!mB}PlF{Ѯm+'0od#ֶKЖJ{Џ^k<@= +WG~%WWp{6zu^?~?/\۽n>F=(۽R+ WWmUҮᯜ{z}vtzz\N]ۛ!4zG0u vmuWڤIzC=b:&ZL\ۮ$zugt3'EsCiM-Sb{_%G%%xi92#OLPP\a#6(9^\gL"fįPhL(y0.UJNƕ6@kDqhhW2#A,4fc|#%MoGu-rE%iPt PDT'Ng.JN$a(9Eq>:@|g\$PgK7?B|j\-;@k_6ck 8+-(#)F('*EBqPX^)bE'f/q=@n%'Nf%N4*6L8ǰzK~-_(^i xmOaOŁ%?X)k^Lo9UOۙ͘833t?l䝕+: 8l2N1f Bqdž-0 I ̛xf 0X@;chVh&`.4@=_P㫡 +QOQ9)C{eh 2 )Cпr}@ߥJ 㪆x`fnX i4B!5C #y30i#YG+R3kBA-J5ꑪP/CIoq̿|"ۗ3`MH ޚ^c9-|Mš84̂rMmhZ;ІG;0|co&F*H*-)W2꒒07ID6sy&`=Na?`;75GN?m3PcjY TT6Qi0΁42 }a;FD:d)g8$լ uFi 8 #UmN[-U\Є㚡 *C108G Fc5^[&k"לi@n~?Sl.rT1%d8aaHʍI0)hVeL%ZP#mhv6~4&V2`Lͬ 1y0@ujPU. i<;fgq8Neꠞ5G 0aTA RA.!KIer6@ 1LG;4\q {eu5`lXHb؏xwy4%7PBy1efʸ s_P*V#F=kpT9J2QŪ55yO[hC:V!uڣ UC*IU '2gIPz6X%p/܃w]dq'܁%mbԻ/B;"E&ꠖ,ƚZeB=R6rWmq( K`)e)Ǹ SĘ*\H5PcjqTF=RQ\K݃㤻I5/`,R-Աz\ܟЂrR+QPwڱ-KK}t?[FZ5cM5 u6Uy9,{kIz~&= OdV1͚&Li#')@VhVjf ^`/ԲMeD;Y U<<Ϣϰ5+ GvO:=ZOa6Bfϰgs cp"B^$͚&1ud5ƫF*뤜ao`)o-xpІkVml$5P؄km̅y~#kfss'\K뱦uXg;mx Ii-{)k452JӜ~~>ZP!mh&~G6iE5a0hzϥZjPm -do58Nzem5T ug=k{cǿQ2=jd<<\N/V9olʹeM1T:=ΓxzMTM`'Vʬ݌ZϤ9ttKHbo>zBz2?\"M'*ɯMzWOwDWOqMnk}'*&z:릲41ne~؛ĪV%zWzV7auU7YSUoU%*5%*!UɨJD4l'r~.73SoVvyAS>~%ZROvtS4S%*tSZ-@[ DO,ע~,Hf)U>rI7ֲHc'c U~rC7/fsB'*TiJܔ5PynMܢv?v'ss6B57[y.Gs3=7ӳ3X17STf`agy/-R)3a詖7S,7RRHiD: 7QRiJHOHO THOT =QihINGOoNfIt:9N f<\T΢o9JobD"*QiJAÛ|詇7h I:T¡ =X*PiT4QvF<#4h3 ]BsO a3Žӟ>-H[-ӂ|^l6WV% {?+J2WfJk.k<`ͷ\'(Q|ln Ps?%f;G ;QQP_GėJ~cU|k~1o{A>mKA>n}./l}#XA~#{qu<@ɓ-<ŊߦV%O:ݬ.JfhvN PSuj}̾V%[VB~`CA0kpía%GZg%d3J_ Wρx̳F[Lyh?4cq1s}O08?'vǔʾ/?;mNCɸ=OaRꏟ2qLG{l7(3jkp[~¸i~<O[їp6|펯wk|]}\.8&?{o3*ʮrV:3eڸIc]l/Jzo,8zUիݸҡ?}lPWƌ=Vx%'Lx pTWܻo7ɲ% I6 j?c6Ohf&d JTZKֆ*"lvhCkQaFkMmk3-T"}vrwN}{={罗CGvhϘw " L ,^ItanlZI)vaT#4iW!#B@DB0FO! Cp!"!q.\^uߥß x ]ϔ#i)x_kټz쪛x~,mwʘPOsgfym_2X %P tIx]#߹ЌhL7ƜEץc((L@0 a2F ZG(@0at"܀0$B, hn ,F(A(qӦsRc?q.'AegF+H:DKa-m]sWYuǭ/癐+ftmW<0F.9쑙wBԎvuv1Vgp j389McK5Zc/$/UiE2ZԈz;R?A –^}vvPS]'ΧzU_Du3a͆tWQO}4"_>tHLOmk_V="xeS_~O"FwRԯ.%5oy4F{{rR\Wvjur?Phc"Jy~4;pdz<OC]Bt2Zel~xJG@eCOJ\+;Dݭ ?Uz~y# ćφsZS j*ϣ<ռ82[Lz:_p(cړ|#жaFR|{#bz:QuO(#*Ʉ+JDޝ~p~sax$P:%xHqo./dϳFdD"MFZ3ā]Dk򾁲A R4Wt^x-ucN MutO*SeqFӛLT ?.Q:-p6_֌>aJ D'eEkj #hfĢ5_Z"h툠Ek5un+Yi\D/;eB,?}N#&ѣ4SԺp \5ͪX2,e- hꥡBH.G.EffsƖGuFVJNķZs ^b+’ȒAAI@kTee-$NTuQ~/WzW6*Z/XRXu8cR<*G$?ړ@;饓z]'P|lg\}ykUelώ0qKS1:(|dD4iSCߏ{jW>T>1TLMb]5f BO~B,aЙ5cQ_u HFk#Aqًn6pf8b&х^tMUj7xc7pvHnj4P?~I:?;*gxu|O@hBn. 0r6r>M5)ɨE;,,xA1|G*’V_5I)QB9>yd3L[|. 0*C2}>oM H]U[p˿[޵j˧\;oδňڈ=Y(0ˏў Aw.՗<_ޭ ~#lh=Gp G},O8 y-Q>a5U-޷27|wRtXCis:3ڳ㹃n[_ȋ+&olNzK8(;Uw;zfnz;Mh}Kp6疧\!Ĺ}5qjDhbؚy4QMLDGa_j4qn̷q ʿGbqbk"M&O+"4bhbE? ڇ@O;pΐ{EȉFTx߶oLQ{&;9r;.(5%*s^eg~C _8},w&\ړ&؏I>iO~?}lZ9 ,+'S.i?e9i W݄".̗ò3[ EGeȕ)Ap,%Ozn%w_? `, 7h -p܃uXͰ "C$.QзC,d%~i ,q*h ABe#RB7ZBe=e#H㋬PIT87jIRYxV;ޢ J;@yIGs b/Vl'{!ѣHgl+Kl.-qvFD8YΦYqZCY{sw03DI;&{-yMhFS>D& fw_J%m+pCWrשvTBg 5`MH L19Yxstq(Q r𚊋v +oYXO'K,NBʓti/GYAҿdžI?x& 9o^4Z>al._ J'S0B$|Q,{Bq&`b8Dc;fM Y-QHK_pN,Xmm%ޱ npHR KɀXLKkveT)V)U\SWx˿w"^-c;ߍtZ޽H={ "-؏'?OBaXxPC~/[ ^*s^ ?w³K/gxB9b_sf!4lw t\Ho,@ig8| awWZ>5[\dy.nQSpZ^9ɩ숛䒪Le_L;c)m6gbLIQvx*1NqY_[("pyQr6JR"{l~F=Pǽ?<%> Q2Tdk0\h;htDZ<J'hg0ݠx=*`;L d;bKXŴ?nKSN91,g£z;h9rjzux9K]\Ro oN7j,ȯ d!4@ TB5-(z/Z)Q)ѷ&^WQث¼>WDⲈttqr1 9\$+;JB F+9`<> |DFtIKlN&PS VX[]_xbdpgihln`qsXvxP{ }~vnf^ښVҟNʤF©>6.&z4X>tld<4 , b:2|"$t'-38p l;As`$DFIKNpRtc.egsvx|RփXԡP̦Hī@~hb v nf^V "BZ)+? ,0 2P@&jlmoq{nΈVTY[d^`h6=TR2ޙZzڱֻ½zZ:,t`@ `L.p&bC)(0   80bCorelDRAW &CorelDraw.Graphic.70CorelDRAW0bCorelDRAW Oh+'0` px   ( 4 @LT(Data Networking-Concepts and Technologyece!C:\My Documents\DataTemplate.potnollap1 Do501Microsoft PowerPointemp@_G@ ݽ@p/i =@b2G oM  yl& &&#TNPPp0D v & TNPP &&TNPP    - "-- !-- "-&Gy& - Times New Roman- .l2 CPrinting LISA 98 (c) 1997-1998 Patrick Powell http://www.astart.com                 .&y&  . 2 e0 .& "--L$$ZH7((7HZv(7HZfxvZH7(xf--&& "--L$$` O >/##/> O ` p #/>O``rp`O>/#r ` --&--yPH-- "Arial- .2 wManaging( . .<2 c#Network Printers and Print Spoolers#"        .--q1 -- "ArialT- .2 5kPatrick . .2 5 Powell  ."Arial- .2 ^`papowell. . 2 ^@. .2 ^astart . . 2 ^'.com. .2 Astart  . .H2 + Technologies, 9475 Chesapeake Dr., Suite D   . .$2 _San Diego, CA 92123. .+2  619-874-6543 http://www. . .2 ;astart  . . 2 ~.com .--"Systemn-&TNPP &՜.+,0l   On-screen Show[ j Times New RomanArial Courier NewSymbolDataTemplate.pot-Managing Network Printers and Print Spoolers&Part 1- Printer Hardware and FirmwarePart 2-BSD Print Spoolers-Part 3 - Managing Print Spooler Operations Part 4 - Horrible Problems.Part 5 - Multi-platform Printing and GatewaysPart 5 - Contd References Software&Part 1 Printer Hardware and FirmwareBasic Printer OperationPrinter Communication Channels Serial PortAdvantages of Serial PortDisadvantages of Serial PortParallel Port - AdvantagesParallel Port - DisadvantagesNetwork InterfaceNetwork DisadvantagesNetwork AdvantagesHybrid SolutionsHP Jetdirect ConfigurationJetdirect Configuration FileJetdirect RestrictionsDirect ConnectionsSNMPSNMP To The Rescue (Maybe)WPrint Job Formats and Page Description Languages OR What Do You Send To The Printer?Print Job Data Formats)Vintage Printers (Impact, Daisy, etc...)Printing Text on AntiquesAdvice on Antiques PlottersRaster Output Devices XerographyPostScript OriginsPostScript DescriptionPostScript Printer ControlPostScript Disadvantages"PostScript Disadvantages (Contd)PostScript AdvantagesPostScript BooksEmbedded PostScript (EPS),PostScript Document Structuring ConventionsTools For PostScriptTools for PostScript (Contd)Binary Communications Protocol&Tagged Binary Communications Protocol+Postscript Printer Description Files (PPD) PPD WarningsHP PCLPCL ReferencesPCL EvolutionPCL DisadvantagesPCL AdvantagesMystery PCL Commands Quick TestQuick Test (Contd)Portable Document Format (PDF) Printing PDFPortable Job Language PJL FeaturesPJL DisadvantagesPPD Meets PJLPart 2 Print SpoolersPrint Spooler Basics#Common Print Spooler ArchitecturesLPR (BSD/RFC1179) ArchitectureLP (SVR4) Architecture!Novell Print Server ArchitectureNT Print Server Architecture ObservationRFC 1179 Printer ProtocolRFC1179Basic Concepts Print JobPrint Job FilesControl File FormatData File FormatControl File BotchesJob Transfer ProtocolProtocol HeadachesPrinter Status (LPQ)Remove Job (LPRM)Start Printer (LPC)LPC (Not Defined by RFC1179)BSD Print Spoolers Why LPD/BSD? Why LPRng?LP and LPSTAT EmulationNetwork Based AdministrationLPRng SecurityLPD/BSD DetailsSpooler Operation/etc/printcapImportant keywordsLightweight printcap filesSimplified FormatNew Printcap Guidlines LPR ClientFormatsFormats and Filters Binary Files"Destination Printer SpecificationLightweight ClientsLPRng Printer Name Conventions ExtensionsLPR Client and FiltersFilters and Job Processing%Basic Filters - Text TransformationsLPRngs lpf filterPostScript Printer FiltersLPRngs psfilter APSFILTERLPRng APSFILTERLPRngs ifhp FilterHP Printer ProblemsSimple Parallel Port Printer&PS, PJL, or PCL Parallel Port PrinterSimple Serial Port Printer$PS, PJL, or PCL Serial Port Printer%PS, PJL, or PCL Network Port Printer*Part 3 Managing Print Spooler OperationsPrinters, Queues, and Status LPQ - Status Short Status Long StatusVerbose StatusNo Slide TitleStatus ReportsAlternative Status ReportsLPQ and SecurityLPRM - Job RemovalLPC - LPD/BSD Printer ControlLPC - LPRng VersionLPC - Basic Commands LPC - LPRng Additional CommandsLPC - LPRng Job ControlLPC - LPRng Server Control'Print Queue Job Priority and SelectionLPC TOPQ CommandAdding A Printer"Adding A Printer - Printer Checks"Adding A Printer - Printcap EntryDiagnostics and Debugging DiagnosticsLPRng DiagnosticsClient Operation TracingServer Operation Tracing"Debugging Spooling and UnspoolingFixing Problems Installing and Setting Up LPRngInstalling LPRngLPRng's checkpc Program Setting Up LPRng on BSD Systems!Setting Up LPRng on Solaris/SVR4LP System EmulationLoad Sharing and Printer PoolsPrinter Pools and Load SharingLoad Sharing DetailsLoad Sharing Details (Contd)Bounce QueuesBounce Queues and FiltersReasons for BQ UseCorrect BQ UseAlternate BQ PrintcapUpdate Data File FormatsEditing Control FilesRouting Jobs to Spool QueuesRoutingRouting FilterHost Specific Printcap EntriesHost Specific Printcap EntryJPart 4 Horrible Problems Permissions Authentication Accounting Defaults PermissionsLPRng Permissions File Example1 Example 2AuthenticationAuthenticator Example: PGPForcing Authentication AccountingJob Level AccountingPage Level AccountingAccessing Page CountersConfiguration and DefaultsConfiguration Variables-Part 5 Multi-platform Printing and GatewaysPrint Job PreparationLP / LPD Uses Late BindingLPR -Z Options!MS Windows/MAC Use Early Binding!Early Binding and Job GenerationDriversNetwork Printing!Win95 and LPR/BSD Print Spoolers AlternativesWhy Use Wslpr?Why Not JetDirect? Windows 98"Windows NT Server LPR/BSD Gateway LPRng on NT Novell Printer LPD/BSD Gateways!Samba, SMB, and LPD/BSD Gateways!PCNFS and PCNFSD Gateways to LPDApple and LPR/BSDLP (Solaris) to LPD Printing$Monitoring Printer Status with SNMPPerl, Tcl/TK to the Rescue?Where Do I Get Help?SummaryQuestions and Answers  Fonts UsedDesign TemplateEmbedded OLE Servers Slide Titles&CorelDraw.Graphic.70CorelDRAW0bCorelDRAW &CorelDraw.Graphic.70CorelDRAW:0bCorelDRAW &CorelDraw.Graphic.70CorelDRAW0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAWr0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAWo0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAW0 bCorelDRAW &CorelDraw.Graphic.70CorelDRAW/ 00DTimes New Roman$b$b0b0TbTb 0DArialNew Roman$b$b0b0TbTb 0" DCourier Newman$b$b0b0TbTb 010DSymbol Newman$b$b0b0TbTb 0 @ @`  @n?" dd@  @@``   x      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTVWXYZ[\]^`abdefilnorstuvwy_dhqrltu  _ghqcjkx{}|~mpzp}|~`2$"xܓ1oTz2$ļ&&s+/Tz2$rp9)`z2$|@C}IRիW z2$v:TEjmz2$M15}韙G %;h+z2$(@ uE1xfcz2$r n+" zS ~1?@8  g4$d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnoprstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~$d`b 0XbLppp@ g4*d*d`b 0Xb, ppp@  <4!d!d$b$b\ <4BdBd$b$bf\ l?, Printing LISA 98 (c) 1997-1998 Patrick Powell http://www.astart.comO =,Managing Network Printers and Print Spoolers--$Patrick Powell papowell@astart.com Astart Technologies, 9475 Chesapeake Dr., Suite D San Diego, CA 92123 619-874-6543 http://www.astart.com ~ZX%Part 1- PrinterPowerPoint Document(q[ DocumentSummaryInformation8Root EntrydO)74PicturesCurrent User$SummaryInformation( !"#./50E6789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;_[ blap1Oh+'0` px   ( 4 @LT(Data Networking-Concepts and Technologyece!C:\My Documents\DataTemplate.potnollap1 Do501Microsoft PowerPointemp@_G@ ݽ@p/i =@b2G oM  yl& Hardware and Firmware.Printer Mechanisms Host/Printer Connections Serial, Parallel, Network Configuration and Problems Network Printer Support Print Job Formats for Vintage Printers Print Job Formats for Modern Printers Page Description Languages PostScript and PCL Job Control and Format Languages PostScript, PCL, and PJL ~,Mh!,Mh!Part 2-BSD Print SpoolersPrint Spooler Basics Print Spooler Client-Server Structure BSD, SVR4, Novell, MS RFC1179 BSD Print Spoolers LPRng Printing Process Printcap Files Filters Printcap Examples 6EUEU>m  ,Part 3 - Managing Print Spooler Operations Management Facilities lpc, printcap Adding New Printers checkpc (lprng) Installation replacing print spooling system Diagnostics Load Sharing and Printer Pools Bounce Queues Routing Host Specific Printcap Entries~ ` `b ~ Part 4 - Horrible Problems8Permissions Security and Authentication Accounting SNMP 8-Part 5 - Multi-platform Printing and GatewayskNetwork Print Spooling Drivers and Print Spoolers Printer Gateways Microsoft Print Spooler Windows 95 WslprePart 5 - Cont d zNT LPR Support Novell Print Spooler PCNFS (Sun Microsystems) PCNFSD LPR Support Samba Samba LPR Support Desperate Measures, ReferencesPostScript Tutorial and Cookbook, Adobe Systems, Addison-Wesley 1985 PostScript Language Program Design, Adobe Systems, Addison-Wesley 1985 PostScript Language Reference Manual, 2nd Edition, Adobe Systems, Inc. Addison-Wesley, 1990 Portable Document Format Reference Manual, Adobe Systems, Inc. Addison-Wesley, 1993 Web Site for Adobe and more documents: http://www.adobe.com/supportservice/devrelations/technotes.html d@>y  -SoftwareLPRng ftp site: unless otherwise indicated, all software can be obtained from the ftp://ftp.astart.com/LPRng site. See the README and INDEX file GNU software can be obtained from many mirror sites. Try ftp://ftp.uu.net/pub/gnu and look for MIRRORS PostScript Utilities psutil31.tgz More PostScript Utilities psutilmore2.tgz GhostScript GNU GhostView GNU Adobe Acrobat PDF viewer/tools www.adobe.com LPRng LPRng distribution FILTERS_LPRng collection of filters, patched for LPRng XiB( 1'#T%Part 1 Printer Hardware and Firmware&&.Basic Printer OperationA Printer is a peripheral device, usually attached to a host computer The host computer transfer print files to the printer over the communication channelPrinter Communication ChannelsPrinter must be connected to source of print jobs Channel determines the rate at which text (characters) are delivered to printer Also determines the reliability Also determines the availability of error messages and diagnostics Simple and Cheap is not always best Serial PortSlowest and most error prone Older printers had a very slow transfer speed (9600 bits/second) due to the real time requirements of handling paper Newer ones can go up to 115 Kbps Data transmissions errors VERY common Printer does not have a large buffer to store input so flow control is absolutely required Hardware (RTS/CTS) flow control almost mandatory Software flow control (DCON/DCOFF or CTRL-S/CTRL-Q) can be used over networks Watch out for buffering in network!%Advantages of Serial Port Error and Status messages available from printer Most systems have multiple serial ports, can attach multiple printers Can be put on terminal server (watch out for flow control, enable RTS/CTS) Cables can be up to 50 feet long Cheat and they can be up to 200 feet long Disadvantages of Serial PortVery very slow Did I mention flow control problems? This is a major headache Errors in data transmissions can have interesting effects Note: Software Flow Control Headaches Some software flow control tries to accommodate errors by restarting transmission when ANY character is received from the other end after a time-out period. When you get a printer error, the printer sends CTRL-S to stop job and then later reports status, you lose rest of job. (Headache #27)$%% Parallel Port - AdvantagesTransfers data 8 bits at a time Flow control implicit because it uses a hardware handshake for data transfer. Very low error rate on data transfer Parallel Port - DisadvantagesHas limited bi-directional capability Out of Paper, Error indication Bi-directional support limited on most interfaces May not be able to indicate error condition when flow control enabled Surprisingly low throughput due to interrupt per byte on most PCs and workstations Games played at OS level to improve this Real pain is getting status back using bi-directional mode Real cheap folks can even use print sharing boxes You get what you pay for Don t call me when they lock up& 2FS)m9& 2FS)m9> 7} Network InterfaceUsually supports various protocols IPX (Novell and Microsoft) NETBUI (Microsoft) TCP/IP (LPD + RAW Connection) Printer builders are (or did not use to be) network aware Has changed tremendously over last couple of years Configuration of interface is now usually pretty simple once you understand the various options Front panel configuration is really quite simple HP Jetdirect has BOOTP/DHCP configuration 6#L)#L)o   Network DisadvantagesAnybody can print on the printer Leads to the mysterious print job from nowhere In spite of being on the network, only one person can actively use the printer at a time Yes, yes, I know what the documentation says and I am telling you what REALLY happens Multiple users can/will/have locked up the interface (this is a known problem with HP Jetdirect interfaces) Multiple users trying different protocols can/will/have locked up the interface When the printer dies there is usually no handy way to reset it without powering it downl!/ZV!ZVU !Network AdvantagesVery high throughput, and has built in flow control Very low transfer error rate Ethernet has CRC Higher level protocols also do checking at transport level Can be configured using BOOTP/DHCP Status can be obtained by using SMTP on most systems If DHCP working, can even reboot printer Printer sharing becomes very simple, in principle For shared system resource, may actually be the cheapest interface as it does not require host for support (see LPR/LPD later for details)ZQLX*QLX*"Hybrid Solutions6Print Server Boxes Has network interface, supports parallel port and serial ports for printers and/or modems I have used several different ones with various levels of success Lantronics - works, configuration tricky, good functionality Rose - works, configuration simple, limited functionality Dumb Network Interface (LPserver UNIX/NT/W95) Extremely stupid program that turns your PC into a TCP/IP raw socket connection to the printers serial (bi-directional) or parallel (unidirectional) port This has its place when doing accounting, very tricky printer operationsZw.w.> vy#EHP Jetdirect Configuration Similar in principle to most printer network interfaces Front Panel Configuration Enable basic network protocols IPX/Novell Print Spooler DCP/Microsoft TCP/IP Set IP address, netmask, syslog server Enable BOOTP/DHCP configuration BOOTP/DHCP Configuration BOOTP/DHCP supplies IP configuration information Specifies a TFTP server and file that has detailed printer configuration information~R.' R.' ,$FJetdirect Configuration File # Administration Info name: picasso location: 1st floor, south wall contact: Phil, ext 1234 # Only allow connections from network/netmask or host allow: 10.0.0.0 255.0.0.0 allow: 15.1.2.3 idle-timeout: 120 syslog-facility: local2 # SNMP Configuration get-community-name: blue set-community-name: yellow trap-community-name: red trap-dest: 15.1.2.3 trap-dest: 15.2.3.4 authentication-trap: on` N5Ut5,Ey#%GJetdirect Restrictions The  allow configuration parameter enables you to restrict access to the printer from the specified network/subnetmask addresses This is essential to controlling access to your printer You need to use the BOOTP/DHCP to set the IP address, gateway, and syslog server Note: Setting up the BOOTP/DHCP server can be tricky. You will need to either have a DHCP server on each subnet, run a  forwarder process on a host on the subnet, or have your router forward requests to a server. (Hint: if you have Cisco router, use the  ip helper command to specify the DHCP server address.) bm r-a7&|Direct ConnectionsTCP/IP Port 9100 on the Jetdirect card is a direct connection to the print engine This is a bi-directional connection, and error messages will be written on it Other network support cards have similar facilities; if they do not, then DON T USE THE CARD Usually only one connection at a time can be active; this can cause problems if trying to share the printer among several different hosts, :,'ISNMP Simple Network Management Protocol provides a common interface to obtaining information about the status of network devices. An  agent process resides in the network device, spies on the activities of the device, and provides information when queried. Agents can also generate messages (traps) when a specific activity is detected A  manager process queries agents for status Agents can also  write information to the network device when requested by a manager. This allows configuration management to be done by a manager.(HSNMP To The Rescue (Maybe)6The SNMP standards include a Printer MIB In principle, you can use a simple SNMP manager to query the values of the Printer MIB. These are, to put it mildly, very basic. In practice, most vendors have extended the MIB to provide more detailed information about the error conditions, status, etc..... Unfortunately, most print spoolers do not use the SNMP facilities to query printers The common SNMP managers such as HP Openlook, SunNetManager, etc..... have the common printer MIBs already provided. If you are an administrator, please learn more about managers7Z7> "R)UVPrint Job Formats and Page Description Languages OR What Do You Send To The Printer?WW*Print Job Data FormatsCoherent and organized approach to this subject is impossible due to historical development Will give a rapid, functional, biased, opinionated, subjective, etc.... view of the subject Remember: each manufacturer tries to distinguish their product in the market Remember: THERE ARE NO STANDARDS Actually, this is a lie. There are standards. Nobody follows them :-) Remember: The printer with the largest market share becomes the standard Thus: Every 3 years there is a new standard.@'Hv'HO'+(Vintage Printers (Impact, Daisy, etc...)*Fixed size characters (Fixed width/height fonts) Character set at the whim/market demand of the manufacturers Page dimensions based on paper sizes 8.5 x 11 inches (letter), 11 x 15 (computer listing) some larger size (legal) whacko A4/A3 sizes (Europe) De Facto Standard 132 columns and 66 lines Unless it was 60 lines (margin at top? bottom) Or 80 columns with 66 line??? And don t forget metric sizes...Hjj,Printing Text on AntiquesSend characters, print characters. Simple? Wrong. You forgot about INTERNATIONALIZATION $ are simple, try typesetting French, etc.... How about EBCDIIC? (Don t laugh) Tabs? Support for tabs? Why? Smaller text files, good for limited file systems Harder to change to other manufacturer s printer De Facto: tabs are at 8 positions Market forces demand features Italics, true bolds, Condensed Fonts Need to have escape sequences to enable these THERE ARE NO STANDARDS3V@j3+@ '-Advice on Antiques(There are a lot of them out there - they were built for abuse, they run and run and run Get the manuals, make 3 copies, and save them for the next admin - they will probably still be in use when you retire Most new impact printers are extremely simple to use Very few features Modeled on major (antique) market dominators If you have to print multiple copies on forms, this is your ONLY choice, so make sure you choose wisely Daisy wheels make best multiple copy impressions Dot matrix are faster, wear out Keep a spare in the back room for parts6??.PlottersOK, these are not printers, but they turned out to inspire the next step in Page Description Languages Original plotters were Analog, hooked to instrumentation. Drew nice curves on EXPENSIVE green paper Market developed for plotters which could be attached to  smart instruments or computers which simulated analog plotters Drew backgrounds, labels, etc... HP (and others) developed several Plotter Control Languages Had embedded commands for drawing textl*z!<'*z!<'/Raster Output DevicesOriginally electrostatic plotters, would draw a plot a line at a time Needed to convert Gerber (or HP or IBM& ) plotter stuff to raster format Insight that they could also produce hard copy of text using various fonts TROFF meets VARIAN to produce $$$ of revenue as graduate students burn up 100s of rolls of expensive electrostatic paper0 XerographyXerography works by whacking charges onto a drum The whacking is done by high intensity tightly focused light (laser?) Generated by scanning original mechanically OR BY SHINING A LASER ONTO THE PAPER AND TURNING THE LIGHT ON AND OFF UNDER COMPUTER CONTROL VOILA! The Laser Printer6vv1PostScript OriginsDeveloped originally as part of 1975-1980 research into computer graphics Origins in computer picture generation Text, fonts, etc..., were wedged into the language, trying to import concepts of typography into the programming language Model used was to have dumb programs generate PostScript, and have a smart PostScript converter do rasterization Adobe produced first PostScript Language definition, and the first PostScript printersMX2PostScript Description*PostScript is a Stack Based Programming Language You don t want to know. You REALLY don t want to know. If you want to know, you are warped. Or a Computer Science Whacko. PostScript files are programs. A typesetting program is a Program Generator that produces programs for another computer that runs to produce your output. Are you starting to understand why you have problems with printing PostScript?3PostScript Printer ControlSince PostScript is a program running on the printer, why not give it the ability to control the type of paper, paper trays to be used, duplex printing, etc...? We do this by having the PostScript program set values of various system dictionary variables (I told you, you REALLY don t want to know). After the job finishes, you should really revert to a known state of these system dictionary variables Right? Wrong! You may want to set these to be permanent for all jobs So we have a password needed. Guess what the password is? Right. You guessed it! (Answer: no password)4PostScript DisadvantagesSince it is a programming language, it comes in different versions This is mandatory, and is Bernard s Law: Never trust Version 1. It requires memory to store the program, to hold temporary results, and to do rasterization, font conversions, etc.... Thus we encounter Booth s Law: You never have enough memory for a graphics program. There is always a bug in the compiler/interpreter Even correct PostScript code does not always work correctly when the interpreter has errorslDAyU2\DAyU2\5BPostScript Disadvantages (Cont d)qSince you are running a program, the page generation can be hideously slow if you are doing fancy graphics operations such as scaling, rotation, etc... of large bit mapped graphics and fills When a problem is encountered, you have limited diagnostic capability. Ghostscript Is Your Friend - Use It Watch out for PostScript Interpreter Version/Level/Revision Problems ,jj  _6PostScript Advantages$Incredibly portable across different vendors printers for text/picture generation Not due to the language, but due to Adobe selling the code for the Interpreter at such a low price that everybody used it (Not true any more, so we are seeing some interesting PostScript bugs). Now everybody needs to be Adobe Bug Compatible Remember: THERE ARE NO STANDARDS Previewers can be built that will give you an exact idea of what your PostScript will look like De Facto the standard, most portable way to generate documents for printing6E"E"f 7PostScript Books`PostScript Tutorial and Cookbook The Blue Book Very easy to read Use Ghostscript and learn about PostScript programming PostScript Language Reference Manual 2nd Edition Almost incomprehensible; it IS a standard, after all Appendixes are VERY helpful You can get a copy from Adobe s web site www.adobe.com`!W%!W%D 8Embedded PostScript (EPS) Self contained PostScript files that produce a graphical object when executed See PostScript Reference Manual Appendix H Version 3.0 is most common Almost all graphical object editors import and/or export EPS Some Web browsers even have extensions that display it${{9+PostScript Document Structuring Conventions,,PostScript Reference Manual, Appendix G Version 3 is most generally used Specifies how PostScript should be used for document generation Prologue sets up overall characteristics of document, such as fonts, subroutines, etc... Body consists of set of independent pages Each page can be removed/duplicated/inserted in document without altering printing Standard also provides guidelines for information about documentH(!@(!@: Tools For PostScriptAssumes Document Structure Version 3 PS Utilities Version 3.1 (psutil3.tar.gz) Set of UNIX tools for massaging PostScript file psnup will print N virtual pages per real page psselect will select pages to be printed psrev will reverse page order PS fixer (psmoreutils2.tar.gz) merges pages of two documents, good for putting a background on a document Selectively places/orients individual pages or sets of pages on a document desperation tool made from psutils, cannot live without itPOZZZZOtK1)  4;!:Tools for PostScript (Cont d)Several tools available that allow you to add documentation control to PostScript files For example, you can have the first page fed from a special tray, and then remainder from another tray These are usually commercially available, but freeware/shareware versions have been spotted. Some commercial print spoolers incorporate this functionality into their Print Spooler filters (see later) See the LPRng ftp/web site for pointers6X0(X0(<"Binary Communications Protocol"PostScript language specification states that the program must be in printable ASCII characters or a limited set of control characters However, you can embed inline binary data into a PostScript file using very tricky methods Some control characters normally cause a PostScript Interpreter to end execution, send status, or may actually be ignored. The Binary Communications Protocol escapes these control characters. If you have character C needing escape you send: 0x01 C ^ 0x40 e.g. - 0x01 (^A) -> 0x01  A or 0x01 0x41$,,=#%Tagged Binary Communications ProtocolWarning If you have a file with embedded BCP escapes and you redo the BCP escapes, then you will destroy the escaping This leads to the Tagged BCP protocol We add a ^A M sequence (this is an escaped CR character) to indicate that we are doing BCP and have put in the escapes When we scan the file and detect ^A M we do not add more escapes This is one of the leading causes of problems printing PostScript files with binary images produced by some graphics programsZo&~o&~>9*Postscript Printer Description Files (PPD)7You can use PostScript to manage and control various printer operations What variables/operations/values do you need? Each PostScript printer should have a PostScript Printer Description file that contains the various PostScript manageable options and how to set them Example: HP4MP - setting manual feed *ManualFeed True: "1 dict dup /ManualFeed true put setpagedevice" *ManualFeed False: "1 dict dup /ManualFeed false put setpagedevice" The contents include the PostScript needed to set the required operations Easy to extract from the PPD file and send to printersjHZ.ZZZZH.2        ?: PPD WarningsEach printer has a possibly different set of commands, so check the PPD file for the printers Manufacturers are not required to provide PPD files, so you may have to dig around for them. There are many extensions to the basic PPD capabilities, some of which are very specific to a particular printer. WARNING: I have discovered discrepancies between the PPD, manual, and actual operation. RTFM, and try it out. WARNING: some options interact, and will lock up printers. PPD and documents do not cover this@$HP PCLHewlett Packard developed a line of laser printers and wanted to sell them to the various printing markets They also did not like paying Adobe royalties and licensing fees They invented the Printer Command Language (PCL) Version 1, Version 2, Version 3, Version 4... Currently we have Version 5 and rumors of 6 PCL is NOT a programming language, it is a Printer Command Language It specifies where on a page to draw lines, glyphs, and does it very well.6ZZA;PCL ReferencesPCL 5 Printer Language Technical Reference Surprisingly readable, but BORING, repetitive, and written in a horribly verbose manner. Read and memorize Section 3.8 Resetting the Print Environment \]E and \]%-12345X are your friends, and will help you keep your sanity Note that this sequence, when sent in a PCL file, will terminate job printing and may have some surprising consequences Read Chapter 4 - PCL Job Control Commands This will explain most of the printer control functionsl+Y*8+Y*8B% PCL EvolutionOriginal versions of PCL did not support downloadable fonts. This was supposed to be a feature - you would buy font cards and plug them in. User feedback (flames) convinced HP that they needed to support downloadable fonts Then we had raster graphics support added Then we had page structuring support added It now can make the printer sing, dance, and do just about anything (as long as you know the correct PCL commands)C&PCL DisadvantagesOlder PCL printers do not have downloadable font support You may (again) need lots of memory for fonts, images, etc.... (There s no such thing as a free lunch) It uses control characters (ESC) for many of the commands, making it difficult to fix/mangle/edit PCL files without special editors capable of handling long lines, control characters, etc....D'PCL Advantages0Simple simple to generate Slightly smaller files than PostScript Very much faster page generation, as there is little to do except read the input and copy BitBlits to the graphics memory Even has commands to do the printer mechanism commands such as bin selections, etc...., built into the language now. E>Mystery PCL CommandsSince each printer has a different set of capabilities, you need to have PCL commands to operate the printer There should be a  PCL Printer Description file for each printer, right? ANSWER: no Each printer should have a document providing a complete list of the PCL commands supported, right? ANSWER: ummm& right& but only the development group has that information, the printer is not in development any more, & Situation is getting better, but documentation is still weak point for specialized operationsZ dz^ dz^.F< Quick TestFIf you are using PostScript and TBCP, what is the end TBCP sequence? ANSWER: \]%-12345X If you want to make sure that your PostScript job gets printed, even if the previous job did not end with the EOJ string, what should you put into your file? ANSWER: \]%-12345X before the PostScript If you are trying to decide if a file is PostScript or PJL or text, you might try looking for %!PS as the first characters, right? ANSWER: Yes, No, Maybe? Sigh& It all depends on how smart/stupid/weird the writers of the PostScript generation program are...rE)E)G?&Quick Test (Cont d) $How can you tell if you have PCL or PostScript? Ummm& throw it at the printer and if it works, it was PostScript GhostScript can be modified to disregard the various PCL sequences. This makes life much easier when trying to preview files produced on MS/Apple based programs60A0A,/< H(Portable Document Format (PDF)PDF is basically PostScript s version of PCL It is very simple All the time consuming PostScript operations are disallowed There are some minor HyperText things thrown in to provide some previewer help Concept was to generate your document in PostScript or PCL, run it through a PDF converter, and you get PDF. To print, you expand the PDF into the more verbose PostScript, add the Job Structuring Conventions, and you are done Adobe now sells PDF viewers and translators...6--, ! I@ Printing PDFlVery few (none?) printers will accept PDF files You need to preprocess them into PostScript Ghostscript should be able to do this Whoops its cookies on many PDF files You can extend GhostScript to handle PDF files now with reasonable success, but you will be violating some patents, restrictions on encryption, etc etc. Look at the GhostScript web site for details6\K\K>[ O  J)Portable Job LanguageNow printers have to support PCL, PostScript, TEXT, and who knows what. Documents need to be printed using different papers, formats, orientations, etc.... Need a higher level language to control this type of operation, overriding (perhaps) the operations in the document Portable Job Language was intended to do this Most important feature is ^]-012345XPJL EOF This causes a functional reset of the printer Cannot be ignored, escaped, hidden, etc.... Restores sanity to the printing world$kkK* PJL FeatureslProvides ways to specify the Page Description Language PJL Select Postscript Provides ways to specify the orientation (if the PDL does not override it) Basically, provides a way of overriding the PDL requests And most important: Provides a standard set of error messages to be returned IRREGARDLESS of the PCL This last feature is worth the pain and effort of PJLl7Q67 (6L+PJL DisadvantagesNot all PJL features are supported in all printers Printer vendors are very closed mouthed about what they support. There is no  PJL Printer Document standard for PJL HP does not provide details on all of the error messages, leaving it up to implementers to discover that there are a whole new set of messages concerned with various printer operations Try getting a paper jam in a multi-bin feed printer and see what messages you get Different releases of printer EPROMs support different PJL sets HPXXSi are notoriously different from other HPs And of course - different vendors have different messagesdcPRP@P0P:PcR@0:>%;MA PPD Meets PJLVIf a printer supports PostScript, there is usually a PPD file for the printer. HP has very nicely put in the various PJL and PostScript sequences needed to perform the various printer control functions in their distributed PPD files. This information is not documented of course& but handy to know NVPart 2 Print SpoolersO/Print Spooler BasicsUsers create jobs (print files) They use a print client to send job to a print server or spooler The spooler then transfers jobs to a printer Multiple users (clients) can transfer jobs to a server A server can transfer multiple jobs to printersZP4"Common Print Spooler Architectures+LPR/BSD UNIX variants include PLP, LPRng RFC1179 documents client/server protocol TCP/IP network based LP/SVR4 UNIX Proprietary client/server protocol Novell Netware Semi-proprietary protocol IPX Network based (or IPX over TCP) Microsoft SMB Basics documented, but details are not NetBUI, IPX based Z #>: Z #>:>"ut Q6LPR (BSD/RFC1179) Architecturelpd is the print daemon listens on port TCP port 515 for requests from lpr clients uses printcap for configuration lprm (job removal), lpq (job status) communicate over port 515 jobs stored in spool queue on server host6[i[iZC RR7LP (SVR4) Architecturelpsched is the print daemon listens on /dev/printer (FIFO) uses /etc.../lp/* files for configuration lpstat, lpadmin, accept, enable used to control operation jobs stored in spool queue on server host6IdIdZ!US8 Novell Print Server ArchitectureFile Server is the print daemon files placed on server and requests made for printing from clients uses database for configuration printing done by server process on file server or other host management done using admintool or other facility jobs stored in spool queue on file server< ZcZZ c =TBNT Print Server ArchitectureServer is the print daemon files placed on server and requests made for printing from clients uses registry for configuration printing done by server process on file server or other host management done using control panel jobs stored in spool queue on file server<ZcZZcUC ObservationArchitectures are almost identical Should be easy to understand, right? WRONG You can set up the various pieces easily, but the problems start when you want to do more than just fling files at printers Accounting Restricting access Error logging and recoveryHH}9H}9VWRFC 1179 Printer ProtocolW5RFC1179Documented the original BSD print spooler network protocol Incomplete, inconsistent, and open to abuse& I mean implementation inconsistencies Only common, non-proprietary, open standard available today This situation may change as the IETF has a Internet Printing Protocol (IPP) working group making progress towards a new and sensible standard6o?XXBasic ConceptsLPD server listens on TCP/IP port 515 for connections from client programs (LPR, LPQ, LPRM, LPC) and other LPD servers Connections originate from port 721 - 731 to server (Privileged Port in old TCP/IP network software). Clients send requests, get confirmation and/or status in return Request can be: (LPR) transfer job (LPQ) get queue status (LPRM) remove job (LPC LPRng Extensions) queue control$.c.cpYY Print Job%control file contains information about the job submitter and the way the job is to be processed by the print server names the data files for the job one or more data files control file and data files are transferred in binary form from client to server server must interpret contents of filesH h( h(ZZPrint Job FilesFile names have defined format cf X nnn hostname - control file cfA001patrick cfA002patrick.astart.com X is a letter indicating job priority nnn is a  job sequence number hostname is the name of the host originating the job Data file names should have same format df X nnn hostname dfA001patrick dfB001patrick the X is a sequence identifier Order that files should be sent in is not defined Most network printers ignore the control files and just print data files, treating each as an independent jobP"P)PzP(PPPPP"'z(t8*x[[Control File FormatASCII printable characters, line ending with \n Example: Hastart4.astart.com Host name Proot User name (banner) J(stdin) Job title CA Job class Lroot User name (billing) fdfA458astart4.astart.com Data file N(stdin) Data file name UdfA458astart4.astart.com Unlink data file Lines starting with upper case letters are information Lines starting with lower case letters are data files93 m9    mB(<6/\\Data File FormatEach data file in the control file is identified by a line starting with a lower case letter. This letter indicates the  format of the data file and is a hint to the server on how it should be printed. The U lines in the control file were originally used to indicate that the spooler should remove the data files after printing. By default, most spoolers do this by default. $|{Control File BotchesAs you see, the format of the control files is trivial It is amazing that so many implementations get it wrong& Use non-ASCII characters (UNICODE) Use CR/LF as end of line indication Exceed maximum line lengths Give each job the same control file/data file name Give data files names like dfA371jobs.dat - based on name of file |p(p,]]Job Transfer ProtocolClient sends a line of the form: \002printer\n Server responds with \000 Client sends control file transfer request \002cfXnnnhostname length\n Server responds with \000 Client sends length bytes of control file, then \000 Server responds with \000 Client sends data file transfer request \003dfXnnnhostname length\n Server responds with \000 Client sends length bytes of data file, then \000 Server responds with \000 Repeat sending data files until all done.!  E   L  )! ,1(.)^^Protocol HeadachesSome clients decide to send data files first, then control file Some clients do not send data files in same order as listed in control file Some clients send \n\r or \r\n instead of \n Some clients put non-ASCII characters in the control file Some clients do not use correct names for data files Some clients put in non-present data files Now, nobody would put in a line like: f/etc.../password or U/etc.../password in the control file, would they? Hmm...S&Q &+__Printer Status (LPQ)RFC1179 uses the following protocol to get printer status Client sends \003printername [keys]\n short? format \004printername [keys]\n alternate? \009printername [keys]\n LPRng verbose Server responds with status, then closes the connection There is no definition of what the status format must be. Every LPD server returns a different one Keys are used to refine the status, e.g. - select a job No definition of what information is searched for...pGu G   ``Remove Job (LPRM)\RFC1179 uses the following protocol to remove printer job Client sends \005printername user[keys]\n Server responds with status, then closes the connection There is no definition of what the returned status format must be. Every LPD server returns a different one Keys are used to select a job No definition of what information is searched for...:HHaaStart Printer (LPC)Amazingly, RFC1179 only has one command to control a printer. Client sends \001printername\n Server responds with \000 and starts the printerLK1KbbLPC (Not Defined by RFC1179) In addition to the standard functions defined by RFC1179, there is need for some sort of administration control. This is not part of RFC1179, and is usually implemented by the LPC program. Thus, there is no way to remotely manage a printer using RFC1179, leading to the use of SNMP...ccBSD Print Spoolersdd Why LPD/BSD?kUses RFC1179 All other protocols are proprietary It is trivial to implement over a network, and allows any TCP/IP based protocol to provide print services Other protocols can be gatewayed to RFC1179 based printers/spoolers with very little effort Least common denominator in multiprotocol printing Available on all UNIX, Microsoft, Apple, etc... etc... platforms$l , WLeD Why LPRng?LPRng is a descendant of the LPD/BSD family of spoolers It provides administrative control over printing operations It has incredible flexibility, logging, debugging You pay for this by not having a simple plug and play system for non-trivial setups You need to RTFM quite a bit From the users viewpoint, LPRng strongly resembles the LPR/BSD print spoolers The architecture is similar, but not identical to original BSD<ZZZ$,oLP and LPSTAT EmulationLPRng simulates a large subset of the SVR4 LP and LPSTAT command functionality You can fine tune this emulation to be more vendor specificfeNetwork Based AdministrationAdministration of printers and print queues is a major problem in large system administration is management of print queues On most SVR4 and BSD print systems you must log in as root, execute multiple different commands, and perhaps even delete or edit files by hand LPRng extends RFC1179 and provides a LPC command as well as the LPQ, LPRM, and LPR support There is even a strongly authenticated version using Kerberos, PGP, or SSL available (compile time option).,  /gfLPRng Security LPRng eliminates many of the security loopholes present in the original BSD code and design Many vendors have shipped LPD distributions with various security problems Remember - LPR/BSD clients run SUID root, allowing users to play games with stack overflows, etc LPR/BSD runs filters as root, and some filters are shell scripts with such things as exec $* Exploiting this to gain root permissions is left as an exercise for the student:_P_P$_PhgLPD/BSD Details Spooler Operation}lpd is the print daemon listens on port TCP port 515 for requests from lpr clients uses printcap for configuration jobs stored in spool queue on server host as control file with user information and list of data files to be printed data files containing information to be printed LPD/BSD requires LPD server to run on same host as client LPRng allows server to be on different hostZ[-xf[-xfHC 'ii /etc/printcapDatabase used to control printer operations Based on the termcap format, #parallel attached DUMB printer pr1|dumb:\ :lp=/dev/lp:sh@:sb:mx#1000:\ :sd=/var/spool/lpd:\ :of=/usr/libexec/of:\ :if=/usr/libexec/if The first part of the printcap is the primary (reference) name and the printer aliases. Following entries are either keywords and values, flags (:sb: set sb flag on, :sh@: sets sh flag off), or numerical values (:mx#1000: or :fx#0x13:)J J !  ^8C t " jjImportant keywordsNlp = the local printing device rm=remote host, rp=remote printer used when jobs are to be forwarded to another host using RFC1179 job transfer sd=spool directory where the jobs are stored sh - suppress (no) headers (banners) when printing locally sb - short (1 line) banner instead of long one mx # - maximum job size (0 is unlimited)ZANAN~ ]*8,'klLightweight printcap files LPRng eliminates much of the overhead of the BSD printcap files #simple printer entry lp|pr:lp=pr@host Client programs only need to know printer name and host running LPD server $@'LR+ TSimplified FormatLp2:server :sd=/var/lpd/lp2 :lp=lp2.astart.com%9100 :if=/usr/libexec/hpif :of=/usr/libexec/hpof No \ at ends of lines Tags can be more than 2 characters lp now can specify remote printer and host, as well as port The form host%port opens a connection to a port on the remote device - allowing direct access to printer (for PJL status reporting) server flags entry as used only by lpd serverNb'Zb~ r(  :New Printcap GuidlinesPut connection or other information used by all LPRng programs in global printcap entry Put server only information in printcap entry AFTER the general one lp:lp=lp@lprng.astart.com # lpd only lp:lp=/dev/lp :sd=/usr/local/spool/lp ...0QP0 % ln LPR Client~Takes a list of files, or input from STDIN, and generates a control file, transfers the control file and data files to the LPD server WARNING BSD/LPR used to write control and data files to the spool directory, requiring SUID ROOT permissions LPRng uses a network connection, and takes extreme precautions when reading files; by default, LPRng s LPR runs as a user program The dreaded LPR -r (remove after printing) is present, but has been tamed6ee,ZfFormatsWhen a file is submitted for printing, LPR makes a copy of the file and sends it to the LPD server Before printing the file, additional processing on the file may be needed GIF file may need to be rasterized Different types of files may need different types of processing The processing is specified with a format indicator LPR default format is f (text format?) LPRng allows explicit format specification lpr -F x# #c)3> %Formats and FiltersFilters are used to process files before sending to the printer Filters are specified in the printcap file as xf entries where x is the format if=/usr/local/lib/filter/ifhp vf=/usr/local/lib/filter/ifhp Some printers require some form of initialization to be done at the start and end of a job The of filter is used to do this processing as well as process banners or job separators generated by the lpd server More on filters later L<n<b] 7mo Binary FilesSome files do not require any modification before being sent to the printer (binary files) The l(literal) format is used to indicate such files Too many people confused 1 and l so LPR uses -b (binary) to specify literal format lpr -b /tmp/binaryfile Just to make life miserable, some PC based LPR clients decided to use v format in the control file for binary files& Sigh& The if filter is used to process literal files, but is invoked with a -c option /usr/local/filter/ifhp -c _M3F8@P !Destination Printer Specification$The LPR -Ppr option explicitly specifies the destination printer If not specified, the PRINTER environment variable sets the default printer; if there is no PRINTER environment variable, the first one in the printcap file is used Note that in LPD/BSD that the LPR client always transfers the job to the local LPD server, which stores it in the spool queue This led to the horrible  set symbolic link and  remove after printing options which have been exploited in the past to do horrible things (but not by your users, right?).,  ;nkLightweight ClientsThe standard BSD implementation requires the printing clients to transfer jobs to a LPD server running on the local host. The local LPD server then will transfer jobs to remote hosts LPRng implements lightweight clients, which will simply transfer jobs directly to remote hosts, eliminating the need for a server running on the local host. This also reduces file space requirements on the local host. If the destination is a printer that implements RFC1179, then you never have to store the print files=p~LPRng Printer Name Conventions'lpr -Ppr@host printcap file is not searched, network connection and default values are used lpr -Ppr printcap file searched for entry lpr PRINTER environment variable used as printer name if no PRINTER environment variable, use first entry in printcap if no printcap, use default printer value ~N !N ! E j  Extensions^If the printcap lp entry is lp=pr@host, this corresponds to or :rm=host:rp=pr: use RFC1179 protocol to transfer files For total abuse, you can use lpr -Phost%port/direct This opens a TCP/IP connection to the remote port on the host and transfers the input files directly You really should use netcat if you want to do this: nc -d host -p port filesP'P'  "ILPR Client and FiltersuBy default, LPR simply copies files to the LPD server The lpr_filter option requests LPR to run the filters on the files before sending to server Allows localized processing and system depending hacks to be done to jobs before sending them to the spooler Very handy when you have vintage software whose output needs to be massaged, and uses hardwired paths to executables $v: 2:9qhFilters and Job ProcessingWhen a job is selected for printing, the LPD server examines the control file for data file format information fdfA001astart4 format f The printcap information is checked for a format f filter program The data file is piped into the filter program and the output is then sent to the printer device If the printer is connected via a simple TCP/IP connection to a port, I.e. - HP Jetdirect port 9100, then LPD/BSD requires special filters to open connection LPRng does this using lp=host%port, simplifying operation and filters tremendously@pZZZpP  D;sm$Basic Filters - Text TransformationsdA filter is given a print file on STDIN and produces output for the print device on STDOUT The most basic filter action is to translate LF to CF/LF combinations; most antique (vintage) printers require this Then we need to expand tabs Then we better look for sequences of \b (backspaces) and overstrikes, and replace them with the right control sequencestv$LPRng s lpf filter$ Extremely simple filter that will do LF to CF/LF expansion, tab expansion, and most simple printing operations such as inserting NULLS after page eject NULLS? yes, sometimes you need to do this to pause long enough for the printer to do a form feed& Sigh When used as the OF filter, will expand a short banner string (single line) into a very nice full page banner This type of thing is used less and less these days, but periodically there are requests for this antique.6jjuqPostScript Printer FiltersMany times PostScript printers are used to print text files; the IF filter should detect a non-PostScript file, and invoke a Text to PostScript translator Some printers stack paper in back to front order; the IF filter should try to do page reversal If the destination printer has PJL support, you better insert the various PJL/PCL strings to reset the printer and put it into PostScript mode. Did I mention getting page counts? And accounting? and storing this in an accounting file? And checking that the person has permission to print?vr LPRng s psfilter  This filter tries to handle most common PostScript printer problems it sends reset sequences it gets status information and produces error messages it even logs messages returned during job printing, such as the PostScript emulators error messages it gets the value of the page counter from the printer It is incredibly paranoid about file formats and tries really hard to make sure that the job being sent is PostScript Yes, it supports Tagged Binary Communications Protocol(DZZDws APSFILTERftp://sunsite.unc.edu /pub/Linux/system/printing/aps-491.tgz author Andreas Klemm andreas@knobel.gun.de co-author Thomas Bueschgens sledge@hammer.oche.de Very nice package that valiantly tries to determine the format of the input files (uses UNIX file utility) and then passes them through the appropriate filters. One of the nice features of this package is that it will even use GhostScript to produce output for a non-PostScript printer Strongly recommended for those desperate situations where run time and file space is no object but  User Proofing is 0F(F     (  LPRng APSFILTER Shameless copy of original APSFILTER, but done in Perl Closes some minor security loopholes and deals with error conditions better Available on the LPRng web site,2] xt&LPRng s ifhp Filter$ Designed to handle HP printers (PCL/PJL) Totally shameless rip-off of the very good JetAdmin filter set produced for Solaris/SunOS The wide variety of HP printers makes it difficult to have a single filter automatically handle all situations, but the ifhp filter tries hard Various flags and options allow it to handle all known HP printers that have published PPD files with PJL information (I hope!) Switches between PCL and Postscript, has a text to PostScript converter ,S HP Printer ProblemsDifferent implementations of printer firmware have different bugs Strange (to HP) combinations of duplex, reverse, and landscape can cause catestrophic failure and require power up initialization Power save mode sometimes requires physical intervention when it turns on TCP/IP connections are left open and do not terminate correctly - you cannot connect to printer and need to power up Other manufacturers have equally nasty problems Simple Parallel Port Printer]# parallel port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp lp=/dev/pr if=/usr/libexec/lpf The sh suppresses banners, mx#0 allows unlimited size files. The printer device (/dev/pr) is opened write only by default. The lpf filter will do LF to CF/LF translation, expand tabs, etc.... This is about as simple as you can make a printcap entry<d dR" 6)g %PS, PJL, or PCL Parallel Port Printer# parallel port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp lp=/dev/pr # For PostScript printer use psfilter package if=/usr/libexec/psif -Tstatus=off # For PJL, PCL printer use CTI-ifhp package if=/usr/libexec/ifhp -Tstatus=off The status=off flag suppresses the filter from getting printer status, as the parallel port is write only The psif and ifhp filters will do Text to PostScript or PCL conversion, and will detect PostScript or PCL files See the psfilter and CTI-ifhp documentation for detailsN O.#,#p" $rf  Simple Serial Port Printerd# serial port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp :rw:lp=/dev/ttya :sy=9600 -echo -crmod -raw \ -oddp -evenp pass8 cbreak ixon if=/usr/libexec/lpf The rw flag opens the serial port read/write The sy (stty) option sets the characteristics of the serial line Any error messages from the printer will be passed to the LPD server for action*      *#PS, PJL, or PCL Serial Port Printer# serial port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp :rw:lp=/dev/ttya :sy=9600 -echo -crmod -raw \ -oddp -evenp pass8 cbreak ixon # For PostScript printer use psfilter package if=/usr/libexec/psif # For PJL, PCL printer use CTI-ifhp package if=/usr/libexec/ifhp Since the serial port is Read/Write, the filters can query the printer for status and use this to control various operationsN }.,}      ~$PS, PJL, or PCL Network Port Printer# serial port printer, no banner lp:sh:mx#0:sd=/var/spool/lpd/lp :rw:lp=prname%9100 # For PostScript printer use psfilter package if=/usr/libexec/psif # For PJL, PCL printer use CTI-ifhp package if=/usr/libexec/ifhp The LPD server will open a connection to port 9100 on the printer, and the filters will direct their output to this port This is extremely effective for network based printersN U.,( "  yu)Part 3 Managing Print Spooler Operations**zPrinters, Queues, and Status,All jobs handled by LPD are placed in a print queue, and then sent to a printer Queue State enabled - accepting jobs disabled - not accepting jobs Printer (Destination) State running - actively printing job idle - waiting for job to print stopped - administratively stopped from printingH\9|\9|{ LPQ - StatusYThe LPQ command is used to request status about a printer or print queue status LPD/BSD has two forms of status - short and long LPRng adds verbose (which is REALLY verbose!) When a LPD server gets a status request, it replies with status for the local queue, and then if the queue is forwarded to a remote printer will forward the LPQ request. If the printer is local to the LPD server, the server will also report printer activity You can restrict the status to only selected jobs by adding keys to the request command. These usually are the user, job id, or host from which the jobs originated.ZZZ Short Statuslw4@astart4 0 jobs  Long StatusPrinter: lw4@astart4 'Hp Laserwriter' Queue: no printable jobs in queue Status: server finished at 07:32:46 Filter_status: ifhp Initial page count 60744, final 60746, Total pages = 2, elapsed time 73 secs at Oct 29 07:32:46 Rank Owner/ID Class Job Files Size Time error papowell@astart4+425 A 425 ERROR: IO error 'Broken pipe', at 10:18:48 P XHj JVerbose StatusCPrinter: lw4@astart4 Comment: Hp Laserwriter Printing: yes Spooling: yes Queue: no printable jobs in queue Status: printing 'papowell@astart4+576', start, attempt 1 at 11:28:18 Status: opening 'astart14.astart.com' at 11:28:18, attempt 1, timeout 10, grace 0 at 11:28:18 Status: accounting at start 'papowell@astart4+576' at 11:28:18 Filter_status: ifhp Initial page count 60744, final 60746, Total pages = 2, elapsed time 73 secs at Oct 29 07:32:46 Filter_status: ifhp Initial page count 60744, final 60746, Total pages = 2, elapsed time 73 secs at Oct 29 07:32:46 $DPC" S  )H$H Job: papowell@astart4+425 status= error Job: papowell@astart4+425 size= 4124 Job: papowell@astart4+425 time= 05:58:45 Job: papowell@astart4+425 error= IO error 'Broken pipe', at 10:18:48 Job: papowell@astart4+425 CONTROL= - Hastart4.astart.com - Ppapowell - J/tmp/a - CA - Lpapowell - N/tmp/a - fdfA425astart4.astart.com - UdfA425astart4.astart.com Job: papowell@astart4+425 HOLDFILE= - active_time 0 - attempt 1 bP$<P#   ! = /  B 6Status ReportsMajor weakness of print spoolers is lack of diagnostic information and job status LPRng assumed that most users would require information allowing them to fix paper jams, etc LPRng based filters generate status reports which are kept in a file in the spool queue. This information can be viewed using the lpq command The lpd server keeps a log file of status information as well This log file is also viewed using the lpq commandbRW} ]Alternative Status ReportsSeveral CGI scripts have been developed which allow you to get LPQ status via a web browser See the LPRng web site for examples You can write some very simple Perl Scripts that can open a connection to the LPD server and get status information. See the LPRng web site for examples Z\$u$\$u$>d5Y|LPQ and SecurityOne of the major flaws in the RFC1179 protocol is that there is no way to identify the originator of a LPQ request. Thus, there is usually no restriction on the data or information returned. Since LPQ returns the host and user which submitted a job, this is a very good way to find the names of machines and hosts to probe when doing hacking attacks Firewalls should disable port 515 for this reason in both directions, as you do not want your folks hacking other systems, right?}LPRM - Job RemovalPThe LPRM program generates an RFC1179 job removal request lprm -Plp 179 (remove job with ID 179) lprm -Plp john (remove FIRST job with user name john) lprm -Plp all (remove ALL jobs - LPRng only) WARNING most LPD/BSD systems will accept any request from any system to delete a job. The wimpy  requests must originate from port 721-731 restriction can be circumvented with a PC and a simple hacking program. Sigh& $:-~LPC - LPD/BSD Printer ControlThe LPD/BSD system usually provides a LPC program that is used to control the LPD activities. Traditionally it does this by reading and writing files in the spool queue, and changing permissions on directories The details of this are too ugly for public discussion. You really don t want to know. This architecture means that you must log onto the server as ROOT to control the print queues LPC - LPRng VersionLPRng extended the RFC1179 protocol to add additional commands for LPD server administration The LPC program generates the appropriate request and sends it to the server The server will then carry out the command WARNING The observant and suitably paranoid administrator will have noted the horrible security loophole that this has opened Don t fret - we have fixed this$mLPC - Basic Commandsstart - enable queue for printing and start unspooling jobs stop - disable unspooling of jobs enable - allow jobs to be spooled disable - prevent jobs from being spooled status - show print spool queue status This is simply same as LPQ, but different format ,,  LPC - LPRng Additional Commandsabort - kill off the filters doing printing and don t restart printing Used when a job gets hung up, the filter is unable to process it, or there is something very strange Job can then be removed using LPRM up - short for enable + start down - short for disable + abort restart (BSD) or kill (LPRng) - kill off the filter and then restart it:GG',LPC - LPRng Job Control )move - move job to another print queue lpc -Plp move lp2 john move jobs in the lp queue to the lp2 queue which were submitted by user john redirect - redirect all newly spooled jobs to another print queue lpc -Plp redirect lp2 jobs submitted to the lp queue will be placed in the lp2 queue X''A'!|%(LPC - LPRng Server Control&lpc reread the lpd server will reread the printcap and configuration information files lpc -Ppr printcap display the printcap information that the server has for printer pr lpc -Ppr debug 1,database,receive set real time debugging information for printer lpc -Ppr debug off terminates debuggingz LD"X LD"X  %  /I&Print Queue Job Priority and Selection<This is explicitly undefined by RFC1179 Most print queues run on a first come, first serve basis LPRng Extensions lpr -cxray will give job priority X (A is lowest, Z is highest), and put it in the xray class lpc class xray restricts printing to only jobs in class xray lpc class off allows all classes to be printed Brr a I* ,LPC TOPQ CommandThe LPC topq (top of queue) command will put a selected job at the top of the queue lpc -Plp topq john will put the first job of user john at the head of the list $U?>IDAdding A Printer !Adding A Printer - Printer ChecksMake sure printer works You would be surprised at how many problems during installation are traced to non-working printers Check out network connection by using diagnostics or direct connection via lpr lpr -Praw@ipaddr /tmp/log General debugging level is 1, and network operation tracing is enabledDc'Gd&F>dLServer Operation TracingFor all operations except job reception and printing, trace output is sent to STDERR Example: lpd -D1,network -F 2>/tmp/log The -F flag causes the server to stay in the foreground, and allows the server to be easily terminatedD^g_f,]l!Debugging Spooling and Unspooling When a spooling or unspooling operation is carried out by the LPD server, the printcap entry is checked for debugging flags (db) and log file (lf) entries lp:sd=/usr/spool/lp:lf=log :db=1,receive,network:max_log#1000 If the log file exists, log and trace information is appended to it When the log file exceeds the max_log (Kbyte) limit, it is truncated to the min_log size This technique allows tracing of job flows through the system in a simple and easy to follow manner^?~ ? 1 &  Fixing ProblemsLMany of the times you will discover that LPRng balks at accepting a job from some other spooling software due to non-compliance with RFC1179 or what can be considered a security risk You can force LPRng to accept the job, but it will insist on  sanitizing it before using it To do this, set the fix_bad_job value in the /etc/lpd.conf file to true. # fix bad control files and data file names # fix_bad_job@ fix_bad_job (_H_HP)}VInstalling and Setting Up LPRng  Insta      !"#$%&'()*+,-./0123456789:;<=>?@ABCDFGHIJKlling LPRng Get the source code from a distribution site ftp://ftp.astart.com/pub/LPRng/ Get the distribution (LPRng-version.tgz) Get the filters as well (LPRng_filters-version.tgz) You will need ANSI C compiler, GNU Make, GNU Zip (gzip) Uncompress and tar the distribution gunzip -c LPRng-*.tgz |tar xf - Configure, compile, and install the code (see documentation for details configure; make clean all install Install the default configuration and permissions files make default -Z}Z\Z ZHZ"Z8Z Z-\ H"8 7  4%LPRng's checkpc Program(  NThe checkpc program reads the printcap files and checks the system spool directories for consistency with the printcap information When invoked by root, checkpc -f will change permissions and create the required files and directories It can also be used to remove old files and truncate log files. See the checkpc man page for details6O b G "Setting Up LPRng on BSD Systems +You must first stop the current LPD ps -axu | grep lp get the LPD PID kill pid Run checkpc to fix the permissions, etc... checkpc -f Edit the rc.local or other rc file to start LPRng lpd if [ -f /etc.../printcap ] ; then echo -n ' lpd' ; /usr/local/bin/lpd; fi; Start lpd and test the system lpd$.+ 6L$+ 6L#    Setting Up LPRng on Solaris/SVR4 The default printing system on Solaris is the lp print spooler. You must first disable this, as well as the various network print services lpshut; nlsadmin -r lpd tcp; nlsadmin -r lp tcp; Check the /etc.../printcap file, then run checkpc to fix the various system files checkpc -f Next, modify the rc files to start lpd instead of lpsched Reboot the system Check the system using lpq, lpr, etc....1R u.\1 8 u-[    ) LP System EmulationDWhen the various LPRng clients are invoked using the LP system names, they will emulate the lp system behavior. This can be done by making symbolic links to the various files: ln -s lpr lp; ln -s lprm remove; ln -s lpq lpstat; This allows programs which require the LP print system to directly use the LPR print facilities:3`3`FR bLoad Sharing and Printer PoolsPrinter Pools and Load Sharing}The LPRng software can do load sharing between a set of printers by having a master spool queue and a set of servers printersuLoad Sharing Details@The jobs in the master queue are sorted by priority When a slave printer is idle, the top priority job in the master queue will be moved to the I dleslave printer queue Example Printcap Entries: master:ss=serv1,serv2 :sd=/var/spool/lpd/master serv1:sv=master :sd=/var/spool/lpd:lp=/dev/pr1:... serv2:sv=master :sd=/var/spool/lpd:lp=/dev/pr2:& Jobs can also be spooled directly to the slave printer queues as well < GGL    N:Load Sharing Details (Cont d)The LPD server tries to use slave printers in round robin order If a slave queue is disabled, no new jobs will be placed in it Slave printers must not perform spooling or load balancing will not work correctly Bounce Queues Bounce Queues and FiltersSometimes it is necessary to perform filtering actions on jobs and then send the job to another print queue This is common when dealing with network printers, or when you want to have special actions performed by a special queue Reasons for BQ Use{#do 2 pages per page up # WRONG Method pr2up:lp=realpr@host:if=ps2up #real printer realpr:lp=pr%9100:sd=/sd:if=ifhp The pr2up printer simply passes 'f' format jobs forwards all job to the real printer, and does not pass the data files through the ps2up filter LPR will look at the pr2up entry and decide to send jobs directly to realpr, so they will never get put into the queue(tt- -Correct BQ Use#do 2 pages per page up # CORRECT Method pr2up:lp=pr2up@host:if=ps2up :sd=/sd/pr2up:bq=realpr@host #real printer realpr:lp=pr%9100:sd=/sd/realpr:if=ifhp The pr2up printer now has an entry that will force the LPR program to send the job to the pr2up queue LPR now look at the pr2up entry, sees the bq entry, and will pass the data files through the ps2up filter The output of the filter will then be sent to the realpr queue(/ oAlternate BQ Printcap #clients see this pr2up:lp=pr2up@host realpr:lp=realpr@host #lpd server adds this pr2up:server:if=ps2up :sd=/sd/pr2up:bq=realpr@host realpr:server:lp=pr%9100:sd=/sd/realpr :if=ifhp (  * Update Data File FormatsAfter the data file has been modified, the original format may be incorrect Example: you want to change the original f format to l so that no further modifications are made The new format entry does this pr2up:server:if=ps2up :sd=/sd/pr2up:bq=realpr@host :new_format=flvl New format entries are pairs of characters; the first is the original format and the second is the new format after processing Note that the control file is changed irregardless if there is a filter for the particular format LFFbEditing Control Files@A bounce queue can also be used to modify a control file. This is commonly needed when either the format of the control file is unsuitable for the destination printer, or when data file formats need to be modified as a result of filtering prgif:lp=realpr@host:sd=/sd:rf=/gif2ps :edit_cf=/usr/lib/cf_editor The cf_editor filter is given the control file and can edit as well. Note that this can include actions such as removing job files. This horrible kludge is needed when dealing with vintage software that produces control files incompatible with newer network printers:D D   Routing Jobs to Spool Queues RoutingISometimes it is necessary to dynamically decide the spool queue to use based on information in either the control file or the format of data files For example: large, medium or small jobs For example: special color processing This is not the same a load sharing, which makes decisions based only on the availability of printers IRouting Filterelp:lp@host lp:server:sd=/var/spool/lp :router=/usr/lib/filter/router The LPD server will put jobs into the spool queue When unspooling them, it will pass the control file through the routing filter The output of the routing filter (on its STDOUT) will be the new printer name You can combine Bounce Queues and Routing, but the results are not predictable(F F ~W Host Specific Printcap Entries  Host Specific Printcap Entry UThe oh (only for this host) entry restricts which host will use a printcap entry Example: lp:oh=*.astart.com,130.191.163.0/24 :lp=lp1@server lp:oh=*.sdsu.edu,130.10.0.0/24 :lp=lp2@server Values are lists of GLOB expressions or IP address and mask values If the host name or address matches, then the printcap entry can be used by the hostLZcTcB   ~ IPart 4 Horrible Problems Permissions Authentication Accounting DefaultsJJ PermissionsyAdministrators may need to restrict access to various printing facilities for policy or financial reasons Most print spooler systems have some sort of mechanism for restricting access Problem with RFC1179 based systems is sparse information available to make decisions You have the endpoint of a connection You have a request type Some requests have a user name, others do not$ m mLPRng Permissions File%Based on Packet Filter concept File contains a list of ACCEPT or REJECT entries Requests are test against entries until a match is found If the result is ACCEPT then the operation is allowed If the result is REJECT then the operation is not allowed You can also put in default ACCEPT or REJECTExample1# Accept LPR requests only from 10 sn ACCEPT SERVICE=R REMOTEIP=10.0.0.0/8 REJECT SERVICE=R # Alternative to the above DEFAULT ACCEPT REJECT SERVICE=R NOT REMOTEIP=10.0.0.0/10 # Example 2O# Let only root and admin on server # have LPC control permissions ACCEPT SERVICE=C SERVER REMOTEUSER=root,admin REJECT SERVICE=C # LPRM Remove requests only from # same host and user as spooled them ACCEPT SERVICE=M SAMEHOST SAMEUSER # Allow test on tester to remove files ACCEPT SERVICE=M REMOTEUSER=test REMOTEHOST=tester.astart.comPPEAuthenticationRFC1179 does not provide any authentication methods Since it is trivial to forge network level packets, etc, this can be a major problem LPRng solution was to provide a general purpose method of adding authentication Currently, PGP and Kerberos authentication is supported,]  AuthenticatorA connection is established, a special AUTH request is sent Part of the AUTH request is the supported authentication methods The reply contains the chosen method to be used Each end of the connection starts an authenticator program which will then perform the various authentication and/or encryption to be done The authenticator programs accept information from the LPRng programs, encapsulate it, and then transfer it to the other endp@ Example: PGPTo use PGP authentication, each LPRng server will require a secret key, and each LPRng user will need the public key of the server Each user will need to provide his public key to the server as well Each RFC1179 request and/or reply is signed with the appropriate key, identifying it as originating from the correct endpoint The use of a Public Key Server to supply user and/or LPRng server keys greatly simplifies this operation If you are really paranoid, you can also encrypt all requests and data file transfers >!,$Forcing AuthenticationThe permissions file is used to specify that some form of authentication must be done ACCEPT SERVICE=R,M,C AUTH=pgp,kerberos REJECT SERVICE=R,M,C NOT AUTH 0VFVE,p  AccountingPDon t spend dollars on counting pennies Levels of accounting Numbers of jobs submitted completed correctly? Incorrectly? Numbers of pages used how do you find this out Z="="Job Level AccountingzPrintcap af entry specifies the accounting file If the af file exists, then a message is written to it at the start and successful conclusion of each job Multiple starts with no conclusion indicates job failure or somebody waited until next to last (blank) page came out and turned off printer& Students& I mean users& become very sophisticated at this type of thing. Less common today given large numbers of printers usually available for use$$ -Page Level AccountingUsually required by some sort of administrative bean-counter who also counts individual paperclips and pencils Also, may be useful when costs need to be allocated on a project or account basis Most Laser based printers usually have a page counter built into them, so that you can determine the number of pages it has printed for servicing purposes If this pagecounter is accessible via the network interface, then you can get the value before and after a job, and determine the exact number of pagesf Accessing Page CountersThere are no standard methods to do this Each model of printer appears to have a different method Even documented methods may not work on printers High throughput printers try to do job buffering, and do not report the correct page count You need to wait for TRUE END OF JOB This really slows down operation Page Counters Lie Usually value is stored in EEPROM on printer You may get the stored EEPROM value, not currentl)j\F^)j\F^Configuration and Defaults The /etc/lpd.conf file can be used to set values of configuration variables Example: # fix bad control file information fix_bad_job The file format is identical to the printcap file, but you do not need the leading colons (:) and each entry must be on a single lineVV/ E/>  YConfiguration VariablesNThere are zillions of configuration variables used by LPRng. Most of these were created in order to configure LPRng operation to be compatible with very strange spooling systems or very nasty hardware Unless you have problems, you can safely ignore them. Really. If you have problems, then you better start reading the LPRng HOWTO.>63,Part 5 Multi-platform Printing and Gateways--Print Job Preparation>When a print job is generated, the information must be in a format suitable for the destination printer. There are two general approaches to the problem: Do the conversion as late as possible in the translation process (late binding) Do the conversion as early as possible in the translation process (early binding) Late binding is favourable to the print generation program, as it does not need to know what specific printer will be used Early binding allows the print generation program to take advantage of any special options that are available on a particular printer <ZZZL LP / LPD Uses Late BindingXThe LP and LPD/BSD print spoolers were designed to use the late binding model The data file format indications were supposed to indicate the type of file and the type of conversion needed The use of PostScript and PCL largely eliminated this need Most UNIX programs cannot take advantage of printer capabilities that are well known to the user YZYLPR -Z OptionsJLPR overcomes some of these problems by allowing users to specify options that are passed to the print filters lpr -Zupperbin,duplex filename The convention is that filters will scan the -Z options for values and use these values to control printing Since these are printer specific, the options can be different for each printer :oo,o  MS Windows/MAC Use Early BindingSystem configuration information records the available printers, I.e. those which have graphic to printer format converters available to the user  Early Binding and Job Generation In order to print, the program needs to select a system configured printer Configuration information includes the format of information to be supplied to a translation program The translation program Additional parameters for the translation program The information to be printed is processed by the translation program The output of the program is then sent either directly to a hardware device via another program or put in a spool file Spool files are then transferred either to the hardware device or a network printer6LLDriversIn the MS Windows environment, Printer drivers are actually several things Graphic to print file conversion programs print file to hardware interface programs This model leads to much confusion, as when errors occur it is difficult to determine if the problem is in the graphic to file conversion part of the driver or in the file to hardware device part of the driver This is even more painful when you do network printing, as now you have to distribute drivers to all printer users6LTGLTGNetwork PrintingIf you plan to print to a non-MS based print spool, choose a print driver which is as vanilla as possible Try to generate PostScript or PCL Try to generate PostScript Level 2 or 3 and the Document Structure Standards$jojo Win95 and LPR/BSD Print SpoolersWhile Win95 does not directly support LPD print spooling, there are several shareware packages that provide this. Recommended: Windows LPR Spooler Version 4.1 Author: Susanne Heil, EDV-Vertieb ftp://ftp.astart.com/LPRng/wlprs41.exe Implements a very good LPR/BSD RFC1179 compliant print server (LPR) and status monitor (LPQ) Uses Winsock interface, and works with Microsoft, FTP, and several other TCP/IP Winsock.dll:sx(b  AlternativesThere are other print spoolers available, as well as some that operate with DOS See the LPRng FTP site ftp://ftp.astart.com/LPRng/WINDOWS Other utilities are there as well:g#"g"#>X+Why Use Wslpr?If you want to have a printer pool, you will be using LPRng or some other UNIX based server (unless you have big bucks) It runs over TCP/IP and can go through firewalls 6pWhy Not JetDirect? Be aware that using different network protocols and the JetDirect software has resulted in locking up most HP printers Most of the software testing was done in situations where there as little if any non-MS network activity, it appears 8  Windows 98Has support for RFC1179 Unfortunately, it appears to have problems when connections time out and periodically jobs just vanished There may be a new version out Real Soon Now Stick with 3rd Party Spoolers!Windows NT Server LPR/BSD GatewayWindows NT supports remote LPR printers. It will translate print jobs spooled to an NT server by creating a control file and sends the data file Unfortunately, there are some minor problems with control file format, missing fields, etc., but these are easily handled by most LPD servers NT also ACCEPTS print jobs for printing This means you can use an NT server for gatewaying to the NT printer environment Beware that there are security holes lurking in all of this q \ LPRng on NTLPRng is being ported to NT Will support the basic LPRng functionality Filters are very messy under NT cannot easily fork processes memory leaks in NT DLLs H+ 7+ 7$-eNovell Printer LPD/BSD Gateways Novell also supports a LPD/BSD gateway facility If your Novell server has TCP/IP support installed and active, you can set up a print queue on a Novell server that will forward jobs to a LPD/BSD server. You can also send jobs to the Novell server and it will print them Samba, SMB, and LPD/BSD GatewaysIf you do not want to install Wslpr on you Win95 system, or do not want to set up an NT Server, the Samba system provides an alternative Samba implements SMB over NetBUI and IPX While it is usually used for file servers, Samba also supports a very nice LPD/BSD gateway facility While not being personally familiar with the details of Samba, reports have indicated that it was trivial to set up and get working "It worked first time, out of the box." Kurt Reynolds<,/ PCNFS and PCNFSD Gateways to LPDPCNFS by Sun Microsystems implements NFS (Network File System) support for PCs. As part of this support, Sun provides source code for the PCNFSD server which is used to authenticate PCs and provide print services via the host that the server runs on. The LPRng distribution has a set of patches for the PCNFSD distribution that interface the PCNFSD server to the LPRng print faciltities. They also close a couple of minor security problems involving unchecked string lengths and stack overflows>f nApple and LPR/BSDThere are several products for Apple NFS support that use the PCNFSD facilities to provide print services. Several people have demonstrated this working very well LP (Solaris) to LPD PrintingSolaris 2.5 System Administration Guide, Vol 2 Chapter 48, Example - Adding Access to a Remote Printer - remote@host # define remote system as BSD, and force connection to be dropped when idle, 1 minute timeout between connections lpsystem -t bsd -T 0 -R 1 host # printer is the lp name for the print queue lpadmin -p printer -s host!remote \ -T unknown -I any accept printer; enable printer lpadmin -p printer -D "remote printer" lpadmin -d printer # make default printervfj p+     (   N##Monitoring Printer Status with SNMP"One of the weak points of distributed print spooling is monitoring printers for error conditions One of the benefits of network printers and the TCP/IP protocol is that you can query them for status from several different locations One of the nasty problems is that most printers do not provide good status indications The good news is that printer vendors are now implementing SNMP agents in most of their printers that allow SNMP managers to easily access printer status The bad news is that each vendor has different extensions and facilities Perl, Tcl/TK to the Rescue?$8I have seen several nice printer monitoring systems developed using Perl and Tcl/TK. Unfortunately, the most elegant ones depended on commercial SNMP managers such as OpenView and SunNetManager. Several interested parties have been working on a simple shareware monitor; watch the LPRng ftp site for detailsbCZ XWhere Do I Get Help?1. If it is a commercial system, try your vendors support group 2. comp.peripherals.printers news group has a large number of discussions about printers and spoolers 3. lprng@iona.ie mail list for LPRng related issues Send mail to lprng-request@iona.ie with subscribe in the body 4. AStArt Technologies provides commercial support for LPRng and offers network consulting and management services. (Shameless plug) Good Luck!4   -VSummaryIf you are going to run printers in a multiplatform environment, you will need to deal with the issues of gateways The LPD/BSD facilities, as defined by RFC1179, provide a common platform for use. While by no stretch of the imagination can this be regarded as an optimal solution, it works well in an environment with a large number of printers which need central mangement and control,%;  Questions and Answers/  0(  l  C D^@   l  C ^p  H  0޽h ? ̙33  @$(  r  S _@   r  S d_p  H  0޽h ? ̙33  P$(  r  S _@   r  S $`p  H  0޽h ? ̙33   ( ` l  C DV@   l  C Vp  H  0޽h ? ̙33r *0T U W Y p*[ b &&#TNPPp0D v & TNPP &&TNPP    - "-- !-- "-&Gy& - Times New Roman- .l2 CPrinting LISA 98 (c) 1997-1998 Patrick Powell http://www.astart.com                 .&y&  . 2 e0 .& "--L$$ZH7((7HZv(7HZfxvZH7(xf--&& "--L$$` O >/##/> O ` p #/>O``rp`O>/#r ` --&--yPH-- "Arial- .2 wManaging( . .<2 c#Network Printers and Print Spoolers#"        .--q1 -- "ArialT- .2 5kPatrick . .2 5 Powell  ."Arial- .2 ^`papowell. . 2 ^@. .2 ^astart . . 2 ^'.com. .2 Astart  . .H2 + Technologies, 9475 Chesapeake Dr., Suite D   . .$2 _San Diego, CA 92123. .+2  619-874-6543 http://www. . .2 ;astart  . . 2 ~.com .--"Systemn-&TNPP &lprng-doc-3.8.Arc2/updateheader0000755000175000017500000000063310554776205013356 00000000000000#!/usr/bin/perl -spi if( $found and /^$/ ){ print < LPRngLogo LPRngTool EOF $found = 0; } elsif( m, [B<-p>] [B<-g>] [B<-s> I] [B<-t> I] [B<-P> F] [B<-i> I] [B<-o> F] [B<-S> I] [B<-I> I] [B<-x>] [B<-f>] [B<-N>] [B<-q>] [F =head1 DESCRIPTION B is a Perl script that creates index data for DocBook XML or SGML files. =cut use Getopt::Std; $usage = "usage: $0 [options] file Do 'perldoc $0' for documentation.\n"; ( $version = '$Revision: 1.8 $' ) =~ s/^\$[R]evision:\s*([^ ]*)\s*\$$/$1/; =head1 OPTIONS =over 5 =item B<-p> Link to points in the document. The default is to link to the closest containing section. =item B<-g> Group terms with IndexDiv based on the first letter of the term (or its sortas attribute). (This probably doesn't handle i10n particularly well.) =item B<-s> Name the IndexDiv that contains symbols. The default is 'Symbols'. Meaningless if B<-g> is not used. =item B<-t> I Title for the index. =item B<-P> F Read a preamble from F. The contents of F will be inserted before the EindexE tag. =item B<-i> I The ID to use for the EindexE tag. =item B<-o> F Output to F. Defaults to F. =item B<-S> I Scope of the index, must be C, C, or C. If unspecified, C is assumed. =item B<-I> I The implied scope, must be C, C, or C. IndexTerms which do not specify a scope will have the implied scope. If unspecified, C is assumed. =item B<-x> Make a SetIndex. =item B<-f> Force the output file to be written, even if it appears to have been edited by hand. =item B<-N> New index (generates an empty index file). =item B<-q> Run quietly. =item B<-V> Print version number and exit. =item F The file containing index data generated by B with the DocBook HTML Stylesheet. =back =cut die $usage if ! getopts('Dfgi:NpP:s:o:S:I:t:xqV'); $linkpoints = $opt_p; $lettergroups = $opt_g; $symbolsname = $opt_s || "Symbols"; $title = $opt_t; $preamble = $opt_P; $outfile = $opt_o || '-'; $indexid = $opt_i; $scope = uc($opt_S) || 'ALL'; $impliedscope = uc($opt_I) || 'ALL'; $setindex = $opt_x; $forceoutput = $opt_f; $newindex = $opt_N; $debug = $opt_D; $quiet = $opt_q; if ( $opt_V ) { print "collateindex.pl $version\n"; exit 0; } $indextag = $setindex ? 'setindex' : 'index'; if ($newindex) { safe_open(*OUT, $outfile); if ($indexid) { print OUT "<$indextag id='$indexid'>\n\n"; } else { print OUT "<$indextag>\n\n"; } print OUT "\n"; print OUT "\n"; print OUT "\n"; exit 0; } $dat = shift @ARGV || die $usage; die "$0: cannot find $dat.\n" if ! -f $dat; %legal_scopes = ('ALL' => 1, 'LOCAL' => 1, 'GLOBAL' => 1); if ($scope && !$legal_scopes{$scope}) { die "Invalid scope.\n$usage\n"; } if ($impliedscope && !$legal_scopes{$impliedscope}) { die "Invalid implied scope.\n$usage\n"; } @term = (); %id = (); $termcount = 0; $quiet || print STDERR "Processing $dat...\n"; # Read the index file, creating an array of objects. Each object # represents and indexterm and has fields for the content of the # indexterm open (F, $dat); while () { chop; chop if /\r$/; if (/^\/indexterm/i) { push (@term, $idx); next; } if (/^indexterm (.*)$/i) { $termcount++; $idx = {}; $idx->{'zone'} = {}; $idx->{'href'} = $1; $idx->{'count'} = $termcount; $idx->{'scope'} = $impliedscope; next; } if (/^indexpoint (.*)$/i) { $idx->{'hrefpoint'} = $1; next; } if (/^title (.*)$/i) { $idx->{'title'} = $1; next; } if (/^primary[\[ ](.*)$/i) { if (/^primary\[(.*?)\] (.*)$/i) { $idx->{'psortas'} = &escape($1); $idx->{'primary'} = &escape($2); } else { $idx->{'psortas'} = &escape($1); $idx->{'primary'} = &escape($1); } next; } if (/^secondary[\[ ](.*)$/i) { if (/^secondary\[(.*?)\] (.*)$/i) { $idx->{'ssortas'} = &escape($1); $idx->{'secondary'} = &escape($2); } else { $idx->{'ssortas'} = &escape($1); $idx->{'secondary'} = &escape($1); } next; } if (/^tertiary[\[ ](.*)$/i) { if (/^tertiary\[(.*?)\] (.*)$/i) { $idx->{'tsortas'} = &escape($1); $idx->{'tertiary'} = &escape($2); } else { $idx->{'tsortas'} = &escape($1); $idx->{'tertiary'} = &escape($1); } next; } if (/^see (.*)$/i) { $idx->{'see'} = &escape($1); next; } if (/^seealso (.*)$/i) { $idx->{'seealso'} = &escape($1); next; } if (/^significance (.*)$/i) { $idx->{'significance'} = &escape($1); next; } if (/^class (.*)$/i) { $idx->{'class'} = &escape($1); next; } if (/^scope (.*)$/i) { $idx->{'scope'} = &escape(uc($1)); next; } if (/^startref (.*)$/i) { $idx->{'startref'} = $1; next; } if (/^id (.*)$/i) { $idx->{'id'} = $1; $id{$1} = $idx; next; } if (/^zone (.*)$/i) { my($href) = $1; $_ = scalar(); chop; die "Bad zone: $_\n" if !/^title (.*)$/i; $idx->{'zone'}->{$href} = $1; next; } die "Unrecognized: $_\n"; } close (F); $quiet || print STDERR "$termcount entries loaded...\n"; # Fixup the startrefs... # In DocBook, STARTREF is a #CONREF attribute; support this by copying # all of the fields from the indexterm with the id specified by STARTREF # to the indexterm that has the STARTREF. foreach $idx (@term) { my($ididx, $field); if ($idx->{'startref'}) { $ididx = $id{$idx->{'startref'}}; foreach $field ('primary', 'secondary', 'tertiary', 'see', 'seealso', 'psortas', 'ssortas', 'tsortas', 'significance', 'class', 'scope') { $idx->{$field} = $ididx->{$field}; } } } # Sort the index terms @term = sort termsort @term; # Move all of the non-alphabetic entries to the front of the index. @term = sortsymbols(@term); safe_open(*OUT, $outfile); # Write the index... if ($indexid) { print OUT "<$indextag id='$indexid'>\n\n"; } else { print OUT "<$indextag>\n\n"; } print OUT "\n"; print OUT "\n"; print OUT "\n\n"; print OUT "$title\n\n" if $title; $last = {}; # the last indexterm we processed $first = 1; # this is the first one $group = ""; # we're not in a group yet $lastout = ""; # we've not put anything out yet @seealsos = (); # See also stack. foreach $idx (@term) { next if $idx->{'startref'}; # no way to represent spans... next if ($idx->{'scope'} eq 'LOCAL') && ($scope eq 'GLOBAL'); next if ($idx->{'scope'} eq 'GLOBAL') && ($scope eq 'LOCAL'); next if &same($idx, $last); # suppress duplicates $termcount--; # If primary changes, output a whole new index term, otherwise just # output another secondary or tertiary, as appropriate. We know from # sorting that the terms will always be in the right order. if (!&tsame($last, $idx, 'primary')) { print "DIFF PRIM\n" if $debug; &end_entry() if not $first; if ($lettergroups) { # If we're grouping, make the right indexdivs $letter = $idx->{'psortas'}; $letter = $idx->{'primary'} if !$letter; $letter = uc(substr($letter, 0, 1)); # symbols are a special case if (($letter lt 'A') || ($letter gt 'Z')) { if (($group eq '') || (($group ge 'A') && ($group le 'Z'))) { print OUT "\n" if !$first; print OUT "$symbolsname\n\n"; $group = $letter; } } elsif (($group eq '') || ($group ne $letter)) { print OUT "\n" if !$first; print OUT "$letter\n\n"; $group = $letter; } } $first = 0; # there can only be on first ;-) print OUT "\n"; print OUT " ", $idx->{'primary'}; $lastout = "primaryie"; if ($idx->{'secondary'}) { print OUT "\n \n"; print OUT " ", $idx->{'secondary'}; $lastout = "secondaryie"; }; if ($idx->{'tertiary'}) { print OUT "\n \n"; print OUT " ", $idx->{'tertiary'}; $lastout = "tertiaryie"; } } elsif (!&tsame($last, $idx, 'secondary')) { print "DIFF SEC\n" if $debug; print OUT "\n \n" if $lastout; foreach (@seealsos) { # it'd be nice to make this a link... print OUT $indent, " ", &escape($_), "\n"; } @seealsos = (); print OUT " ", $idx->{'secondary'}; $lastout = "secondaryie"; if ($idx->{'tertiary'}) { print OUT "\n \n"; print OUT " ", $idx->{'tertiary'}; $lastout = "tertiaryie"; } } elsif (!&tsame($last, $idx, 'tertiary')) { print "DIFF TERT\n" if $debug; print OUT "\n \n" if $lastout; foreach (@seealsos) { # it'd be nice to make this a link... print OUT $indent, " ", &escape($_), "\n"; } @seealsos = (); if ($idx->{'tertiary'}) { print OUT " ", $idx->{'tertiary'}; $lastout = "tertiaryie"; } } &print_term($idx); $last = $idx; } # Termcount is > 0 iff some entries were skipped. $quiet || print STDERR "$termcount entries ignored...\n"; &end_entry(); print OUT "\n" if $lettergroups; print OUT "\n"; close (OUT); $quiet || print STDERR "Done.\n"; sub same { my($a) = shift; my($b) = shift; my($aP) = $a->{'psortas'} || $a->{'primary'}; my($aS) = $a->{'ssortas'} || $a->{'secondary'}; my($aT) = $a->{'tsortas'} || $a->{'tertiary'}; my($bP) = $b->{'psortas'} || $b->{'primary'}; my($bS) = $b->{'ssortas'} || $b->{'secondary'}; my($bT) = $b->{'tsortas'} || $b->{'tertiary'}; my($same); $aP =~ s/^\s*//; $aP =~ s/\s*$//; $aP = uc($aP); $aS =~ s/^\s*//; $aS =~ s/\s*$//; $aS = uc($aS); $aT =~ s/^\s*//; $aT =~ s/\s*$//; $aT = uc($aT); $bP =~ s/^\s*//; $bP =~ s/\s*$//; $bP = uc($bP); $bS =~ s/^\s*//; $bS =~ s/\s*$//; $bS = uc($bS); $bT =~ s/^\s*//; $bT =~ s/\s*$//; $bT = uc($bT); # print "[$aP]=[$bP]\n"; # print "[$aS]=[$bS]\n"; # print "[$aT]=[$bT]\n"; # Two index terms are the same if: # 1. the primary, secondary, and tertiary entries are the same # (or have the same SORTAS) # AND # 2. They occur in the same titled section # AND # 3. They point to the same place # # Notes: Scope is used to suppress some entries, but can't be used # for comparing duplicates. # Interpretation of "the same place" depends on whether or # not $linkpoints is true. $same = (($aP eq $bP) && ($aS eq $bS) && ($aT eq $bT) && ($a->{'title'} eq $b->{'title'}) && ($a->{'href'} eq $b->{'href'})); # If we're linking to points, they're only the same if they link # to exactly the same spot. $same = $same && ($a->{'hrefpoint'} eq $b->{'hrefpoint'}) if $linkpoints; if ($same) { warn "duplicated index entry found, $aP $aS $aT\n"; } $same; } sub tsame { # Unlike same(), tsame only compares a single term my($a) = shift; my($b) = shift; my($term) = shift; my($sterm) = substr($term, 0, 1) . "sortas"; my($A, $B); $A = $a->{$sterm} || $a->{$term}; $B = $b->{$sterm} || $b->{$term}; $A =~ s/^\s*//; $A =~ s/\s*$//; $A = uc($A); $B =~ s/^\s*//; $B =~ s/\s*$//; $B = uc($B); return $A eq $B; } sub end_entry { # End any open elements... print OUT "\n \n" if $lastout; foreach (@seealsos) { # it'd be nice to make this a link... print OUT $indent, " ", &escape($_), "\n"; } @seealsos = (); print OUT "\n\n"; $lastout = ""; } sub print_term { # Print out the links for an indexterm. There can be more than # one if the term has a ZONE that points to more than one place. # (do we do the right thing in that case?) my($idx) = shift; my($key, $indent, @hrefs); my(%href) = (); my(%phref) = (); $indent = " "; if ($idx->{'see'}) { # it'd be nice to make this a link... if ($lastout) { print OUT "\n \n"; $lastout = ""; } print OUT $indent, "", &escape($idx->{'see'}), "\n"; return; } if (keys %{$idx->{'zone'}}) { foreach $key (keys %{$idx->{'zone'}}) { $href{$key} = $idx->{'zone'}->{$key}; $phref{$key} = $key; } } else { $href{$idx->{'href'}} = $idx->{'title'}; $phref{$idx->{'href'}} = $idx->{'hrefpoint'}; } # We can't use because we don't know the ID of the term in the # original source (and, in fact, it might not have one). print OUT ",\n"; @hrefs = keys %href; while (@hrefs) { my($linkend) = ""; my($role) = ""; $key = shift @hrefs; if ($linkpoints) { $linkend = $phref{$key}; } else { $linkend = $key; } $role = $phref{$key}; $role = $1 if $role =~ /\#(.*)$/; $role = $1 if $role =~ /(.*)\./; print OUT $indent; print OUT ""; print OUT "" if ($idx->{'significance'} eq 'PREFERRED'); print OUT &escape($href{$key}); print OUT "" if ($idx->{'significance'} eq 'PREFERRED'); print OUT ""; } if ($idx->{'seealso'}) { push @seealsos, $idx->{'seealso'}; } } sub termsort { my($aP) = $a->{'psortas'} || $a->{'primary'}; my($aS) = $a->{'ssortas'} || $a->{'secondary'}; my($aT) = $a->{'tsortas'} || $a->{'tertiary'}; my($ap) = $a->{'count'}; my($bP) = $b->{'psortas'} || $b->{'primary'}; my($bS) = $b->{'ssortas'} || $b->{'secondary'}; my($bT) = $b->{'tsortas'} || $b->{'tertiary'}; my($bp) = $b->{'count'}; $aP =~ s/^\s*//; $aP =~ s/\s*$//; $aP = uc($aP); $aS =~ s/^\s*//; $aS =~ s/\s*$//; $aS = uc($aS); $aT =~ s/^\s*//; $aT =~ s/\s*$//; $aT = uc($aT); $bP =~ s/^\s*//; $bP =~ s/\s*$//; $bP = uc($bP); $bS =~ s/^\s*//; $bS =~ s/\s*$//; $bS = uc($bS); $bT =~ s/^\s*//; $bT =~ s/\s*$//; $bT = uc($bT); if ($aP eq $bP) { if ($aS eq $bS) { if ($aT eq $bT) { # make sure seealso's always sort to the bottom return 1 if ($a->{'seealso'}); return -1 if ($b->{'seealso'}); # if everything else is the same, keep these elements # in document order (so the index links are in the right # order) return $ap <=> $bp; } else { return $aT cmp $bT; } } else { return $aS cmp $bS; } } else { return $aP cmp $bP; } } sub sortsymbols { my(@term) = @_; my(@new) = (); my(@sym) = (); my($letter); my($idx); # Move the non-letter things to the front. Should digits be thier # own group? Maybe... foreach $idx (@term) { $letter = $idx->{'psortas'}; $letter = $idx->{'primary'} if !$letter; $letter = uc(substr($letter, 0, 1)); if (($letter lt 'A') || ($letter gt 'Z')) { push (@sym, $idx); } else { push (@new, $idx); } } return (@sym, @new); } sub safe_open { local(*OUT) = shift; local(*F, $_); if (($outfile ne '-') && (!$forceoutput)) { my($handedit) = 1; if (open (OUT, $outfile)) { while () { if (//){ $handedit = 0; last; } } close (OUT); } else { $handedit = 0; } if ($handedit) { print "\n$outfile appears to have been edited by hand; use -f or\n"; print " change the output file.\n"; exit 1; } } open (OUT, ">$outfile") || die "$usage\nCannot write to $outfile.\n"; if ($preamble) { # Copy the preamble if (open(F, $preamble)) { while () { print OUT $_; } close(F); } else { warn "$0: cannot open preamble $preamble.\n"; } } } sub escape { # make sure & and < don't show up in the index local $_ = shift; s/&/&/sg; s//>/sg; # what the heck return $_; } =head1 EXAMPLE B B<-o> F F =head1 EXIT STATUS =over 5 =item B<0> Success. =item B<1> Failure. =back =head1 AUTHOR Norm Walsh Endw@nwalsh.comE. Minor updates by Adam Di Carlo Eadam@onshore.comE. =cut lprng-doc-3.8.Arc2/dict0000644000175000017500000003301510523400374011626 00000000000000(null) (null) (null) ACK AIX ANlcnQEBYBYD API APPsocket ARGV ASTART AStArt AU AUTH AUTHFROM AUTHJOB AUTHSAMEUSER AUTHTYPE AUTHUSER AWK Aachen Aauth Acroread Akim Andreas Anvin Apapowell AppSocket AppleTalk Args Ascii AuthID BAQ BB BLUESKY BOOTP BSDi BacKwards Beaulieu Bi Blp Brownworth's CB CD CFG CFLAG CHECKPC CHECKREMOTE CIF CJET CJPMD CMY CMYK CN CONF CONFIG CONT CONTROLLINE CPPFLAGS CR CS CSH CST CTRL Cclass Cedex Centre Checkpc Cifplot Cname Config Cory Cred Cse Cvalue CvsMUxjip Cxi DBLOAD DD DECOUTY DEV DGET DGETENV DIR DIST DLONG DNS DOCTYPE DOM DROOT DSHORT DTD DVI DYN DaemonGID DaemonUID Dave's Ddatabase Ddebuglevel Ddebugopt Debian Debian's Decnet Decouty DefaultValue Demaille DeskJet Deskjet Dirk Ditroff Dnetwork DocBook DocuColor Dugal ECONNREFUSED EDU EGID EIA ENV EOF EOJ EPS EUCS EUID Easycom Edan Emulex Errorcodes ExtendNet FD FF FFEDBEEFDEAF FG FLGS FMHOME FMlpr FQDN FS FV FVtgToAsJUYmzoSFY FWD FWDUSER FX Fc Ff Fformat Fi Fibre Fickenscher FireWire Fn Fo FrameMaker FreeBSD Fx GCC GETENV GID GPL GUI GW Gaine Geomica GetRequests Getopt GhostScript GhostView Gjb Guido Gzip HOLDFILE HOSTNAME HOWTO HPGL HPJCLIMWT HPJetDirect HTML Haldane Hastart Haverland Hh Hobbit Horst Hp HpHzJUMP Htaco Hylands IDs IEN IFHP IFIP IFS IIISi IIISiMX IIIsi IMHO INET INRIA INT IP IPV IPX IPaddr IPmatch IRISA ISI IWs Idzerda InfraRed Initially JABORT JAJUDBRA JFAIL JFAILNORETRY JHOLD JNOPRINT JNOSPOOL JREMOVE JSIGNAL JSUCC JVNC Jaggies Jarausch JetDirect Jetdirect Jinfo JnypypeQiAqo Jobid Justin KADMIN KB KDC KERBEROS KEYFILE KNX KRB Kb Kbytes Kerberized Kerberos KeyID Klemm Kn Knn Kompatibility Kompatible Krb LD LDFLAGS LF LG LINUX LOGDIR LOGNAME LP LPC LPD LPDEST LPDPRT LPQ LPR LPRM LPRng LPRng's LPRngLISA LPSTAT Lantronix LaserJet LaserWriter LaserWriters Lehrstuhl Lexmark Lexmarks Linux LmNvbT LoGger Localhost Lpapowell Lpd MAKEDBM MI MIB MIO MJ MP MPS MSB MSS MVS MX Macs Mathematik McGrath Metacharacters MicroSoft Microserve Microsystem Microsystems Mon Multiport Mynhier NETJet NETPrint NETQue NGPRINTER NIS NL NNN NNNprinter NOADDR NOMODULES NOPUSH NORPC NPA NPHOME NPI NPRINTER NT NVRAM NeWSprint NetBEUI Netatalk Netware NfrotPTUw Nitschke NjaIei Nonprintable Novell Numerische OFS OKzx OPTIND ORG OaK OdHYkeeh OfficeJet OpenBSD Openview Optra OyUCwDfWybgAORuAa PARM PASSTHRU PATHLIST PAULS PCL PCNFS PCNFSD PDF PDT PERL PERMS PGP PGPASSFD PGPPASS PGPPASSFD PGPPASSFILE PGPPATH PID PJL PKD PLP PMSPECIFIC PMTAG PMTYPE PO POSIX PPD PPRINTER PQfO PRINTCAP PRSTATUS PS PST Passthrough PeerAddr PeerPort Perl Pgp PhaserII Php Pjohn Plaser Plp PokAlQMFEDLy PostScript PostScriptLevel PowerPoint Ppapowell Ppr Pprinter Pprintername Pprintqueue Pps Pqueuename Printcap Printqueues Printserver Pserver Pstudent QMS Qlp Qoffice Qpr QsUK Qt Queuename RCF RDYMSG README RECV REMOTEGROUP REMOTEHOST REMOTEIP REMOTEPORT REMOTEUSER REUSEADDR RFC RFCs RItHfFfcHhw RQVMC RUSERID RW RWTH Raccntname Raccountname Rauth ReceiveSecuritySupported Rennes Rname Ro Rremote Runnning SAMEHOST SAMEUSER SETUID SGML SHARENAME SHOWALL SIGSUSP SM SMB SMIT SMIT's SNMP SPARCprinter SPOOLCONTROL STDERR STDIN STDOUT SUID SUNW SUNWpcr SUNWpcu SUNWpsr SUNWpsu SUNWscplp SVCTAG SVR Santana Saroglou SendSecuritySupported SetRequests Shost Shuford SiMX Slackware Solaris Sparc Sparcprinters Stallman Std Subserver SunNetmanage SunOS SuperUser Susr Sven SysV SysVR Syslog SystemV TESTF TESTFILE TESTSUPPORT TFTP TFTPD TGXTe TZ Tbanner Tck Tcrlf Tdev TeX Tektronics Tf Tflag Thes Thi Thu Tk Tkey TpxMmCApolaIb TranslatedValue Ts Ttempfile Ttitle Tue Txxx UDP UID UIDs UJnAhvj UL UPD URL USB USJ USTATUS USTATUSOFF UTILS UX UdfA UdfB Ummm Unspooler UserID Userid UuZWR Uuser Uusername Versatek Volkerding VsbCA Vt VydAAUT Waugh Website WfWg Whooga Wildcard WlDhdtFaAuaMRh XJ XXX XXXX XdfAnnn Xf Xlp Xnnnn XonoiUTMOwDZXJ YP YPDBDIR YPMAPDIR YPPUSH YTgS YXJ YYYY Ylp YnS ZBNlS ZWxsQHNkc ZX Za Zbp Zduplex Zenvelope ZfJzKZjaK Zinlower Zinmiddle Zinupper Zlandscape Zledger Zlegal Zletter Zmanual Zoption Zoptions Zportrait Zsimplex Zthat Zthis Ztransparency aAUTHTYPE aAclV aBxN aCA aD aG aHastart aHh aJ aLpapowell aN aP aPpapowell aQt aUdfA aW aacct aachen aauthtype abled abnormalterm ac accntname accountingref accountingserver accountstart acct acf achk ack acl acroread actv adddd addprinc addr adm admin admscripts ae af afdfA agric ai aidentifier al alL aladdin allowduplicateargs allowgetenv allowuserlogging allowusersetting allpc allprinters ancel andreas anum anumbe anumber aolE aoption ap appledmp appletalk appsocket aprinter aprinting aprocess apsfilter ar args arh asc ascii asd asddasdf asdf asdfasdfadf aspo aspooling astart atalk au auc ault aupdate auth authclientfilter authforward authforwardfilter authforwardid authreceivefilter authref authserverid authtype autoconf autoflush autohold autosense auxw avalu avalue awk axuw bak bannerprinting basename bc ber berkeley bf bi bindir bitcmyk bitrgb bj bjc bk bkf bkfilteroptions bkoffilteroptions bl blockquote blocktransfer bmp bmpmono bnrname bofh bookinfo bootfile bootp bootpd bootptab bouncehost bouncequeues bp bpl bq bqformat br braindead bsd bykey cBAC cGFwb cN ca cappsif cbreak cconnecting ccs cd cdeskjet cdj cdjcolor cdjmono cfA cfB cfX cfZ cfg cfline cflines cfname cgm cgmmono ch chainingfilters chdev checkfornonprintable checkidle checkpc checkstatus chembio chgrp chmod chown chque cif cifplot circ citerefentry cizoWqKvy classinstatus classname classpriority clearstatus clientkey cmd cmljayBBLiBQb cmyk co cols colsep columbia combiNATIONS commonlaser conf config configfile configfiles configsetup configure's conformant connectgrace connectinterval connecttimeout connecttry controlfile controlfilter controlfilterbk coun cp cposupport cppopts cpy createfiles crmod cron crontab crontabs crtscts cs cxh dBATCH dJd dNOPAUSE dSAFER datafiles dave db debian debugopt debugparms declj defaultformat defaulthost defaultpermission defaultprinter defaultprinterwhenunknown defaultpriority defaultq defaultremotehost defaultrmrp defaulttmpdir demaille descrip deskjet dest destfile desthost destport dev devname df dfA dfB dfX dfZ dfa dfaxhigh dfaxlow dfname dfz di diff digprod dir dirname dis disa disablelocalhost dith ditroff djet djps dk dlbGwgPHBhcG dlbGxAYXN dmesg dnj dns doc doctype doppler dpi dresden ds dselect dtd dup dx eDx econnrefused edan edfA edu eecs efi egrep elsif emulex enc encryt endif eng engpc enq enst enum env envar eof eps epson epsonc eq erehwon ericsson ermcap errorcodes errormsg esac esc ethernet eval evenp execprefix execve exitcodes exitlingertimeout exitserver expr extendsys fPYjN failover fapo faqref faqs faroff faxg fd fdfA fdfAnnn fdfB fdfa fdginpt ff ffailure fi fifos fileprog filtercmd filterdir filterldpath filteroptions filteroptionstable filterpath findfont findmail firstname fixup flp flv fminit fo forcefqdnhostname forcelocalhost forcelpqstatus forcequeuename foreach formfeed fp fq fqdn fr freebsd fromhost frominfo fromqueue fulltime functionallity fx gTSJRMw gbe gcc generatebanner getconnection getenv gethostbyaddr gethostbyname getopt getpc getpeername getpwuid getrlimit getsockopt getty gf gfile ghostscript globmatch gmake gov grepping groupid gs gsu gt gunzip gv gw gz gzip gzipped hamburg hange haverlan hd hesiod hexdump hf hfA hh hhost hl hn holdclass hostname hostnames hp hpif hpjet hpl hplaserjet hplcommon hplj hpnp hpnpadmin hpnpsnmp hpnpstat hpprinter ht htaco htm html htmlurl http iI ibm ibmpro ident ids iffilter ifhp ifpap ignorerequesteduserpriority igpm imagen img inary inetd inf informalexample informaltable informatik init inittab insite installingprograms installref int interjob io iona ip ipaddr ipv irisa isi itemizedlist iteral iwhi iwlo iwlq ixon jVUcGeod jVXpGipEo jabort jaggies jarausch jetp jfail jhold jhy jiJdgKiTgGfj jmason jnoprint jnoretry jnospool jobcompletion jobcopies jobend jobfiles jobformat jobid jobids joblength jobname jobsize jobstart jobsteps jobtransfer johny jother jp jpd jremove jsignal jsucc kRH ka kadmin kadmind kcfA kdc kerberos kerberosIV kerberosforwardprincipal kerberoskeytab kerberoslife kerberosrenew kerberosserverprincipal kerberosservice keyfile keyring keytab keytabs kg killpg kinit kjob knx krb ktadd ktuil kxa lEzDsA lantronix lapland laserjet lbp lcub ld ldfAnnn ldopts legalnotice lf lft lfv lg libexec libexecdir linecount linefeed linkend linux linuxdoc listenBSD listenS listitem literallayout lj ljet ljetplus lk ll lll llll ln loadbalance loadsharing localhost lockfile logfile logfiles loggerdestination loggermaxsize loggerpathname loggertimeout logname longnumber lowbar lowres lp lpC lpNet lpQ lpR lpadm lpadmin lpbanner lpc lpccommand lpccommands lpcredirect lpcreread lpd lpdata lpdbounce lpdconf lpddir lpdev lpdforcepoll lpdpermsref lpdpolltime lpdport lpdprintcappath lpf lpfilter lpforms lpl lplabel lpmove lppause lpq lpqall lpr lprM lprbounce lprbsd lprc lpresume lprkey lprm lprng lprngclients lprngoptions lpsched lpshut lpsimulation lpstat lpsystem lpt lptest lpunlock lpusers lpvi lrwxrwxr ls lsa lsqb lt lude lw mIhDMwDJI mIxEd mQCNAzLygTQAAAEEANBW magicfilter mailaddr mailfrom maillist mailname mailoperatoronerror mailto mainqueue mainz majik majikperl makedbm mandir manvolnum maxconnectinterval maxlogfilesize maxserversactive maxstatusline maxstatussize mc md metacharacters mgr mgrgray mgrmono mhost mi minfree minlogfilesize minstatussize misconfigured missingprinter mit mk mkdev mkitab mknod motd moveto ms msg mstimeresolution mtu murphy murpy mv mx mynhier mypass myserver nCA nb nbcs nbsp nc ne necp nend netalk netatalk netcat netgroup netgroups netmasks netnum networkconnectgrace networkprinter newpagecount newps newsprinters nf nismatch nitschke nline nlocal nlps nlsadmin nn nnn nnnn noam noholdall nolink nonprintable nonprivileged normalq normalterm nowait np npapowell npbname nroot ns nserver ntnu nullpage num numjobs nuser oakqO ob objectfile objs oce od oddp ofdetails offilteroptions offline offsite ofhp ofpap oi ok oki oling opendevice opp optind optionindex optra orderedlist org orig originateport os ost othername othernet overcharged oz p'pid paderborn pagecount pagecounters pagecounts paintjet papd papof papowell paprc param params paramters parenb parport parportguide passenv passphrase passphrases passthrough passwd patrick paul pauls pbm pbmraw pc pc's pcfilter pcl pclbanner pclonly pcx pcxgray pcxmono pd pdf pdfAnnn pdfwrite peripheral's perl perms permskeywords permspath permsref pgm pgmraw pgnm pgnmraw pgp pgplpr pgppassphrase pgppath pgpserverkey pht physik picasso pid pipeto pj pjetxl pjl pjxl pkg pkgrm pl plass plp pmadm png pnggray pngmono pnm pnmraw pnn postinstall postremove ppa ppagecount ppagecounter ppages ppd ppmraw ppt pr prdevice preinstall preremove prescans printcap printcapparse printcappath printcapref printcaps printer's printercomm printerenv printerinfo printername printerserver printf printfilter printfiltercommand printhost printingfiles printingjob printjob printjobformats printq printqueue printqueues printserver printspec priv prn programlisting prstatus prtitle ps psbanner psf psmono psqueue ptolemy pubdate pubring pw px py qA qFVx qbAspBopR qconfig qdaemon qms qpagecounter qprt qq qrdoJyIhn que quedev queuecontrolfile queuelockfile queuename queuepause queueresume quickstart rGipbR randkey rasterize rasterized rbC rc rcS rcub rdJjX rdist realfilter reapply redhat refentrytitle reimplemented releaseinfo remotehost remotelogger remotesupport remoteuser renameit repetoire reportserveras requireconfig retryeconnrefused retrynolink retty returnshortstatus reuseaddr reverselpqformat rf rfc rfcs rg rgaine rkt rl rm rmSqCLe rmitab rmpq rosel rowsep rp rrQlUGF rs rsqb rsug rutgers rw rwth rwxr sC sDEVICE sOutputFile sPAPERSIZE sY sa saf safechars santana saveonerror savewhendone sb sbin sbindir sbus scalefont sclient sd sddqgf se secfeatures secfilter secftp secnetwork secnis secoh secprintcap secs secserial sed sendauth sendblockformat senddatafirst sendfailureaction sendjobrwtimeout sendmail sendqueryrwtimeout sendtry serverdict serverhost serverkey servername servertabs servertmpdir setcontrolvals setenginesync setenv setfont setpcvals setsockopt setuid setvar sf sgirgb sh shorthost shortstatus shortstatuslength showpage simplelist simx sj sleeptime slinkdata sm smb smbclient smbprint smc smit smprint snprintf socketapi socketlinger solaris solarisinstall solc sourcehost sourceport sp sparc sparcprinter spoofing spooldir spooldirperms spoolfileperms spoolq spoolqueue spreadjobs sprintf sr src srchost srcport srvtab ss sserver sstatus stalledtime standalone startupfile stats statusdict statusref stcolor stderr stdin stdout stoponabort stopsrc str stronly struct stty su subserver subservers subshells sunfreeware sunsite sunxm sv sxlcrt sy symlinks sysContact sysLocation sysName sysadmins sysconfdir sysdep syslog syslogdevice systemspecific tCdQYXRyaWNrIEEuIFBvd tGQfyp tNov tSun tadf tbody tc tcp tcpip tek tempfile testf testgs textps tf tgroup tgz thead thehamptons tiffcrle tiffg tifflzw tiffpack timestamp timestamps tmp toc topq topsrc tpj tr transfername translateformat ts tscreen tsleeptime tt ttya ttyb ttymon tu twaugh txt ty uab uabgate ualberta ucb ucblib ucs uhhh uiarchive uid uiuc uk ulink um umich ummm umn uncomment uni unihamburg unix unlnkfile unsetenv unsubscribe urgentq url usa usask usedate useidentifier useinfocache usequeuename usergroup userid userinfo userinput username useshorthost usl usr utils utk uuser vAls vAlue vAr vJvtf var variablelist varlistentry vars verbar verbatum vert vf vi virprt vjhg vl vlxf vm vnet vvv wSAnP waitforeof wayoff whitem whitespaces whoami wildcard wimpy wisc wst www wwwthep xWOUc xerox xes xjdirect xjhold xjprint xref xsp xt xvf xxx yFyKunHa yfWRRnfXmoJ yorku yourprinter yp ypcat ypmatch zA zTeoiDNnI zU zopts zsmon zxvf (null) lprng-doc-3.8.Arc2/texmf.cnf0000644000175000017500000000045710523403102012567 00000000000000Add these to the START and END of texmf.cnf /usr/local/share/texmf/web2c/texmf.cnf main_memory = 1100000 param_size = 1500 stack_size = 1500 hash_extra = 25000 string_vacancies = 45000 pool_free = 47500 nest_size = 500 save_size = 5000 pool_size = 500000 max_strings = 55000 font_mem_size= 400000