pax_global_header00006660000000000000000000000064140360303610014506gustar00rootroot0000000000000052 comment=5d6ccb2ef3bf94272e9befbb97c84cffce2cba72 pappl-1.0.3/000077500000000000000000000000001403603036100126235ustar00rootroot00000000000000pappl-1.0.3/BUILD.md000066400000000000000000000046711403603036100140140ustar00rootroot00000000000000Build Instructions ================== PAPPL requires a POSIX-compliant host operating system such as Linux®, macOS®, QNX®, or VxWorks®, a C99 compiler like Clang or GCC, a `make` program that supports the `include` directive, and the following support libraries: - Avahi 0.8 or later (except on macOS) for mDNS/DNS-SD support - CUPS 2.2 or later for the CUPS libraries - GNU TLS 3.0 or later (except on macOS) for TLS support - JPEGLIB 9 or later for JPEG image support (optional for B&W printers) - LIBPNG 1.6 or later for PNG image support (optional) - LIBUSB 1.0 or later for USB printing support - PAM for authentication support (optional) - ZLIB 1.1 or later for compression support Getting Prerequisites --------------------- CentOS 7/Fedora 22/RHEL 7: sudo yum groupinstall 'Development Tools' sudo yum install avahi-devel cups-devel gnutls-devel libjpeg-turbo-devel \ libpng-devel libusbx-devel pam-devel zlib-devel CentOS 8/Fedora 23+/RHEL 8: sudo dnf groupinstall 'Development Tools' sudo dnf install avahi-devel cups-devel gnutls-devel libjpeg-turbo-devel \ libpng-devel libusbx-devel pam-devel zlib-devel Debian/Raspbian/Ubuntu: sudo apt-get install build-essential libavahi-client-dev libcups2-dev \ libcupsimage2-dev libgnutls28-dev libjpeg-dev libpam-dev libpng-dev \ libusb-1.0-0-dev zlib1g-dev macOS (after installing Xcode from the AppStore): (install brew if necessary) brew install libjpeg brew install libpng brew install libusb or download, build, and install libjpeg, libpng, and libusb from source. Building PAPPL -------------- PAPPL uses the usual `configure` script to generate a `make` file: ./configure [options] make Use `./configure --help` to see a full list of options. There is also an Xcode project under the `xcode` directory that can be used on macOS: open xcode/pappl.xcodeproj You can test the build by running the PAPPL test program: testsuite/testpappl Installing PAPPL ---------------- Once you have successfully built PAPPL, install it using: sudo make install By default everything will be installed under `/usr/local`. Use the `--prefix` configure option to override the base installation directory. Set the `DESTDIR`, `DSTROOT`, or `RPM_BUILD_ROOT` environment variables to redirect the installation to a staging area, as is typically done for most software packaging systems (using one of those environment variables...) pappl-1.0.3/CHANGES.md000066400000000000000000000145261403603036100142250ustar00rootroot00000000000000Changes in PAPPL ================ Changes in v1.0.3 ----------------- - The Set-Printer-Attributes operation did not save changes to "printer-contact-col". - Fixed using the "auto" driver without an IEEE-1284 device ID string (Issue #154) - Fixed some more threading issues (Issue #155, Issue #162) - Fixed bogus USB error reporting (Issue #156) - Fixed testpappl on systems without Avahi running (Issue #159) - Adding a printer now works for names with special characters (Issue #161) Changes in v1.0.2 ----------------- - Documentation updates (Issue #140) - The Set-Printer-Attributes operation now properly validates the values of "xxx-default" attributes (Issue #93) - Changes to ready (loaded) media are now validated (Issue #94) - The `papplSystemSetVersions` function now allows changes while the system is running (Issue #123) - The printing defaults page no longer shows a media chooser when there is a single source (Issue #125) - The DNS-SD support functions did not handle when the Avahi daemon is not running (Issue #129) - The printing defaults web page now reports whether the media is borderless (Issue #138) - The `papplClientGetForm` function did not support files larger than 64k (Issue #139) - Deleting and adding a printer with the same name will cause a crash (Issue #141) - Fixed a deadlock issue when calling the `papplPrinterSet...` functions from an iterator callback (Issue #143) - The "Printing Defaults" web page did not show an error message if the defaults could not be validated (Issue #146) - The `server` sub-command now enables TCP/IP connections using the default hostname; auto-started servers still disable TCP/IP connections by default (Issue #147) - The `papplPrinterSetDriverDefaults` function did not validate the defaults against the actual driver data. - The IPP interface no longer allows the Create-Printer operation for single queue applications. - Stopping a printer application with `SIGTERM` now behaves the same as sending a Shutdown-System request. - Added more unit tests to testpappl. - Added better management of the USB and raw printing threads for each printer. - Added better error reporting for USB printers. - `papplDeviceOpen` did not copy the device ID callback. - `papplDeviceList` and `papplDeviceOpen` did not send errors to stderr when a `NULL` error callback was specified. Changes in v1.0.1 ----------------- - Documentation updates (Issue #105) - The `papplSystemLoadState` function did not load vendor attribute defaults correctly (Issue #103) - Vendor options without "xxx-supported" attributes are no longer shown on the printing defaults page (Issue #104) - Added support for Windows 10/Mopria clients that incorrectly convert the printer resource path to lowercase (Issue #106) - The `papplSystemLoadState` function now calls the printer driver's status callback after loading the printer's attributes (Issue #107) - Added additional error handling for memory allocations throughout the library (Issue #113) - Fixed an issue with validation of custom media sizes (Issue #120) - Partially-discovered SNMP printers would cause a crash (Issue #121) - The "copies-supported" attribute was not report correctly. - Job operations that targeted a non-existent job yielded the wrong status code. - Printing a test page from the web interface did not trigger a reload to update the printer and job state. - The TLS web page was hardcoded to use "/etc/cups" for the CUPS server root. - Fixed file output when the job name contains a '/'. - Updated 1-bit driver output to support "photo" dither array for high print quality. - PAPPL now (re)creates the spool directory as needed. - Coverity: Added missing NULL checks. - Coverity: Fixed file descriptor leaks. - Coverity: Fixed some locking issues. - Coverity: Fixed printer-darkness-configured bug in `papplSystemSaveState`. - Coverity: Fixed an error handling bug in the file printing code for the PWG test driver. - Coverity: Removed dead code. Changes in v1.0.0 ----------------- - `papplSystemLoadState` would not load printers whose device IDs contained the `#` character (Issue #92) - Passing "auto" for the driver name would cause a crash if there was no auto- add callback. - Added `papplPrinterGetPath` API to get the path for a printer web page (Issue #97) - The `papplPrinterAddLink` and `papplSystemAddLink` functions now accept an "options" argument instead of the "secure" boolean in order to allow links to be added to multiple places on the web interface in addition to requesting a secure (HTTPS) link (Issue #98) Changes in v1.0rc1 ------------------ - Added IEEE-1284 device ID to argument list for printer driver callbacks (Issue #70) - Documentation updated (Issue #71) - Printers discovered via DNS-SD now report their IEEE-1284 device ID string (Issue #73) - The "auto-add" callback is now part of the system's printer driver interface, allowing IPP, web, and command-line clients to access it (Issue #74) - Now save state after deleting a printer (Issue #75) - Now check whether a named printer already exists (Issue #76) - Support for "output-bin-default" was missing from the web interface (Issue #77) - Fixed support for vendor options at the command-line (Issue #79) - The main loop now shows an error message if an option is provided after "-o" without a space (Issue #80) - Fixed test page and identify buttons (Issue #81) - Code cleanup (Issue #82) - Boolean vendor options are now shown as checkboxes (Issue #85) - Made several improvements to the web interface for adding printers (Issue #86) - `papplSystemLoadState` no longer crashes when it cannot create a printer (Issue #87) - Fixed a crash bug in the "autoadd" command provided by `papplMainloop` (Issue #89) - Added a printer creation callback to `papplSystemSetPrinterDrivers` that is run after a printer is created (Issue #90) - Added the "path" value for the DNS-SD printer web page, and added a registration for the system web page in multi-queue mode. - `papplDeviceRead` now has a 100ms timeout for USB and network connections. - Implemented back-channel and status updates for the USB printer gadget. - Finished implementation of test suite for major code paths/job processing functionality. - Fixed a bug in the log rotation code. - Fixed some threading bugs with the various object lists managed by the system. Changes in v1.0b1 ----------------- - Initial beta release. pappl-1.0.3/CODE_OF_CONDUCT.md000066400000000000000000000007471403603036100154320ustar00rootroot00000000000000Code of Conduct =============== My goal is to provide quality open source software that everyone can use. While I may not be able to address every request or accept every contribution to this project, I will do my best to develop and maintain it for the common good. As part of the open source community, I expect everyone to: - Be friendly and patient. - Be respectful, even if we disagree. - Be honest. - Be accepting of all people. - Fully explain your concerns, issues, or ideas. pappl-1.0.3/CONTRIBUTING.md000066400000000000000000000472441403603036100150670ustar00rootroot00000000000000Contributing to PAPPL ===================== PAPPL is developed and distributed as open source software under the Apache License, Version 2.0. Contributions should be submitted as pull requests on the Github site: http://github.com/michaelrsweet/pappl/pulls Contents -------- - [Build System](#build-system) - [Version Numbering](#version-numbering) - [Coding Guidelines](#coding-guidelines) - [Source Files](#source-files) - [Header Files](#header-files) - [Comments](#comments) - [Indentation](#indentation) - [Spacing](#spacing) - [Return Values](#return-values) - [Functions](#functions) - [Variables](#variables) - [Types](#types) - [Structures](#structures) - [Constants](#constants) - [Shell Script Guidelines](#shell-script-guidelines) - [Makefile Guidelines](#makefile-guidelines) - [General Organization](#general-organization) - [Makefile Documentation](#makefile-documentation) - [Portable Makefile Construction](#portable-makefile-construction) - [Standard Variables](#standard-variables) - [Standard Targets](#standard-targets) - [Object Files](#object-files) - [Programs](#programs) - [Static Libraries](#static-libraries) - [Shared Libraries](#shared-libraries) - [Dependencies](#dependencies) - [Install/Uninstall Support](#installuninstall-support) Build System ------------ The build system uses GNU autoconf to tailor the library to the local operating system. An Xcode project is also provided in the "xcode" directory. To improve portability, makefiles *must not* make use of features unique to GNU make. See the [Makefile Guidelines](#makefile-guidelines) section for a description of the allowed make features and makefile guidelines. Additional GNU build programs such as GNU automake and GNU libtool *must not* be used. GNU automake produces non-portable makefiles which depend on GNU-specific extensions, and GNU libtool is not portable or reliable enough for PAPPL. Version Numbering ----------------- PAPPL uses a three-part version number separated by periods to represent the major, minor, and patch release numbers. Major release numbers indicate large design changes or backwards-incompatible changes to the library. Minor release numbers indicate new features and other smaller changes which are backwards- compatible with previous releases. Patch numbers indicate bug fixes to the previous feature or patch release. > Note: > > When we talk about compatibility, we are talking about binary compatibility > for public APIs and output format compatibility for program interfaces. > Changes to configuration file formats or the default behavior of programs > are not generally considered incompatible as the upgrade process can > normally address such changes gracefully. Production releases use the plain version numbers: MAJOR.MINOR.PATCH 1.0.0 1.0.1 1.0.2 ... 1.1.0 ... 2.0.0 The first production release in a MAJOR.MINOR series (MAJOR.MINOR.0) is called a feature release. Feature releases are the only releases that may contain new features. Subsequent production releases in a MAJOR.MINOR series may only contain bug fixes. Beta-test releases are identified by appending the letter B to the major and minor version numbers followed by the beta release number: MAJOR.MINORbNUMBER 1.0b1 Release candidates are identified by appending the letters RC to the major and minor version numbers followed by the release candidate number: MAJOR.MINORrcNUMBER 1.0rc1 Coding Guidelines ----------------- Contributed source code must follow the guidelines below. While the examples are for C source files, source code for other languages should conform to the same guidelines as allowed by the language. ### Source Files All source files names must be 16 characters or less in length to ensure compatibility with older UNIX filesystems. Source files containing functions have an extension of ".c" for C source files. All "include" files have an extension of ".h". Tabs are set to 8 characters or columns. The top of each source file contains a header giving the purpose or nature of the source file and the copyright and licensing notice: // // Description of file contents. // // Copyright YYYY by AUTHOR. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // ### Header Files Private API header files must be named with the suffix "-private", for example the "pappl.h" header file defines all of the public APIs while the "pappl-private.h" header file defines all of the private APIs. Typically a private API header file will include the corresponding public API header file. ### Comments All source code utilizes block comments within functions to describe the operations being performed by a group of statements; avoid putting a comment per line unless absolutely necessary, and then consider refactoring the code so that it is not necessary. C source files use the C99 comment format ("// comment"): // Clear the state array before we begin... for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++) array[i] = PAPPL_STATE_IDLE; // Wait for state changes on another thread... do { for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++) if (array[i] != PAPPL_STATE_IDLE) break; if (i == (sizeof(array) / sizeof(array[0]))) sleep(1); } while (i == (sizeof(array) / sizeof(array[0]))); ### Indentation All code blocks enclosed by brackets begin with the opening brace on a new line. The code then follows starting on a new line after the brace and is indented 2 spaces. The closing brace is then placed on a new line following the code at the original indentation: { int i; // Looping var // Process foobar values from 0 to 999... for (i = 0; i < 1000; i ++) { do_this(i); do_that(i); } } Single-line statements following "do", "else", "for", "if", and "while" are indented 2 spaces as well. Blocks of code in a "switch" block are indented 4 spaces after each "case" and "default" case: switch (array[i]) { case PAPPL_STATE_IDLE : do_this(i); do_that(i); break; default : do_nothing(i); break; } ### Spacing A space follows each reserved word such as `if`, `while`, etc. Spaces are not inserted between a function name and the arguments in parenthesis. ### Return Values Parenthesis surround values returned from a function: return (PAPPL_STATE_IDLE); ### Functions Functions with a global scope have a lowercase prefix followed by capitalized words, e.g., `papplDoThis`, `papplDoThat`, `papplDoSomethingElse`, etc. Private global functions begin with a leading underscore, e.g., `_papplDoThis`, `_papplDoThat`, etc. Functions with a local scope are declared static with lowercase names and underscores between words, e.g., `do_this`, `do_that`, `do_something_else`, etc. Function names follow the following pattern: - "papplFooCreate" to create a Foo object, - "papplFooDelete" to destroy (free) a Foo object, - "papplFooGetBar" to get data element Bar from object Foo, - "papplFooIsBar" to test condition Bar for object Foo, and - "papplFooSetBar" to set data element Bar in object Foo. - "papplFooVerb" to take an action with object Foo. Each function begins with a comment header describing what the function does, the possible input limits (if any), the possible output values (if any), and any special information needed: // // 'papplDoThis()' - Short description of function. // // Longer documentation for function with examples using a subset of // markdown. This is a bulleted list: // // - One fish // - Two fish // - Red fish // - Blue fish // // > *Note:* Special notes for developer should be markdown block quotes. // float // O - Inverse power value, 0.0 <= y <= 1.1 papplDoThis(float x) // I - Power value (0.0 <= x <= 1.1) { ... return (y); } Return/output values are indicated using an "O" prefix, input values are indicated using the "I" prefix, and values that are both input and output use the "IO" prefix for the corresponding in-line comment. The [`codedoc` documentation generator][1] also understands the following special text in the function description comment: @deprecated@ - Marks the function as deprecated (not recommended for new development and scheduled for removal) @since version@ - Marks the function as new in the specified version. @private@ - Marks the function as private (same as starting the function name with an underscore) [1]: https://www.msweet.org/codedoc ### Variables Variables with a global scope are capitalized, e.g., `ThisVariable`, `ThatVariable`, `ThisStateVariable`, etc. Globals *must not* be used in the PAPPL library. Variables with a local scope are lowercase with underscores between words, e.g., `this_variable`, `that_variable`, etc. Any "local global" variables shared by functions within a source file are declared static. Each variable is declared on a separate line and is immediately followed by a comment block describing the variable: int ThisVariable; // The current state of this static int that_variable; // The current state of that ### Types All type names are lowercase with underscores between words and `_t` appended to the end of the name, e.g., `pappl_this_type_t`, `pappl_that_type_t`, etc. Type names start with the "pappl\_" prefix to avoid conflicts with system types. Private type names start with an underscore, e.g., `_pappl_this_t`, `_pappl_that_t`, etc. Each type has a comment block immediately after the typedef: typedef int pappl_this_type_t; // This type is for foobar options. ### Structures All structure names are lowercase with underscores between words and `_s` appended to the end of the name, e.g., `pappl_this_s`, `pappl_that_s`, etc. Structure names start with the "pappl\_" prefix to avoid conflicts with system types. Private structure names start with an underscore, e.g., `_pappl_this_s`, `_pappl_that_s`, etc. Each structure has a comment block immediately after the struct and each member is documented similar to the variable naming policy above: struct pappl_this_struct_s // This structure is for foobar options. { int this_member; // Current state for this int that_member; // Current state for that }; One common design pattern is to define a private structure with a public typedef, for example: // In public header typedef struct _pappl_foo_s pappl_foo_t // Foo object // In private header struct _pappl_foo_s // Foo object { int this_member; // Current state for this int that_member; // Current state for that }; ### Constants All constant names are uppercase with underscores between words, e.g., `PAPPL_THIS_CONSTANT`, `PAPPL_THAT_CONSTANT`, etc. Constants begin with the "PAPPL\_" prefix to avoid conflicts with system constants. Private constants start with an underscore, e.g., `_PAPPL_THIS_CONSTANT`, `_PAPPL_THAT_CONSTANT`, etc. Typed enumerations should be used whenever possible to allow for type checking by the compiler. The constants for typed enumerations must match the type name in uppercase, for example a `pappl_foo_e` enumeration has constant names starting with `PAPPL_FOO_`. Comment blocks immediately follow each constant: typedef enum pappl_tray_e // Tray enumerations { PAPPL_TRAY_THIS, // This tray PAPPL_TRAY_THAT // That tray } pappl_tray_t; Shell Script Guidelines ----------------------- All shell scripts in PAPPL must conform to the [POSIX shell][POSIX-SHELL] command language and should restrict their dependence on non-POSIX utility commands. [POSIX-SHELL]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18 Makefile Guidelines ------------------- The following is a guide to the [POSIX makefile-based][POSIX-MAKE] build system. These standards have been developed over the years to allow the PAPPL to be built on as many systems and environments as possible. [POSIX-MAKE]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html ### General Organization The source code is organized functionally into a top-level makefile, include file, and subdirectories each with their own makefile and dependencies files. The ".in" files are template files for the autoconf software and are used to generate a static version of the corresponding file. ### Makefile Documentation Each makefile starts with the standard header containing the description of the file, and PAPPL copyright and license notice: # # Makefile for ... # # Copyright YYYY by AUTHOR. # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # ### Portable Makefile Construction We use a common subset of make program syntax to ensure that the software can be compiled "out of the box" on as many systems as possible. The following is a list of assumptions we follow when constructing makefiles: - Targets; we assume that the make program supports the notion of simple targets of the form "name:" that perform tab-indented commands that follow the target, e.g.: target: TAB target commands - Dependencies; we assume that the make program supports recursive dependencies on targets, e.g.: target: foo bar TAB target commands foo: bla TAB foo commands bar: TAB bar commands bla: TAB bla commands - Variable Definition; we assume that the make program supports variable definition on the command-line or in the makefile using the following form: name=value - Variable Substitution; we assume that the make program supports variable substitution using the following forms: - `$(name)`; substitutes the value of "name", - `$(name:.old=.new)`; substitutes the value of "name" with the filename extension ".old" changed to ".new", - `$(MAKEFLAGS)`; substitutes the command-line options passed to the program without the leading hyphen (-), - `$$`; substitutes a single $ character, - `$<`; substitutes the current source file or dependency, and - `$@`; substitutes the current target name. - Suffixes; we assume that the make program supports filename suffixes with assumed dependencies, e.g.: .SUFFIXES: .c .o .c.o: TAB $(CC) $(CFLAGS) -o $@ -c $< - Include Files; we assume that the make program supports POSIX include lines, e.g.: include ../Makedefs include Dependencies - Comments; we assume that comments begin with a # character and proceed to the end of the current line. - Line Length; we assume that there is no practical limit to the length of lines. - Continuation of long lines; we assume that the `\` character may be placed at the end of a line to concatenate two or more lines in a makefile to form a single long line. - Shell; we assume a POSIX-compatible shell is present on the build system. ### Standard Variables The following variables are defined in the "Makedefs" file generated by the autoconf software: - `AR`; the static library archiver command, - `ARFLAGS`; options for the static library archiver command, - `bindir`; the binary installation directory, - `BUILDROOT`; optional installation prefix (defaults to DSTROOT), - `CC`; the C compiler command, - `CFLAGS`; options for the C compiler command, - `CODE_SIGN`: the code signing utility, - `CODESIGN_IDENTITY`: the code signing identity, - `CSFLAGS`; options for the code signing utility, - `datadir`; the data file installation directory, - `DSOFLAGS`; options for the shared library building command, - `includedir`; the public header file installation directory, - `INSTALL`; the install command, - `LDFLAGS`; options for the linker, - `libdir`; the library installation directory, - `LIBS`; libraries for all programs, - `LN`; the ln command, - `mandir`; the man page installation directory, - `MKDIR`; the mkdir command, - `OPTIM`; common compiler optimization options, - `prefix`; the installation prefix directory, - `RANLIB`; the static library indexing command, - `RM`; the rm command, - `RMDIR`; the rmdir command, - `SHELL`; the sh (POSIX shell) command, - `srcdir`; the source directory, - `sysconfdir`; the system configuration directory, and - `top_srcdir`; the top-level source directory. ### Standard Targets The following standard targets are defined in each makefile: - `all`; creates all target programs, libraries, and documentation files, - `clean`; removes all target programs libraries, documentation files, and object files, - `depend`; generates automatic dependencies for any C source files (also see "DEPENDENCIES"), - `distclean`; removes autoconf-generated files in addition to those removed by the "clean" target, - `install`; installs all distribution files in their corresponding locations (also see "INSTALL/UNINSTALL SUPPORT"), and - `uninstall`; removes all distribution files from their corresponding locations (also see "INSTALL/UNINSTALL SUPPORT"). ### Object Files Object files (the result of compiling a C source file) have the extension ".o". ### Programs Program files are the result of linking object files and libraries together to form an executable file. A typical program target looks like: program: $(OBJS) TAB echo Linking $@... TAB $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) TAB $(CODE_SIGN) $(CSFLAGS) -i org.msweet.pappl.$@ $@ ### Static Libraries Static libraries have a prefix of "lib" and the extension ".a". A typical static library target looks like: libname.a: $(OBJECTS) TAB echo Creating $@... TAB $(RM) $@ TAB $(AR) $(ARFLAGS) $@ $(OBJECTS) TAB $(RANLIB) $@ ### Shared Libraries Shared libraries have a prefix of "lib" and the extension ".dylib" or ".so" depending on the operating system. A typical shared library is composed of several targets that look like: libname.so.1: $(OBJECTS) TAB echo $(CC) $(DSOFLAGS) -o $@ ... TAB $(CC) $(DSOFLAGS) -o $@ $(OBJECTS) TAB $(RM) `basename $@ .1` TAB $(LN) $@ `basename $@ .1` libname.1.dylib: $(OBJECTS) TAB echo $(CC) $(DSOFLAGS) -o $@ ... TAB $(CC) $(DSOFLAGS) -o $@ \ TAB TAB -install_name $(libdir)/$@ \ TAB TAB -current_version 1.0.0 \ TAB TAB -compatibility_version 1 \ TAB TAB $(OBJECTS) $(LIBS) TAB $(CODE_SIGN) $(CSFLAGS) -i org.msweet.pappl.`basename $@ .1.dylib` $@ TAB $(RM) `basename $@ .1.dylib`.dylib TAB $(LN) $@ `basename $@ .1.dylib`.dylib ### Dependencies Static dependencies are expressed in each makefile following the target, for example: foo: bar Static dependencies are only used when it is not possible to automatically generate them. Automatic dependencies are stored in a file named "Dependencies" and included at the end of the makefile. The following "depend" target rule is used to create the automatic dependencies: depend: TAB $(CC) -MM $(CFLAGS) $(OBJS:.o=.c) | \ TAB TAB sed -e '1,$$s/ \/usr\/include\/[^ ]*//g' \ TAB TAB -e '1,$$s/ \/usr\/local\/include\/[^ ]*//g' >Dependencies ### Install/Uninstall Support All makefiles contains install and uninstall rules which install or remove the corresponding software. These rules must use the $(BUILDROOT) variable as a prefix to any installation directory so that PAPPL can be installed in a temporary location for packaging by programs like `rpmbuild`. The `$(RANLIB)` command must be run on any static libraries after installation since the symbol table is invalidated when the library is copied on some platforms. pappl-1.0.3/LICENSE000066400000000000000000000261361403603036100136400ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pappl-1.0.3/Makedefs.in000066400000000000000000000026741403603036100147030ustar00rootroot00000000000000# # Make definitions for the Printer Application Framework # # Copyright © 2019-2020 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # # Be silent... .SILENT: # Version numbers... PAPPL_VERSION = @PAPPL_VERSION@ PAPPL_VERSION_MAJOR = @PAPPL_VERSION_MAJOR@ PAPPL_VERSION_MINOR = @PAPPL_VERSION_MINOR@ # Programs and options... AR = @AR@ ARFLAGS = @ARFLAGS@ CC = @CC@ CFLAGS = -I.. @CFLAGS@ $(OPTIM) $(WARNINGS) CODE_SIGN = @CODE_SIGN@ CODESIGN_IDENTITY = - CSFLAGS = -s "$(CODESIGN_IDENTITY)" @CSFLAGS@ --timestamp DSOFLAGS = @DSOFLAGS@ $(CFLAGS) INSTALL = @INSTALL@ LDFLAGS = @LDFLAGS@ $(OPTIM) LIBS = @LIBS@ LN = @LN@ MKDIR = @MKDIR@ -p OPTIM = @OPTIM@ RANLIB = @RANLIB@ RM = @RM@ -f RMDIR = @RMDIR@ SHELL = /bin/sh WARNINGS = @WARNINGS@ # Targets LIBPAPPL = @LIBPAPPL@ # Directories... bindir = @bindir@ datadir = @datadir@ datarootdir = @datarootdir@ exec_prefix = @exec_prefix@ includedir = @includedir@ infodir = @infodir@ libdir = @libdir@ libexecdir = @libexecdir@ localstatedir = @localstatedir@ mandir = @mandir@ oldincludedir = @oldincludedir@ prefix = @prefix@ privateinclude = @privateinclude@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ top_srcdir = @top_srcdir@ BUILDROOT = $(DSTROOT)$(RPM_BUILD_ROOT)$(DESTDIR) # Build commands... .c.o: echo Compiling $<... $(CC) $(CFLAGS) -c -o $@ $< pappl-1.0.3/Makefile000066400000000000000000000026261403603036100142710ustar00rootroot00000000000000# # Top-level makefile for the Printer Application Framework # # Copyright © 2020 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # include Makedefs # Source directories... DIRS = \ doc \ man \ pappl \ testsuite # Make all targets... all: echo Using CC="$(CC)" echo Using CFLAGS="$(CFLAGS)" echo Using DSOFLAGS="$(DSOFLAGS)" echo Using LDFLAGS="$(LDFLAGS)" echo Using LIBS="$(LIBS)" for dir in $(DIRS); do \ echo Making all in $$dir...; \ (cd $$dir; $(MAKE) $(MFLAGS) all) || exit 1; \ done # Remove object and target files... clean: $(RM) testpappl.state for dir in $(DIRS); do \ echo Cleaning all in $$dir...; \ (cd $$dir; $(MAKE) $(MFLAGS) clean) || exit 1; \ done # Remove all non-distribution files... distclean: clean $(RM) Makedefs config.h config.log config.status pappl/pappl.pc -$(RM) -r autom4te*.cache # Make dependencies depend: for dir in $(DIRS); do \ echo Updating dependencies in $$dir...; \ (cd $$dir; $(MAKE) $(MFLAGS) depend) || exit 1; \ done # Install everything... install: for dir in $(DIRS); do \ echo Installing in $$dir...; \ (cd $$dir; $(MAKE) $(MFLAGS) install) || exit 1; \ done # Test everything... test: for dir in $(DIRS); do \ echo Testing in $$dir...; \ (cd $$dir; $(MAKE) $(MFLAGS) test) || exit 1; \ done # Don't run top-level build targets in parallel... .NOTPARALLEL: pappl-1.0.3/NOTICE000066400000000000000000000021111403603036100135220ustar00rootroot00000000000000PAPPL - Printer Application Library Copyright © 2020-2021 by Michael R Sweet. Copyright © 2007-2019 by Apple Inc. Copyright © 1997-2007 by Easy Software Products. (Optional) Exceptions to the Apache 2.0 License: ================================================ As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. pappl-1.0.3/README.md000066400000000000000000000106721403603036100141100ustar00rootroot00000000000000PAPPL - Printer Application Framework ===================================== ![Version](https://img.shields.io/github/v/release/michaelrsweet/pappl?include_prereleases) ![Apache 2.0](https://img.shields.io/github/license/michaelrsweet/pappl) [![Build Status](https://travis-ci.org/michaelrsweet/pappl.svg?branch=master)](https://travis-ci.org/github/michaelrsweet/pappl) [![Coverity Scan Status](https://img.shields.io/coverity/scan/22385.svg)](https://scan.coverity.com/projects/michaelrsweet-pappl) [![LGTM Grade](https://img.shields.io/lgtm/grade/cpp/github/michaelrsweet/pappl)](https://lgtm.com/projects/g/michaelrsweet/pappl/context:cpp) [![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/michaelrsweet/pappl)](https://lgtm.com/projects/g/michaelrsweet/pappl/) PAPPL is a simple C-based framework/library for developing CUPS Printer Applications, which are the recommended replacement for printer drivers. It was specifically developed to support [LPrint][1] and a future [Gutenprint][2] Printer Application but is sufficiently general purpose to support any kind of printer or driver that can be used on desktops, servers, and in embedded environments. PAPPL supports JPEG, PNG, PWG Raster, Apple Raster, and "raw" printing to printers connected via USB and network (AppSocket/JetDirect) connections. PAPPL provides an embedded [IPP Everywhere™][3] service that provides access to printers locally or on your whole network. PAPPL is licensed under the Apache License Version 2.0 with an exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used freely in any project you'd like. If you want to support the development of this framework financially, please consider sponsoring me through [Github][4]. I am also available to do consulting and/or development through my company Lakeside Robotics (). My hope is that PAPPL will accelerate the adoption of IPP Everywhere™ and make it easier for people to support other IPP-based licensing programs like [AirPrint™][5] and [Mopria®][6]. Requirements ------------ PAPPL requires a POSIX-compliant host operating system such as Linux®, macOS®, QNX®, or VxWorks®, a "make" utility that supports the `include` directive (like GNU make), a C99-compatible C compiler such as GCC or Clang, and the "pkg-config" utility. It also requires the following support libraries: - Avahi 0.8 or later (except on macOS) for mDNS/DNS-SD support - CUPS 2.2 or later for the CUPS libraries - GNU TLS 3.0 or later (except on macOS) for TLS support - JPEGLIB 9 or later for JPEG image support (optional for B&W printers) - LIBPNG 1.6 or later for PNG image support (optional) - LIBUSB 1.0 or later for USB printing support - PAM for authentication support (optional) - ZLIB 1.1 or later for compression support Most development happens on a Mac, with testing on various Linux distributions and a [Raspberry Pi Zero W][7] to ensure that memory and CPU requirements remain low. Documentation and Examples -------------------------- Documentation can be found in the "doc" and "man" directories. The OpenPrinting group has [written a tutorial][8] showing how to migrate the `rastertohp` driver from CUPS to a Printer Application using PAPPL, which can be used as a recipe for migrating any CUPS driver. This example is available in the [hp-printer-app][9] project and is also discussed in the PAPPL documentation. The OpenPrinting group has also developed a [PostScript printer application][10] using PAPPL to support the many otherwise unsupported PostScript and Ghostscript-based printers. Legal Stuff ----------- PAPPL is Copyright © 2019-2021 by Michael R Sweet. This software is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS). See the files "LICENSE" and "NOTICE" for more information. This software is based loosely on the "ippeveprinter.c" code from [CUPS][11]. [1]: https://github.com/michaelrsweet/lprint [2]: http://gutenprint.sf.net/ [3]: https://www.pwg.org/ipp/everywhere.html [4]: https://github.com/sponsors/michaelrsweet [5]: https://support.apple.com/en-us/HT201311 [6]: https://mopria.org/ [7]: https://www.raspberrypi.org/products/raspberry-pi-zero-w/ [8]: https://openprinting.github.io/documentation/02-designing-printer-drivers/ [9]: https://github.com/michaelrsweet/hp-printer-app [10]: https://github.com/openprinting/ps-printer-app [11]: https://www.cups.org/ pappl-1.0.3/SECURITY.md000066400000000000000000000125621403603036100144220ustar00rootroot00000000000000Security Policy =============== This file describes how security issues are reported and handled, and what the expectations are for security issues reported to this project. > Note: As there are currently no stable releases of PAPPL, any security fixes > to the PAPPL software will be pushed to the Github repository as soon as a > fix is available. Once there *is* a stable release, the usual responsible > disclosure policies will apply. Responsible Disclosure ---------------------- With *responsible disclosure*, a security issue (and its fix) is disclosed only after a mutually-agreed period of time (the "embargo date"). The issue and fix are shared amongst and reviewed by the key stakeholders (Linux distributions, OS vendors, etc.) and the CERT/CC. Fixes are released to the public on the agreed-upon date. > Responsible disclosure applies only to production releases. A security > vulnerability that only affects unreleased code can be fixed immediately > without coordination. Vendors *should not* package and release unstable > snapshots, beta releases, or release candidates of this software. Supported Versions ------------------ All production releases of this software are subject to this security policy. A production release is tagged and given a semantic version number of the form: MAJOR.MINOR.PATCH where "MAJOR" is an integer starting at 1 and "MINOR" and "PATCH" are integers starting at 0. A feature release has a "PATCH" value of 0, for example: 1.0.0 1.1.0 2.0.0 Beta releases and release candidates are *not* prodution releases and use semantic version numbers of the form: MAJOR.MINORbNUMBER MAJOR.MINORrcNUMBER where "MAJOR" and "MINOR" identify the new feature release version number and "NUMBER" identifies a beta or release candidate number starting at 1, for example: 1.0b1 1.0b2 1.0rc1 Reporting a Vulnerability ------------------------- Report all security issues to "security AT msweet.org". Expect a response within 5 business days. Any proposed embargo date should be at least 30 days and no more than 90 days in the future. PGP Public Key -------------- The following PGP public key can be used for signing security messages. ``` -----BEGIN PGP PUBLIC KEY BLOCK----- Comment: GPGTools - https://gpgtools.org mQINBF6L0RgBEAC8FTqc/1Al+pWW+ULE0OB2qdbiA2NBjEm0X0WhvpjkqihS1Oih ij3fzFxKJ+DgutQyDb4QFD8tCFL0f0rtNL1Iz8TtiAJjvlhL4kG5cdq5HYEchO10 qFeZ1DqvnHXB4pbKouEQ7Q/FqB1PG+m6y2q1ntgW+VPKm/nFUWBCmhTQicY3FOEG q9r90enc8vhQGOX4p01KR0+izI/g+97pWgMMj5N4zHuXV/GrPhlVgo3Wn1OfEuX4 9vmv7GX4G17Me3E3LOo0c6fmPHJsrRG5oifLpvEJXVZW/RhJR3/pKMPSI5gW8Sal lKAkNeV7aZG3U0DCiIVL6E4FrqXP4PPj1KBixtxOHqzQW8EJwuqbszNN3vp9w6jM GvGtl8w5Qrw/BwnGC6Dmw+Qv04p9JRY2lygzZYcKuwZbLzBdC2CYy7P2shoKiymX ARv+i+bUl6OmtDe2aYaqRkNDgJkpuVInBlMHwOyLP6fN2o7ETXQZ+0a1vQsgjmD+ Mngkc44HRnzsIJ3Ga4WwW8ggnAwUzJ/DgJFYOSbRUF/djBT4/EFoU+/kjXRqq8/d c8HjZtz2L27njmMw68/bYmY1TliLp50PXGzJA/KeY90stwKtTI0ufwAyi9i9BaYq cGbdq5jnfSNMDdKW2kLCNTQeUWSSytMTsdU0Av3Jrv5KQF8x5GaXcpCOTwARAQAB tExNaWNoYWVsIFN3ZWV0IChzZWN1cml0eUBtc3dlZXQub3JnKSAoU2VjdXJpdHkg UEdQIEtleSkgPHNlY3VyaXR5QG1zd2VldC5vcmc+iQJUBBMBCgA+FiEEOElfSXYU h91AF0sBpZiItz2feQIFAl6L0RgCGwMFCQeGH4AFCwkIBwMFFQoJCAsFFgIDAQAC HgECF4AACgkQpZiItz2feQIhjhAAqZHuQJkPBsAKUvJtPiyunpR6JENTUIDxnVXG nue+Zev+B7PzQ7C4CAx7vXwuWTt/BXoyQFKRUrm+YGiBTvLYQ8fPqudDnycSaf/A n01Ushdlhyg1wmCBGHTgt29IkEZphNj6BebRd675RTOSD5y14jrqUb+gxRNuNDa5 ZiZBlBE4A8TV6nvlCyLP5oXyTvKQRFCh4dEiL5ZvpoxnhNvJpSe1ohL8iJ9aeAd5 JdakOKi8MmidRPYC5IldXwduW7VC7dtqSiPqT5aSN0GJ8nIhSpn/ZkOEAPHAtxxa 0VgjltXwUDktu74MUUghdg2vC1df2Z+PqHLsGEqOmxoBIJYXroIqSEpO3Ma7hz0r Xg1AWHMR/xxiLXLxgaZRvTp7AlaNjbqww8JDG8g+nDIeGsgIwWN/6uPczledvDQa HtlMfN97i+rt6sCu13UMZHpBKOGg7eAGRhgpOwpUqmlW1b+ojRHGkmZ8oJSE7sFT gzSGNkmfVgA1ILl0mi8OBVZ4jlUg6EgVsiPlzolH92iscK7g50PdjzpQe0m3gmcL dpOmSL8Fti05dPfamJzIvJd28kMZ6yMnACKj9rq/VpfgYBLK8dbNUjEOQ2oq7PyR Ye/LE1OmAJwfZQkyQNI8yAFXoRJ8u3/bRb3SPvGGWquGBDKHv2K1XiCW65uyLe5B RNJWmme5Ag0EXovRGAEQAJZMFeIMt/ocLskrp89ZyBTTiavFKn9+QW7C2Mb36A73 J2g9vRFBSRizb+t8lSzP/T1GbKS0cEmfEpQppWImTbOMV6ZgxrM0IUy1Yd7Kyc0K oNMZvykRYwVMzxB5hiQ88kCLfqTNCveIvu1xcB9pWkf+cuDmGCxA3I+yc3Eh/SOP urDsHObt7fyEmJpSxCXlMFHRCuWyGXhMNvhR186t9mANW0PyxKJ8efr+2Vhm1+pA Vk9JESac/lREvx9PVFmlPdqgqRkQ0TQB5+ROo9Wy77cxQr5+rvSZZff630I1YgZf Ph6xOV1/q6vJ3RBNA2nPSTjPeeWQ7pTn7PZGJwCjIUjhMbO+EJVKUJNOAEg033mG tLfbFUYdhA/dRgFuKz90loCMfsnf3e4o/TFydSHUuwBUtOWkL1BBWEbk95M/Zr00 w5fD9knas1u5Lc4ogXzTFPnvJ6hM1RAFJEd+FYzJZIvzwrIx4Ag1DOKViVBpeLTu HWj+xckEgvxEBglplALzfSIJ0CLQSNL8iMFbzCnPeUoQfPkqu37KHrB9syAA06Tb qw1Ax0qBqKInGIgBd0w6dFLF3s04xVcPAXWyJ0w4I7h2bs+aD6YwwK6xxCtXxtN5 Q1LQM8s3tKNXER3mZ8zfwgwjsdLVwhXhysFi6Dlkvk/Vrbn1QDfJnzq+F9LsGRGb ABEBAAGJAjwEGAEKACYWIQQ4SV9JdhSH3UAXSwGlmIi3PZ95AgUCXovRGAIbDAUJ B4YfgAAKCRClmIi3PZ95AhDZD/40fShzDS/smZZL0oXN4GgZ62FrXWBdLjontkXo d8hDh1wJZwqsLVbtO2Gu0CPeH9GclQ3bYsR19sGMM4FDgjMu57O/TU6GZl2Ywcjh ayhRTHyAq/BKZn71AM0N7LS8MdNTaLbTbzEu5oGbAmOVv5f0SUnQoGxbeF8ih5bo hR3ZcORujWMgnymL3+cerNyIDQAtfMAUTfpVcwem4CvquA9Wjtur8YN1t+N7I3o2 eMTNSyNUL9Yx3NxbyJ0yrrMvASo+ZVRaPW5+ET9Iqd68ILSY04Gnar3URJssggX8 +cuyEbP9bAG8qYqcr2aSC2dW84mL/RnZGR//1dfS0Ugk6Osj0LSF5i+mz0CbIjYQ PKgLlgpycuGZBC5kG3RWWfanM0HxPDx10a7vEWA1A5Q+csx4yi3CW/giC1zAdhUO cJ1l4Uj/oxpGeLN7BnT/2NzU/px2fpbaG+xU4HlwjzFM2cIOUIohHFhdvFZbFIIA mePqTBnEB3HtXYRTgMoYDXLWhlOXjyVnMR45WDfvEA3KqbAz6sNMtaOJ6rHBWnR1 1YbpvDWUeaGSLXBoGyo3RgTrN9jON8lE/oUxFobnEdfZGD+uwIniylc5rw3+VkBU +QGZDfgPgxjSmKsWq1cK6rNfBacGYrdyqf90VemEsvR8r0Ump0RPzBMlAAq0Xkup WkiKlA== =0GzT -----END PGP PUBLIC KEY BLOCK----- ``` pappl-1.0.3/config.guess000077500000000000000000001313551403603036100151530ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-11-29' # This file 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 3 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, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches with a ChangeLog entry to config-patches@gnu.org. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; or1k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pappl-1.0.3/config.h.in000066400000000000000000000015131403603036100146460ustar00rootroot00000000000000// // Configuration header file for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // Version numbers #define PAPPL_VERSION "" #define PAPPL_VERSION_MAJOR 0 #define PAPPL_VERSION_MINOR 0 // Location of CUPS config files #define CUPS_SERVERROOT "/etc/cups" // DNS-SD (mDNSResponder or Avahi) #undef HAVE_DNSSD #undef HAVE_AVAHI // GNU TLS #undef HAVE_GNUTLS // libjpeg #undef HAVE_LIBJPEG // libpng #undef HAVE_LIBPNG // libusb #undef HAVE_LIBUSB // libpam #undef HAVE_LIBPAM #undef HAVE_SECURITY_PAM_APPL_H #undef HAVE_PAM_PAM_APPL_H // String functions #undef HAVE_STRLCPY // Random number support #undef HAVE_SYS_RANDOM_H #undef HAVE_ARC4RANDOM #undef HAVE_GETRANDOM #undef HAVE_GNUTLS_RND pappl-1.0.3/config.sub000077500000000000000000001054121403603036100146110ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2013 Free Software Foundation, Inc. timestamp='2013-10-01' # This file 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 3 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, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches with a ChangeLog entry to config-patches@gnu.org. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 \ | or1k | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or1k-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pappl-1.0.3/configure000077500000000000000000004772041403603036100145500ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.70 for pappl 1.0.3. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and $0: https://github.com/michaelrsweet/pappl/issues about $0: your system, including any error possibly output before $0: this message. Then install a modern shell, or manually $0: run the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pappl' PACKAGE_TARNAME='pappl' PACKAGE_VERSION='1.0.3' PACKAGE_STRING='pappl 1.0.3' PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pappl/issues' PACKAGE_URL='https://www.msweet.org/pappl' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS WARNINGS OPTIM CSFLAGS LIBPAPPL PKGCONFIG_LIBUSB PKGCONFIG_LIBPNG PKGCONFIG_LIBJPEG PKGCONFIG_GNUTLS PKGCONFIG CUPSCONFIG INSTALL ARFLAGS LN RM MKDIR CODE_SIGN AR RANLIB OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC DSOFLAGS host_os host_vendor host_cpu host build_os build_vendor build_cpu build PAPPL_VERSION_MINOR PAPPL_VERSION_MAJOR PAPPL_VERSION target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_static_cups with_dnssd enable_libjpeg enable_libpng enable_libusb enable_pam enable_debug enable_maintainer enable_sanitizer with_dsoflags with_ldflags ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures pappl 1.0.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/pappl] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of pappl 1.0.3:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-static-cups use static CUPS libraries, default=no --enable-libjpeg use libjpeg for JPEG printing, default=auto --enable-libpng use libpng for PNG printing, default=auto --enable-libusb use libusb for USB printing, default=auto --enable-libpam use libpam for authentication, default=auto --enable-debug turn on debugging, default=no --enable-maintainer turn on maintainer mode, default=no --enable-sanitizer build with AddressSanitizer, default=no Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-dnssd=LIBRARY set DNS-SD library (auto, avahi, mdnsresponder) --with-dsoflags=... Specify additional DSOFLAGS --with-ldflags=... Specify additional LDFLAGS Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . pappl home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF pappl configure 1.0.3 generated by GNU Autoconf 2.70 Copyright (C) 2020 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by pappl $as_me 1.0.3, which was generated by GNU Autoconf 2.70. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="config.guess config.sub" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" PAPPL_VERSION="1.0.3" PAPPL_VERSION_MAJOR="`echo 1.0.3 | awk -F. '{print $1}'`" PAPPL_VERSION_MINOR="`echo 1.0.3 | awk -F. '{printf("%d\n",$2);}'`" printf "%s\n" "#define PAPPL_VERSION \"$PAPPL_VERSION\"" >>confdefs.h printf "%s\n" "#define PAPPL_VERSION_MAJOR $PAPPL_VERSION_MAJOR" >>confdefs.h printf "%s\n" "#define PAPPL_VERSION_MINOR $PAPPL_VERSION_MINOR" >>confdefs.h # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else $as_nop ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac host_os_name=`echo $host_os | sed -e '1,$s/[0-9.]*$//g'` host_os_version=`echo $host_os | sed -e '1,$s/^[^0-9.]*//g' | awk -F. '{print $1 $2}'` # Linux often does not yield an OS version we can use... if test "x$host_os_version" = x; then host_os_version="0" fi CFLAGS="${CFLAGS:=}" CXXFLAGS="${CXXFLAGS:=}" DSOFLAGS="${DSOFLAGS:=}" LDFLAGS="${LDFLAGS:=}" LIBS="${LIBS:=}" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then for ac_prog in clang cc gcc do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in clang cc gcc do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL ac_prog_cc_stdc_options= case "x$ac_cv_prog_cc_c11" in #( x) : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } ;; #( xno) : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } ;; #( *) : ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c11" CC="$CC$ac_prog_cc_stdc_options" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } ;; esac if test "x$ac_cv_prog_cc_c11" != xno then : ac_prog_cc_stdc=c11 ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc1x -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL ac_prog_cc_stdc_options= case "x$ac_cv_prog_cc_c99" in #( x) : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } ;; #( xno) : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } ;; #( *) : ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c99" CC="$CC$ac_prog_cc_stdc_options" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno then : ac_prog_cc_stdc=c99 ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL ac_prog_cc_stdc_options= case "x$ac_cv_prog_cc_c89" in #( x) : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } ;; #( xno) : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } ;; #( *) : ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c89" CC="$CC$ac_prog_cc_stdc_options" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno then : ac_prog_cc_stdc=c89 ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 else $as_nop ac_prog_cc_stdc=no ac_cv_prog_cc_stdc=no fi fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_AR+y} then : printf %s "(cached) " >&6 else $as_nop case $AR in [\\/]* | ?:[\\/]*) ac_cv_path_AR="$AR" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_AR="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi AR=$ac_cv_path_AR if test -n "$AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 printf "%s\n" "$AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi for ac_prog in codesign true do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_CODE_SIGN+y} then : printf %s "(cached) " >&6 else $as_nop case $CODE_SIGN in [\\/]* | ?:[\\/]*) ac_cv_path_CODE_SIGN="$CODE_SIGN" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_CODE_SIGN="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi CODE_SIGN=$ac_cv_path_CODE_SIGN if test -n "$CODE_SIGN"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CODE_SIGN" >&5 printf "%s\n" "$CODE_SIGN" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CODE_SIGN" && break done # Extract the first word of "mkdir", so it can be a program name with args. set dummy mkdir; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_MKDIR+y} then : printf %s "(cached) " >&6 else $as_nop case $MKDIR in [\\/]* | ?:[\\/]*) ac_cv_path_MKDIR="$MKDIR" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_MKDIR="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi MKDIR=$ac_cv_path_MKDIR if test -n "$MKDIR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR" >&5 printf "%s\n" "$MKDIR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "rm", so it can be a program name with args. set dummy rm; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_RM+y} then : printf %s "(cached) " >&6 else $as_nop case $RM in [\\/]* | ?:[\\/]*) ac_cv_path_RM="$RM" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_RM="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi RM=$ac_cv_path_RM if test -n "$RM"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RM" >&5 printf "%s\n" "$RM" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "ln", so it can be a program name with args. set dummy ln; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_LN+y} then : printf %s "(cached) " >&6 else $as_nop case $LN in [\\/]* | ?:[\\/]*) ac_cv_path_LN="$LN" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_LN="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi LN=$ac_cv_path_LN if test -n "$LN"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LN" >&5 printf "%s\n" "$LN" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "$ac_cv_prog_ranlib" = ":"; then ARFLAGS="crs" else ARFLAGS="cr" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for install-sh script" >&5 printf %s "checking for install-sh script... " >&6; } INSTALL="`pwd`/install-sh" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: using $INSTALL" >&5 printf "%s\n" "using $INSTALL" >&6; } if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cups-config", so it can be a program name with args. set dummy ${ac_tool_prefix}cups-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_CUPSCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $CUPSCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_CUPSCONFIG="$CUPSCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_CUPSCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi CUPSCONFIG=$ac_cv_path_CUPSCONFIG if test -n "$CUPSCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CUPSCONFIG" >&5 printf "%s\n" "$CUPSCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_CUPSCONFIG"; then ac_pt_CUPSCONFIG=$CUPSCONFIG # Extract the first word of "cups-config", so it can be a program name with args. set dummy cups-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_CUPSCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_CUPSCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_CUPSCONFIG="$ac_pt_CUPSCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_CUPSCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_CUPSCONFIG=$ac_cv_path_ac_pt_CUPSCONFIG if test -n "$ac_pt_CUPSCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_CUPSCONFIG" >&5 printf "%s\n" "$ac_pt_CUPSCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_CUPSCONFIG" = x; then CUPSCONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CUPSCONFIG=$ac_pt_CUPSCONFIG fi else CUPSCONFIG="$ac_cv_path_CUPSCONFIG" fi if test "x$CUPSCONFIG" = x; then as_fn_error $? "Sorry, this software requires libcups-dev." "$LINENO" 5 fi case "`$CUPSCONFIG --api-version`" in 1.* | 2.0 | 2.1) as_fn_error $? "Sorry, you need CUPS 2.2.0 or higher." "$LINENO" 5 ;; esac CUPS_SERVERROOT="`$CUPSCONFIG --serverroot`" printf "%s\n" "#define CUPS_SERVERROOT \"$CUPS_SERVERROOT\"" >>confdefs.h if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKGCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $PKGCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKGCONFIG="$PKGCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PKGCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKGCONFIG=$ac_cv_path_PKGCONFIG if test -n "$PKGCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKGCONFIG" >&5 printf "%s\n" "$PKGCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_PKGCONFIG"; then ac_pt_PKGCONFIG=$PKGCONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKGCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_PKGCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKGCONFIG="$ac_pt_PKGCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKGCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKGCONFIG=$ac_cv_path_ac_pt_PKGCONFIG if test -n "$ac_pt_PKGCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKGCONFIG" >&5 printf "%s\n" "$ac_pt_PKGCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_PKGCONFIG" = x; then PKGCONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKGCONFIG=$ac_pt_PKGCONFIG fi else PKGCONFIG="$ac_cv_path_PKGCONFIG" fi # Check whether --enable-static_cups was given. if test ${enable_static_cups+y} then : enableval=$enable_static_cups; fi if test x$enable_static_cups = xyes; then CFLAGS="$CFLAGS `cups-config --static --cflags`" DSOFLAGS="$DSOFLAGS `cups-config --static --ldflags`" LDFLAGS="$LDFLAGS `cups-config --static --ldflags`" LIBS="$LIBS `cups-config --image --static --libs`" else CFLAGS="$CFLAGS `cups-config --cflags`" DSOFLAGS="$DSOFLAGS `cups-config --ldflags`" LDFLAGS="$LDFLAGS `cups-config --ldflags`" LIBS="$LIBS `cups-config --image --libs`" fi ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" if test "x$ac_cv_func_strlcpy" = xyes then : printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h fi ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_h" = xyes then : fi if test x$ac_cv_header_pthread_h != xyes; then as_fn_error $? "Sorry, this software requires POSIX threading support." "$LINENO" 5 fi for flag in -lpthreads -lpthread -pthread; do { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create using $flag" >&5 printf %s "checking for pthread_create using $flag... " >&6; } SAVELIBS="$LIBS" LIBS="$flag $LIBS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { pthread_create(0, 0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : have_pthread=yes else $as_nop LIBS="$SAVELIBS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test x$have_pthread = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$CFLAGS -D_THREAD_SAFE -D_REENTRANT" break else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi done # Check whether --with-dnssd was given. if test ${with_dnssd+y} then : withval=$with_dnssd; fi if test "x$with_dnssd" != xmdnsresponder -a "x$with_dnssd" != xno -a "x$PKGCONFIG" != x -a x$host_os_name != xdarwin; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Avahi" >&5 printf %s "checking for Avahi... " >&6; } if $PKGCONFIG --exists avahi-client; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$CFLAGS `$PKGCONFIG --cflags avahi-client`" LIBS="$LIBS `$PKGCONFIG --libs avahi-client`" printf "%s\n" "#define HAVE_AVAHI 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if test x$with_dnssd = xavahi; then as_fn_error $? "libavahi-client-dev needed for --with-dnssd=avahi." "$LINENO" 5 fi fi elif test x$with_dnssd = xavahi; then as_fn_error $? "pkgconfig and libavahi-client-dev needed for --with-dnssd=avahi." "$LINENO" 5 elif test x$with_dnssd != xavahi -a "x$with_dnssd" != xno; then ac_fn_c_check_header_compile "$LINENO" "dns_sd.h" "ac_cv_header_dns_sd_h" "$ac_includes_default" if test "x$ac_cv_header_dns_sd_h" = xyes then : case "$host_os_name" in darwin*) # Darwin and macOS... printf "%s\n" "#define HAVE_DNSSD 1" >>confdefs.h LIBS="$LIBS -framework CoreFoundation -framework SystemConfiguration" ;; *) # All others... { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for current version of dns_sd library" >&5 printf %s "checking for current version of dns_sd library... " >&6; } SAVELIBS="$LIBS" LIBS="$LIBS -ldns_sd" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int constant = kDNSServiceFlagsShareConnection; unsigned char txtRecord[100]; uint8_t valueLen; TXTRecordGetValuePtr(sizeof(txtRecord), txtRecord, "value", &valueLen); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_DNSSD 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LIBS="$SAVELIBS" if test x$with_dnssd = xmdnsresponder; then as_fn_error $? "mDNSResponder required for --with-dnssd=mdnsresponder." "$LINENO" 5 fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi fi PKGCONFIG_GNUTLS="" if test "x$PKGCONFIG" != x; then if $PKGCONFIG --exists gnutls; then CFLAGS="$CFLAGS `$PKGCONFIG --cflags gnutls`" LIBS="$LIBS `$PKGCONFIG --libs gnutls`" printf "%s\n" "#define HAVE_GNUTLS 1" >>confdefs.h PKGCONFIG_GNUTLS="gnutls >= 3.0," fi fi ac_fn_c_check_header_compile "$LINENO" "sys/random.h" "ac_cv_header_sys_random_h" "$ac_includes_default" if test "x$ac_cv_header_sys_random_h" = xyes then : printf "%s\n" "#define HAVE_SYS_RANDOM_H 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "arc4random" "ac_cv_func_arc4random" if test "x$ac_cv_func_arc4random" = xyes then : printf "%s\n" "#define HAVE_ARC4RANDOM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getrandom" "ac_cv_func_getrandom" if test "x$ac_cv_func_getrandom" = xyes then : printf "%s\n" "#define HAVE_GETRANDOM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "gnutls_rnd" "ac_cv_func_gnutls_rnd" if test "x$ac_cv_func_gnutls_rnd" = xyes then : printf "%s\n" "#define HAVE_GNUTLS_RND 1" >>confdefs.h fi # Check whether --enable-libjpeg was given. if test ${enable_libjpeg+y} then : enableval=$enable_libjpeg; fi PKGCONFIG_LIBJPEG="" if test x$enable_libjpeg != xno; then have_jpeg=no if test "x$PKGCONFIG" != x; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libjpeg via pkg-config" >&5 printf %s "checking for libjpeg via pkg-config... " >&6; } if $PKGCONFIG --exists libjpeg; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; }; printf "%s\n" "#define HAVE_LIBJPEG 1" >>confdefs.h CFLAGS="$CFLAGS `$PKGCONFIG --cflags libjpeg`" LIBS="$LIBS `$PKGCONFIG --libs libjpeg`" PKGCONFIG_LIBJPEG="libjpeg," have_jpeg=yes else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; fi fi if test $have_jpeg = no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing jpeg_start_decompress" >&5 printf %s "checking for library containing jpeg_start_decompress... " >&6; } if test ${ac_cv_search_jpeg_start_decompress+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char jpeg_start_decompress (); int main (void) { return jpeg_start_decompress (); ; return 0; } _ACEOF for ac_lib in '' turbojpeg jpeg do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_jpeg_start_decompress=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_jpeg_start_decompress+y} then : break fi done if test ${ac_cv_search_jpeg_start_decompress+y} then : else $as_nop ac_cv_search_jpeg_start_decompress=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_jpeg_start_decompress" >&5 printf "%s\n" "$ac_cv_search_jpeg_start_decompress" >&6; } ac_res=$ac_cv_search_jpeg_start_decompress if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" printf "%s\n" "#define HAVE_LIBJPEG 1" >>confdefs.h have_jpeg=yes fi fi if test x$enable_libjpeg = xyes -a $have_jpeg = no; then as_fn_error $? "libjpeg-dev 8 or later required for --enable-libjpeg." "$LINENO" 5 fi fi # Check whether --enable-libpng was given. if test ${enable_libpng+y} then : enableval=$enable_libpng; fi PKGCONFIG_LIBPNG="" if test "x$PKGCONFIG" != x -a x$enable_libpng != xno; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libpng-1.6.x" >&5 printf %s "checking for libpng-1.6.x... " >&6; } if $PKGCONFIG --exists libpng16; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; }; printf "%s\n" "#define HAVE_LIBPNG 1" >>confdefs.h CFLAGS="$CFLAGS `$PKGCONFIG --cflags libpng16`" LIBS="$LIBS `$PKGCONFIG --libs libpng16` -lz" PKGCONFIG_LIBPNG="libpng >= 1.6," else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; if test x$enable_libpng = xyes; then as_fn_error $? "libpng-dev 1.6 or later required for --enable-libpng." "$LINENO" 5 fi fi elif test x$enable_libpng = xyes; then as_fn_error $? "libpng-dev 1.6 or later required for --enable-libpng." "$LINENO" 5 fi # Check whether --enable-libusb was given. if test ${enable_libusb+y} then : enableval=$enable_libusb; fi PKGCONFIG_LIBUSB="" if test "x$PKGCONFIG" != x -a x$enable_libusb != xno; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libusb-1.0" >&5 printf %s "checking for libusb-1.0... " >&6; } if $PKGCONFIG --exists libusb-1.0; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_LIBUSB 1" >>confdefs.h CFLAGS="$CFLAGS `$PKGCONFIG --cflags libusb-1.0`" LIBS="$LIBS `$PKGCONFIG --libs libusb-1.0`" if test "x$host_os_name" = xdarwin; then LIBS="$LIBS -framework IOKit" fi PKGCONFIG_LIBUSB="libusb-1.0 >= 1.0," else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if test x$enable_libusb = xyes; then as_fn_error $? "libusb-1.0-0-dev required for --enable-libusb." "$LINENO" 5 fi fi elif test x$enable_libusb = xyes; then as_fn_error $? "pkgconfig and libusb-1.0-0-dev required for --enable-libusb." "$LINENO" 5 fi # Check whether --enable-pam was given. if test ${enable_pam+y} then : enableval=$enable_pam; fi if test x$enable_libpam != xno; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else $as_nop ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : printf "%s\n" "#define HAVE_LIBDL 1" >>confdefs.h LIBS="-ldl $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5 printf %s "checking for pam_start in -lpam... " >&6; } if test ${ac_cv_lib_pam_pam_start+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lpam $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pam_start (); int main (void) { return pam_start (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pam_pam_start=yes else $as_nop ac_cv_lib_pam_pam_start=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5 printf "%s\n" "$ac_cv_lib_pam_pam_start" >&6; } if test "x$ac_cv_lib_pam_pam_start" = xyes then : printf "%s\n" "#define HAVE_LIBPAM 1" >>confdefs.h LIBS="$LIBS -lpam" fi ac_fn_c_check_header_compile "$LINENO" "security/pam_appl.h" "ac_cv_header_security_pam_appl_h" "$ac_includes_default" if test "x$ac_cv_header_security_pam_appl_h" = xyes then : printf "%s\n" "#define HAVE_SECURITY_PAM_APPL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "pam/pam_appl.h" "ac_cv_header_pam_pam_appl_h" "$ac_includes_default" if test "x$ac_cv_header_pam_pam_appl_h" = xyes then : printf "%s\n" "#define HAVE_PAM_PAM_APPL_H 1" >>confdefs.h fi if test x$ac_pam_start = xno -a x$enable_libpam = xyes; then as_fn_error $? "libpam-dev required for --enable-libpam." "$LINENO" 5 fi fi if test "$host_os_name" = darwin; then LIBPAPPL="libpappl.1.dylib" else LIBPAPPL="libpappl.so.1" fi # Check whether --enable-debug was given. if test ${enable_debug+y} then : enableval=$enable_debug; fi # Check whether --enable-maintainer was given. if test ${enable_maintainer+y} then : enableval=$enable_maintainer; fi # Check whether --enable-sanitizer was given. if test ${enable_sanitizer+y} then : enableval=$enable_sanitizer; fi if test x$enable_debug = xyes; then OPTIM="-g" CSFLAGS="" else OPTIM="-g -Os" CSFLAGS="-o runtime" fi WARNINGS="" if test -n "$GCC"; then if test x$enable_sanitizer = xyes; then # Use -fsanitize=address with debugging... OPTIM="-g -fsanitize=address" else # Otherwise use the Fortify enhancements to catch any unbounded # string operations... CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2" fi if test "$host_os_name" = darwin; then # macOS Clang is conservative about "-Wall" warnings WARNINGS="-Wall -Wunused" else # Newer GCCs generate a lot of not-useful/reliable warnings with "-Wall" WARNINGS="-Wall -Wunused -Wno-unused-result -Wno-char-subscripts -Wno-format-y2k -Wno-maybe-uninitialized -Wno-switch -Wno-format-truncation" fi if test x$enable_maintainer = xyes; then WARNINGS="$WARNINGS -Werror" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fPIE" >&5 printf %s "checking whether compiler supports -fPIE... " >&6; } OLDCFLAGS="$CFLAGS" case "$host_os_name" in darwin*) CFLAGS="$CFLAGS -fPIC -fPIE -Wl,-pie" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : OLDCFLAGS="-fPIC $OLDCFLAGS" LDFLAGS="-fPIE -Wl,-pie $LDFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; *) CFLAGS="$CFLAGS -fPIC -fPIE -pie" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : OLDCFLAGS="-fPIC $OLDCFLAGS" LDFLAGS="-fPIE -pie $LDFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac CFLAGS="$OLDCFLAGS" fi # Check whether --with-dsoflags was given. if test ${with_dsoflags+y} then : withval=$with_dsoflags; DSOFLAGS="$withval $DSOFLAGS" fi # Check whether --with-ldflags was given. if test ${with_ldflags+y} then : withval=$with_ldflags; LDFLAGS="$withval $LDFLAGS" fi ac_config_files="$ac_config_files Makedefs pappl/pappl.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by pappl $as_me 1.0.3, which was generated by GNU Autoconf 2.70. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to . pappl home page: ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ pappl config.status 1.0.3 configured by $0, generated by GNU Autoconf 2.70, with options \\"\$ac_cs_config\\" Copyright (C) 2020 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makedefs") CONFIG_FILES="$CONFIG_FILES Makedefs" ;; "pappl/pappl.pc") CONFIG_FILES="$CONFIG_FILES pappl/pappl.pc" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi pappl-1.0.3/configure.ac000066400000000000000000000263601403603036100151200ustar00rootroot00000000000000dnl dnl Configuration script for the Printer Application Framework dnl dnl Copyright © 2019-2021 by Michael R Sweet dnl dnl Licensed under Apache License v2.0. See the file "LICENSE" for more dnl information. dnl dnl We need at least autoconf 2.60... AC_PREREQ(2.60) dnl Package name and version... AC_INIT([pappl], [1.0.3], [https://github.com/michaelrsweet/pappl/issues], [pappl], [https://www.msweet.org/pappl]) AC_CONFIG_HEADERS([config.h]) PAPPL_VERSION="AC_PACKAGE_VERSION" PAPPL_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`" PAPPL_VERSION_MINOR="`echo AC_PACKAGE_VERSION | awk -F. '{printf("%d\n",$2);}'`" AC_SUBST([PAPPL_VERSION]) AC_SUBST([PAPPL_VERSION_MAJOR]) AC_SUBST([PAPPL_VERSION_MINOR]) AC_DEFINE_UNQUOTED([PAPPL_VERSION], "$PAPPL_VERSION", [Version number]) AC_DEFINE_UNQUOTED([PAPPL_VERSION_MAJOR], $PAPPL_VERSION_MAJOR, [Major version number]) AC_DEFINE_UNQUOTED([PAPPL_VERSION_MINOR], $PAPPL_VERSION_MINOR, [Minor version number]) dnl Get the build and host platforms and split the host_os value AC_CANONICAL_BUILD AC_CANONICAL_HOST [host_os_name=`echo $host_os | sed -e '1,$s/[0-9.]*$//g'`] [host_os_version=`echo $host_os | sed -e '1,$s/^[^0-9.]*//g' | awk -F. '{print $1 $2}'`] # Linux often does not yield an OS version we can use... if test "x$host_os_version" = x; then host_os_version="0" fi dnl Compiler options... CFLAGS="${CFLAGS:=}" CXXFLAGS="${CXXFLAGS:=}" DSOFLAGS="${DSOFLAGS:=}" LDFLAGS="${LDFLAGS:=}" LIBS="${LIBS:=}" AC_SUBST(DSOFLAGS) dnl Standard programs... AC_PROG_CC(clang cc gcc) AC_PROG_RANLIB AC_PATH_PROG(AR,ar) AC_PATH_PROGS(CODE_SIGN, codesign true) AC_PATH_PROG(MKDIR,mkdir) AC_PATH_PROG(RM,rm) AC_PATH_PROG(LN,ln) dnl Figure out the correct "ar" command flags... if test "$ac_cv_prog_ranlib" = ":"; then ARFLAGS="crs" else ARFLAGS="cr" fi AC_SUBST(ARFLAGS) dnl install-sh AC_MSG_CHECKING([for install-sh script]) INSTALL="`pwd`/install-sh" AC_SUBST(INSTALL) AC_MSG_RESULT([using $INSTALL]) dnl CUPS AC_PATH_TOOL(CUPSCONFIG, cups-config) if test "x$CUPSCONFIG" = x; then AC_MSG_ERROR([Sorry, this software requires libcups-dev.]) fi case "`$CUPSCONFIG --api-version`" in 1.* | 2.0 | 2.1) AC_MSG_ERROR([Sorry, you need CUPS 2.2.0 or higher.]) ;; esac CUPS_SERVERROOT="`$CUPSCONFIG --serverroot`" AC_DEFINE_UNQUOTED(CUPS_SERVERROOT, "$CUPS_SERVERROOT") dnl Check for pkg-config, which is used for some other tests later on... AC_PATH_TOOL(PKGCONFIG, pkg-config) dnl CUPS library... AC_ARG_ENABLE(static_cups, [ --enable-static-cups use static CUPS libraries, default=no]) if test x$enable_static_cups = xyes; then CFLAGS="$CFLAGS `cups-config --static --cflags`" DSOFLAGS="$DSOFLAGS `cups-config --static --ldflags`" LDFLAGS="$LDFLAGS `cups-config --static --ldflags`" LIBS="$LIBS `cups-config --image --static --libs`" else CFLAGS="$CFLAGS `cups-config --cflags`" DSOFLAGS="$DSOFLAGS `cups-config --ldflags`" LDFLAGS="$LDFLAGS `cups-config --ldflags`" LIBS="$LIBS `cups-config --image --libs`" fi dnl String functions... AC_CHECK_FUNCS(strlcpy) dnl POSIX threads... AC_CHECK_HEADER(pthread.h) if test x$ac_cv_header_pthread_h != xyes; then AC_MSG_ERROR([Sorry, this software requires POSIX threading support.]) fi for flag in -lpthreads -lpthread -pthread; do AC_MSG_CHECKING([for pthread_create using $flag]) SAVELIBS="$LIBS" LIBS="$flag $LIBS" AC_LANG([C]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([#include ],[pthread_create(0, 0, 0, 0);])], [have_pthread=yes], [LIBS="$SAVELIBS"]) if test x$have_pthread = xyes; then AC_MSG_RESULT([yes]) CFLAGS="$CFLAGS -D_THREAD_SAFE -D_REENTRANT" break else AC_MSG_RESULT([no]) fi done dnl DNS-SD support... AC_ARG_WITH(dnssd, [ --with-dnssd=LIBRARY set DNS-SD library (auto, avahi, mdnsresponder)]) if test "x$with_dnssd" != xmdnsresponder -a "x$with_dnssd" != xno -a "x$PKGCONFIG" != x -a x$host_os_name != xdarwin; then AC_MSG_CHECKING(for Avahi) if $PKGCONFIG --exists avahi-client; then AC_MSG_RESULT([yes]) CFLAGS="$CFLAGS `$PKGCONFIG --cflags avahi-client`" LIBS="$LIBS `$PKGCONFIG --libs avahi-client`" AC_DEFINE([HAVE_AVAHI], 1, [DNS-SD (mDNSResponder or Avahi)]) else AC_MSG_RESULT([no]) if test x$with_dnssd = xavahi; then AC_MSG_ERROR([libavahi-client-dev needed for --with-dnssd=avahi.]) fi fi elif test x$with_dnssd = xavahi; then AC_MSG_ERROR([pkgconfig and libavahi-client-dev needed for --with-dnssd=avahi.]) elif test x$with_dnssd != xavahi -a "x$with_dnssd" != xno; then AC_CHECK_HEADER(dns_sd.h, [ case "$host_os_name" in darwin*) # Darwin and macOS... AC_DEFINE([HAVE_DNSSD], 1, [DNS-SD (mDNSResponder or Avahi)]) LIBS="$LIBS -framework CoreFoundation -framework SystemConfiguration" ;; *) # All others... AC_MSG_CHECKING(for current version of dns_sd library) SAVELIBS="$LIBS" LIBS="$LIBS -ldns_sd" AC_TRY_COMPILE([#include ],[ int constant = kDNSServiceFlagsShareConnection; unsigned char txtRecord[100]; uint8_t valueLen; TXTRecordGetValuePtr(sizeof(txtRecord), txtRecord, "value", &valueLen);],[ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_DNSSD], 1, [DNS-SD (mDNSResponder or Avahi)])],[ AC_MSG_RESULT([no]) LIBS="$SAVELIBS" if test x$with_dnssd = xmdnsresponder; then AC_MSG_ERROR([mDNSResponder required for --with-dnssd=mdnsresponder.]) fi]) ;; esac ]) fi dnl GNU TLS... PKGCONFIG_GNUTLS="" AC_SUBST(PKGCONFIG_GNUTLS) if test "x$PKGCONFIG" != x; then if $PKGCONFIG --exists gnutls; then CFLAGS="$CFLAGS `$PKGCONFIG --cflags gnutls`" LIBS="$LIBS `$PKGCONFIG --libs gnutls`" AC_DEFINE(HAVE_GNUTLS) PKGCONFIG_GNUTLS="gnutls >= 3.0," fi fi dnl Random number support... AC_CHECK_HEADER(sys/random.h, AC_DEFINE([HAVE_SYS_RANDOM_H], 1, [Have header?])) AC_CHECK_FUNCS(arc4random getrandom gnutls_rnd) dnl libjpeg... AC_ARG_ENABLE(libjpeg, [ --enable-libjpeg use libjpeg for JPEG printing, default=auto]) PKGCONFIG_LIBJPEG="" AC_SUBST(PKGCONFIG_LIBJPEG) if test x$enable_libjpeg != xno; then have_jpeg=no if test "x$PKGCONFIG" != x; then AC_MSG_CHECKING([for libjpeg via pkg-config]) if $PKGCONFIG --exists libjpeg; then AC_MSG_RESULT([yes]); AC_DEFINE([HAVE_LIBJPEG], 1, [libjpeg]) CFLAGS="$CFLAGS `$PKGCONFIG --cflags libjpeg`" LIBS="$LIBS `$PKGCONFIG --libs libjpeg`" PKGCONFIG_LIBJPEG="libjpeg," have_jpeg=yes else AC_MSG_RESULT([no]); fi fi if test $have_jpeg = no; then AC_SEARCH_LIBS(jpeg_start_decompress, turbojpeg jpeg,[ AC_DEFINE([HAVE_LIBJPEG], 1, [libjpeg]) have_jpeg=yes]) fi if test x$enable_libjpeg = xyes -a $have_jpeg = no; then AC_MSG_ERROR([libjpeg-dev 8 or later required for --enable-libjpeg.]) fi fi dnl libpng... AC_ARG_ENABLE(libpng, [ --enable-libpng use libpng for PNG printing, default=auto]) PKGCONFIG_LIBPNG="" AC_SUBST(PKGCONFIG_LIBPNG) if test "x$PKGCONFIG" != x -a x$enable_libpng != xno; then AC_MSG_CHECKING([for libpng-1.6.x]) if $PKGCONFIG --exists libpng16; then AC_MSG_RESULT([yes]); AC_DEFINE([HAVE_LIBPNG], 1, [libpng]) CFLAGS="$CFLAGS `$PKGCONFIG --cflags libpng16`" LIBS="$LIBS `$PKGCONFIG --libs libpng16` -lz" PKGCONFIG_LIBPNG="libpng >= 1.6," else AC_MSG_RESULT([no]); if test x$enable_libpng = xyes; then AC_MSG_ERROR([libpng-dev 1.6 or later required for --enable-libpng.]) fi fi elif test x$enable_libpng = xyes; then AC_MSG_ERROR([libpng-dev 1.6 or later required for --enable-libpng.]) fi dnl libusb... AC_ARG_ENABLE(libusb, [ --enable-libusb use libusb for USB printing, default=auto]) PKGCONFIG_LIBUSB="" AC_SUBST(PKGCONFIG_LIBUSB) if test "x$PKGCONFIG" != x -a x$enable_libusb != xno; then AC_MSG_CHECKING([for libusb-1.0]) if $PKGCONFIG --exists libusb-1.0; then AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_LIBUSB], 1, [libusb]) CFLAGS="$CFLAGS `$PKGCONFIG --cflags libusb-1.0`" LIBS="$LIBS `$PKGCONFIG --libs libusb-1.0`" if test "x$host_os_name" = xdarwin; then LIBS="$LIBS -framework IOKit" fi PKGCONFIG_LIBUSB="libusb-1.0 >= 1.0," else AC_MSG_RESULT([no]) if test x$enable_libusb = xyes; then AC_MSG_ERROR([libusb-1.0-0-dev required for --enable-libusb.]) fi fi elif test x$enable_libusb = xyes; then AC_MSG_ERROR([pkgconfig and libusb-1.0-0-dev required for --enable-libusb.]) fi dnl PAM support... AC_ARG_ENABLE(pam, [ --enable-libpam use libpam for authentication, default=auto]) if test x$enable_libpam != xno; then dnl PAM needs dlopen... AC_CHECK_LIB([dl], [dlopen]) AC_CHECK_LIB([pam], [pam_start], [ AC_DEFINE([HAVE_LIBPAM], 1, [libpam]) LIBS="$LIBS -lpam"]) dnl PAM has two "standard" locations for its header... AC_CHECK_HEADER(security/pam_appl.h, AC_DEFINE([HAVE_SECURITY_PAM_APPL_H], 1, [Have header?])) AC_CHECK_HEADER(pam/pam_appl.h, AC_DEFINE([HAVE_PAM_PAM_APPL_H], 1, [Have header?])) if test x$ac_pam_start = xno -a x$enable_libpam = xyes; then AC_MSG_ERROR([libpam-dev required for --enable-libpam.]) fi fi dnl Library target... if test "$host_os_name" = darwin; then LIBPAPPL="libpappl.1.dylib" else LIBPAPPL="libpappl.so.1" fi AC_SUBST(LIBPAPPL) dnl Extra compiler options... AC_ARG_ENABLE(debug, [ --enable-debug turn on debugging, default=no]) AC_ARG_ENABLE(maintainer, [ --enable-maintainer turn on maintainer mode, default=no]) AC_ARG_ENABLE(sanitizer, [ --enable-sanitizer build with AddressSanitizer, default=no]) if test x$enable_debug = xyes; then OPTIM="-g" CSFLAGS="" else OPTIM="-g -Os" CSFLAGS="-o runtime" fi WARNINGS="" AC_SUBST(CSFLAGS) AC_SUBST(OPTIM) AC_SUBST(WARNINGS) if test -n "$GCC"; then if test x$enable_sanitizer = xyes; then # Use -fsanitize=address with debugging... OPTIM="-g -fsanitize=address" else # Otherwise use the Fortify enhancements to catch any unbounded # string operations... CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2" fi dnl Show all standard warnings + unused variables when compiling... if test "$host_os_name" = darwin; then # macOS Clang is conservative about "-Wall" warnings WARNINGS="-Wall -Wunused" else # Newer GCCs generate a lot of not-useful/reliable warnings with "-Wall" WARNINGS="-Wall -Wunused -Wno-unused-result -Wno-char-subscripts -Wno-format-y2k -Wno-maybe-uninitialized -Wno-switch -Wno-format-truncation" fi dnl Maintainer mode enables -Werror... if test x$enable_maintainer = xyes; then WARNINGS="$WARNINGS -Werror" fi dnl See if PIE options are supported... AC_MSG_CHECKING(whether compiler supports -fPIE) OLDCFLAGS="$CFLAGS" case "$host_os_name" in darwin*) CFLAGS="$CFLAGS -fPIC -fPIE -Wl,-pie" AC_TRY_COMPILE(,,[ OLDCFLAGS="-fPIC $OLDCFLAGS" LDFLAGS="-fPIE -Wl,-pie $LDFLAGS" AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) ;; *) CFLAGS="$CFLAGS -fPIC -fPIE -pie" AC_TRY_COMPILE(,,[ OLDCFLAGS="-fPIC $OLDCFLAGS" LDFLAGS="-fPIE -pie $LDFLAGS" AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) ;; esac CFLAGS="$OLDCFLAGS" fi dnl Extra linker options... AC_ARG_WITH(dsoflags, [ --with-dsoflags=... Specify additional DSOFLAGS ], DSOFLAGS="$withval $DSOFLAGS") AC_ARG_WITH(ldflags, [ --with-ldflags=... Specify additional LDFLAGS ], LDFLAGS="$withval $LDFLAGS") dnl Generate the Makefile... AC_OUTPUT(Makedefs pappl/pappl.pc) pappl-1.0.3/doc/000077500000000000000000000000001403603036100133705ustar00rootroot00000000000000pappl-1.0.3/doc/Makefile000066400000000000000000000013151403603036100150300ustar00rootroot00000000000000# # Documentation page makefile for the Printer Application Framework # # Copyright © 2020 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # include ../Makedefs DOCFILES = \ pappl.html \ pappl-512.png \ pappl-block.png # Make everything all: # Clean everything clean: # Clean all non-distribution files distclean: clean # Update dependencies depend: # Install everything install: echo Installing documentation in $(BUILDROOT)/$(datadir)/doc/pappl... $(INSTALL) -d -m 755 $(BUILDROOT)/$(datadir)/doc/pappl for file in $(DOCFILES); do \ $(INSTALL) -c -m 644 $$file $(BUILDROOT)/$(datadir)/doc/pappl; \ done # Test everything test: pappl-1.0.3/doc/pappl-512.png000066400000000000000000000632361403603036100155310ustar00rootroot00000000000000PNG  IHDRxiCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*i(0eiTXtXML:com.adobe.xmp Michael Sweet Copyright 2020 Michael Sweet New Image New Image ^IDATx Uř/pA8 h"8,8("qVPAyH4Fy󬀭_ߺk:kuJ:f0Qvw]{y<{r]ާ<BU88[!J\e #OۤQB)]:uq>ʩu.v@!Tre~; sqԨQCG=hȑ#6z#9M/ƌ塇 ?VY#F4`C0PN&Xti(/{ې!C>0`}GAn݂Ν;۷֭[yPm~_cOV[sM>xy.dpT77ۆi[ ~޷o߭ݻw:tP%(W?^^7e汾uCu7B)2Q Nafo5aMjF:~'o|0B~t28I榮w}7+4B֛(L.F a`@СC;σ|v Zj#_oȏUs;0~Azdnu&لt6wؑc?c?m lf(=]4"R:~Mr7]vh˯ȿh\.evQ6(0PsCj6[vaZt؏؏%O3nHQ5@ߌ2׃:hS6~D؏_,u)Cfq=zQ -7!bo|!P!#@>!t@`FZ8ߚ-Ś PLz`ypS*&-@4 2Je /j ؏؏UfY? } v7sG  ڔKu??~~/SW&b; Xlp\o)~~~Og6a}@h@]C@Xͷu]A`?c?'M_cgI [P)p̄Oh[G~!ˏɟ]h/0h'`k)~J. :Mr- PJɟɯ]k? "="%,}1nn۶-!0~~ n Pm@pfoB$5c?c?Wos0?Шvhc?Jh~NJW8K`.! UH9R3K_d?8L |h.W1ܚ9MSud%wBP%'~-6?耰.!.SB3u8p!O$.ZW+$U"?R  K.c# nM@ۤAgI`?c?c/ho1j40!wD:B]' 1EF }w"xzOspkR*EGS0ρ@ww. l4:CEp W )?jLJ(PzwfgoN}nP*_ !pg?s5{zcRGRS9 +8?51 g؂EY`3E{u'SWM)FG;T?+"6\KOƯ~b4+`X8+@q wPX3f̤@:|~"2O_ׯia*@TV )& P+zѺv-9 `̘8|:PAܿ#Fc֌㌴aa1ݳX\0ο SlmժUbBi8|RIpI_ڵF@W]&gL$0 lf>@p7 m8p YaA' 0ȉ͂fnnݺ:TdɿH31& W P4@Y&%4@.]N E/fbYw+_0`pHf`ZCkMS jMMp5iwu@(i[ø*;t#$~zv>'Q(޺vu@wR[Rԕ{x\CTe;WSǣ~`W>:,X@C 3Uqfߚ5pF.(h4SÆ #|rH}rg#_)zD@v}1 pW}) C#gVbzٿa>y3:"*@u_KTx76#)V3rR;?P ֙46 iw6@AuUG(p5~^10S ߄":J,g#Aܷ:[]SIh1iC 8'&(=0 0Kd . TP|?kad@%kI]l! / / T6O>_m(ԻJqM%{A}Jl4. 3 0k<ܣGr5="]x̙ `:hd;Q__? = qj5S7j(i49b:[oT|ԩS' qj1bFxz3 ۷~&'Ps_~?ON6:-B _oYg(&vII<Pt Q0~Ԩ|U)Bҏ{8b[nFxo 湮,I~"OFHת{xpQ 0> 84T]5Gt>o|D Z1=Q5I~5_e:{!*kkѷ i5pq00jO6o5-%Cq]EMtU/[ѿ`SZ̨O? K=z,Dg6L ތӣlHѿ}MT_6wk"Z hg-Mj;WM;~Z9[ o#R+/VG9vwTFR,ŀj 06|D~=r y?)>tp{Sdc7J@AXtFRU#/ۜFuڱ缟C>N9='ɋ}Vdѿ/)@jGD] `ء`cDYr}G*~?:o/۸_ j99>~u^k0cEeFN?¾^WDߋ0/F:g47a5!@}`d]~_=8\Ǣ*~ٟA{+ ܿ5Zᯐ׋*d;pmBV_Li]._#) vC vX#E?=ff/m0%%( `R]d{Se6sh@BP6h Ϸ? h@@P6(wP Ϸ?ѿ: e%x sQ-D s!vcc@RH1F%S@vGU?B(A``J :BrE` -*d߉O E:8vKHXX??" A`FUM@ɻ~[ Z?kyI@w>߳^yG jh{kl0jTi@#F)@i`$60nT۟Gz݌aC5Y`*)*@C৞{w]dlum'x'ыj;bV^fFl05v߻.z ջ2'@ 4e@S>c;v 6o8%Z3#g0zKԓ@=V='aGf[sE@UqLrXF!pZlyY]#]7#@)שt|FU .1\*KQOHU " 5@ƈȆP;Q_;P J0D@T5EࡱStͶE8]㮄@\-n7@9 iVSZg@#y] ]]\ 5kE}!?@XMiWMt iDaO+t ^_`6 4!rOԏG5kv9؆3Zk:fLEo?n3R}vSbgj=D!HWMksޡst-tMtmtXۿG@~D ?#8D B"jԿ›H ?P@   @:?7@ !:@:pB8P@I@$h@tp EO  h J O?:@O ?h@ttt"(~H'J O:h"@:h@@H DH`@~@?4 :ho"@Dr?Bh?h?@)httt"(~H'J O:h"@:h@@H DHHB@Ip:l@%YIz D Dq] /@ !Pj"X?XY?HZ-i ?@ h HHi0сPh%@?PrOԏG݀JOq}~K'Cd,сPh%@?@ o~ b? )?BtOI4 ::@)DE ?)@t@4mqOM^4 '?xPw:Oݽv?Pax Hh0mWm9ihn&6iq7Ʃ&~U-'BH0,jhER*ѳrl(G @6/70$ʭ^SmW(+#59bs:_Wg#@OrxaHF!t_SGS*/B~?>F!MBWu~Vh&28)IoOX%-9jZ $OvB]P%SQHO2UZZrCD#ۘ!' R쳬}Gy;7ْPs:pRHdϚ5+xr`k{BBI>(y霷5QHmMmNm([paAT?)VX~a"CQ Q6H ntyG[lIw*}P̉76dPN_[nݺ5ә?~g iK/m 6}]wݕPse, 6e[:5"Ep_(pv#Nj[oij]v_|qj'L'|cǎ ;U+H_׉p U>v)[aXW[S; )矧BguVj9-g5<V\2Xܥ;4"NusFaQ#׻DP[S8scݺuiƾҾwڵproN #`Ta j@yNioǏ⋼iEtBIrsoGv1PF͔ꫯ)Sľ̙EكסZr?O!.8@2&t.P:M{=شh߳aÆ߳fH@x(,ѶٌO;+j<kxgʙ dt}Y^;4iRm^WP /tkbXBI`? P@ !@H 8@8B@ "@"@@RBER80 80 R88@*B+;.8pfp`RpNW_}ujժ3fΜY[fMaÆ'! \uU)I=Xdɒ#^{Ϝ1cFI Gydp0PHׯVXQ=zSNO}Ǎ7ވBd>;5j;vl*OI'_}Y׼Iӧ^xap 'Aw}s[q <̞=;.袪uGuTo~ 5\n?pPW^ .lϝviϙ3'K{'xꩧx 曃SO=5/^\d()6^~u |(C?xpw_|q0q៫"w^(XvmΝs=urq9O`@nv@ `_sϥ(ߟzF۾W20[n?p_|TD]{wuW0k֬W_a@ Hn @HܾN(xG[8^z)U?>pԃF-[zu{Uw:5rW8@jY-[,k{+Vd|[?sTDݻ@ ~%{>ՂC N+T%ԲƎGҒ6P3sSNe@HPP B@pE 80R! )R\{b~<q|>>WO׭ZOKP#f @!")PXi?yG| ;DtQG&LO?=;S5kVs=lذ!x7{n͚5xسp”-֭KHJ')G)tM5GqDk55cƌ8qƥ_|2(N:f#iy5xSϽUaR/r~`ɑ9se]Pnk9S0} / N8ᄪ8y5_)SDF >촑[ǏxQuN=PM@+ %?!9̙;뮻.%֏>`Ŋ?](]ay{ zu]իSn{7xcj?:ߛo983up1ǤW_m߷KeܹꫯN[x)du4@:묳RZ_?/nw~]TM@ڵk;u9L4@z( !ΙS ,g Z~ e*ZwVLq9hӝb!w3$@@j{[j*7\o;Z'?:|FuS"x9{"ŗ qg( ?vrDټG]^ߣUX\uM9S k4*giI\?>͉W1"+WLѱܥ%MMk+7sRN_|:O&E;fB"+V`?PG)V2Kf>}'n#%9b|F!Ѵk[Mz$JkFQ{6[oxsz꩑^?>@  \7g@іƶo76W6F64lSXBu r/b~`ĉN6{5~RwG?ORs]*=-4=qGMN%yw?l~ϵ^"g sB(rb_F"M]/٣*E ׿K믿\@{u]\TdVP:[4z7ߜ + Q/ PZ \'/G†2=ϯ^:.ڸ+Ǘ,Yvt/ꟛu^{]prBnA}_.$Pw}wl =J >TJ ʠ  V~ugϞ+|c,d}0~:6gΜGnh~͊+z;W};#Z7:w~pG;#2JߡѸ{]?}f.E廑ECTA``̘1_666uO:F^i忣@Q9('o?KN:΁}[:XG UʅG}ؕ{!t>WNik|'ھlٲ)we]PEi7HI=ߕ(=֭[WgF(wV_Ǟhl6#/<:յ5ӵ"n!Fk^Oe E.V5ZԚC8Ε[{k4ѹ5GI) 7aS1CUjol=!O-(o)Ӕ<֭~ mjG ^ V675UZkS38) t ֊ [\KeaUI {GIyiM{3BF:pG n:^)le+5: ۙJ (i~(^jk TgTa_{}wȦ#.P {!l(5۷XdFQqLgY2w(ӈnF*ǎ^i6Бk-/zBT(u\#[4*}dz= {h7ZglVM￿QY=']L•^eIW k#`NǔFQN*rzmprL u:ᚯFRgTz^ ?Ii." T?1WkFB>Qb?n^-8TWj!uw\鈙R+n!k!ȃgtIu ;wFeF Cv֭bs664gokД5K2QJ'ps wvJ_DvVsNTϿ5nt-pa$S+ ;| ] C ѣ"`7nnȾ qYL KD5~a`R *70ɏZM`^@-Ө6uX~^GUǻNڭŊ)=`GJ}ؿRHw].Ҭ,(k5hk\ ;dȐv>}|Y U܎ocv7w]Æ&Q( o{Aq_z_[s&"IwF$* uk`wiC9X\2cۏ*!GUX.Pj BJ. & w tO0{F{Z"y `7ؙ] sv;RћQ J$p29:4e@) 1Ml%HPP@\(:j")G-T,p 9>uׯ!!L4:<z\6lHw?!Qv,Ew~[loI=hw%w|%@a|쳙tuqZt(h1= RZVw{!Ls`xf-mX _gH) hU%KV;(1bʆ /YFezT?wzFNgue48P==)l] Rq@PS#^:_M?Sj98~}y p#jow۴is9!,h4 }IW*pvMS4Z#W2ZT=|fΜY}s*j^F,~wkbΫ[:"Co4hj sݰQ|3ݻ'j7P$h W>_+fj*r %oM}p FpQ_7$rN߾}& Z%Ǽ6'srM5V ;NrN d1j>!f-!@z1q"n8p5_ n^`خ*z 7kWo{\[f Ma:@yO#o5F*rto'xGO۶m Gm@Mn(P\u'[L5*tyS\r խ˱A{S@* kJvء<qR# @jX״y@zyfv_D\W]Ck`;a}ĉqv_ߧgG/PQ;vp΀yZk[֙I+Ojۯ/]Nݗx-\/ bhN.{z\3l:m*K&]>8@KBi,^8 @2W{WW/oSNjrEm/v ŦrwlL% P4ZR @¡ʉ0 Zڷڹ{)$i":S?_w_73 < :~d0*uZV{l!EVfv#k$܏BI_kEYh_T/ @Zt`7w8QЖ&c?c?cuO_eGl_ClnQ=L*f1-4~~Ee Gv_1bVrU` 4-0BOQ ?L}ٌlQ!a &(Qc?c$y?5Ѝh'f+ BzFw+z_4vS) 5Սom  _,/G*QMbjyz_^4cvǝW)h`g}`Թsg~؏؏_w4t6W7>eŀF{5trmA{} /wBϮ?蠆o6v_2Q3D'ZH;/0FA؏_󒿓3sosBoHkkfտ]T*ͫڂ@. qСC7Wb@~!Ksw&SrnտoC/9DEܭ=Ð DBrsf߃wټBN'o7=ZW(?-00TtȨB؏؏_[Nӂ?'M5VGm_iel K\)Wa ?~~/uZD^_ V B+eZ h1+ EA~؏؏Щ~g]oI m_QT;ghAp~$`?c?'N"?~霿*FyO7_@wV]&8@PК3BI,B*E~T?7b?~џ]_7 ~;G+>+gD|/,rҥK&|6J(.BŒY8m_:~hz_[@YQE񇘩7hD `?c?c/$?㜿E{1ۼU8?CLJ`3[^?~~/DGKOe!>v[zwg[`!33@`+TDYݻw@b?7UV?#fZ?ې ЏqqE~:"ET1,[Mfsn݂R*H؏؟L/?O_ >gf5k>7m7cBtS7(cB˩sOl?_p JHZqRҭ PHe\8brH]wu`*6n l27b'@؏؟,?_04aBmUUb뷅~G~P/?c}Byh$-7C'%G [( 3,/| \cM^g٦sw!tЁB_y8}/wOp6?=;߆* p GZPk wwfؔOh[0) ̰ ް9f:֙~o6SLi]ѣG*4y E[B cOV[sߍ_Qv{#2';`lVwZϹr5 v`8Ц C)6pѰV_@0W@bӐ\U`}hMCdŘ1cT !Pe~Xe_~ES ~gϞ*} _ GQ:+w}y5A]1Н!+lZ8>,ְQfIoY~ șac9;9΍y!J>ѻw:#}oPmE[W?׺7%` hᴊ°KN͘Dtsz0pϒA,P;p !/?}]g4ߗ~~4`#;S 7%G8i).h 4mT`s< X@b ,X@pu*B:N:Ŏ_99ӟЏPou*Q;CD.)h@Ҧ\[#0&i] L <3ÆpBP0/l0'_BLQ:}VO'a\/89~;w FulJ.@`07)Za0`# L”- !1hN"*[?ճ:ia~\ӷ4y `g Q ndZ 6 %;~i_Be۾:ywan-sG黣}[ s82@\T\@z8Q Fž( Wr-('g:0T_ Ή 0=( z:B2rw.q#~{wkF |  vA!Tv}N᮳sH_# ~t ,p`!J]B\q}pgwro9}Q~O3jbIENDB`pappl-1.0.3/doc/pappl-block.png000066400000000000000000003303111403603036100163030ustar00rootroot00000000000000PNG  IHDR;1gAMA a cHRMz&u0`:pQ<bKGD pHYs.#.#x?vorNTϢwIDATxw|uwBC BAEiR TTT TNςwbŊbWlwُl(*6lxiߝٝwfF;+֟,G3YXD\e+[2G+-1P;XKIff+9>Ra<T`4G۪{ߛ&eXtmj9zɊԀ` a@1AGװĕ5W] ojVd88,6jZbdc- jz\g2G,Cj::ԟힼβ{Xư׌r\j5W_?st9l "u=8낲꼯}f1GϛՍ2 | 5Q^G4Hbdk );N^iR2{,;fʌoδ"uAʋmh9: <žx9# Q .f>7G@~Z)mya'g9vO9%-e#;bGmjDʯ ,i@RpqdEjyqYNd'g=vVsuOe#;쫼BO)Rۘ{9n/d1JXG$ez'N=9۝echTfdzb)a_~y+R$||(NbaRnMܸ>xO5d'~j :>*u]P6þ߼aju}i@GLuЬ8/{;NQ]Юފto낲UGs40AjFz,!e#;w_F[@+ st9,΅.Q\Y@ѣ>^]3ɷQ1ǯ\u/1.Állw:??LY@7'}lc}Ui4\+E?H@$$f@>vK}[8Od~βw=Zs֗ҧPm=33l)VKW^FjY+ iO+P#jmc_%e!>|![X2) ڨ sut9hB=0헵j[B"ubomT`Rn55HVk_k63Rbd[k:Z-MI|t:Y"58 HI>idfkESf68%T~P,I5,;Eu~v}>\Q}he*2W%j99:}/ st4*Nqn4G0W__#d4GwHkz'ƎQ skWf|*ˬP}ltHmR9:-2W_F>KwIVFSp_GygsZcJ-ߧ?bbΰ6G@|Oj)A9s:xܺaVˈ7ZXs9zm 9V]}o>2W/X@ձN(wǎyf1G,V>:Үj}8iyuP nU<-b> P?{;NQ]( =L?j@ 6sWu0WjimR9:Y^J{eG/Y@Sc- Si?gyWUOt|}?L}ds4};d"ԱZs8?Knx.؎./D٫ŏ'P5stDVy~0WX@MH̉T-}81x&+}ފq]vvg)3Ǽc9j 訸;Hz U[^PlV&9:3Re>-U}.L*ڎǩvems5\Ǖ,S^f%n.\ Kcm{^{kWE,#U`P%(e"Q?|AϢ6k*xy/!^WQ&e[@ !ZZDSڽ$'F{w<βSTggoOJQ)g2ˌ$5W]sX9aXꘫ|X_H]WŴxҗ1I;p9ڜb/NZ~%){EuG_y. Ɵ3;w86[yrRB-$avs=_**);ĉOqe۝eOeg՛4~OʝJ07G$a>X+IT)ѥ1OPo˴ s"nv'TC{;NQ)qqms|evs4=6W) S}옔ee$U1w+&pc +P#niE;ퟲ+s2|?[?\ML6Y=UJziO'9svvg)3#ImO~S=}*3~7.PlhsdstO˾ʗylfx0$%aRMsu 6X+pz$mK՛Le<@|ޯK GX@CѶ$os%ȓh5Ne\mRT;hOz1*ps:ƤxI{GRb&趀zŮmL}މy@5B _?ԗVLw|;QW{YvLc^x 8NnQ49>;{Rp9ci9粯}>36%a;}k)n7}n4Gטk8-6stcs-;}{ Nb۝eNϏ/Pu8i%9`K>=vXJ._T; u99Ӧq ?_mb^Ƨ>dJecj~a:4Wך'5`*\X}8A%JY^hJ?_y1???q,;Eu}~Lʶ*N3W fN6y]$9;n}}ڝ h]F\Su옣ԇK~,8̫׼>:aa>u7Q?wǺk2WX@gw^xÇgڨv EOW:cXsx]ҏ/3RxwQ9ݎW<ߚ/Ug:"<9Dhq,;Eu??3Wυ]s/%[.b,'vtGsjѢ(݀ż]]8(3Go[j9\5GKqCƎ:bkyut3ғd~&Uq* +TQ~.t GD5*9ώ˨">}7GWeꅸݚ+kՋhwQ/?'Ē} ۀOVr_4\Yvd~'/ei>.%I5>7Q_rN/_y?c?Ӻj B if[3Ga+7cm~a]Մxq׀zVjJ'"^`^ 1;i:1Ѧ92$>}=b8U~yV$h~M~9!ē/zoӖrѷ[4b:vg)Sכd;Jca9󆭍jk}vsUri lm&}>˝ﮄG*}"iVVU\\8;;aU9 +Ts{(=lcOm1N#?on*( h4\k^FW\ bO628upOqANbZZy%v+h$V?\͉IO]tt ݿβST'yYc:u[>\52ߐ}<T)ˊ\]guJY}\ۇuڇx =؇mvp:êkno8}wSF{_ڞj2#ocDʟ31;ϧyr|Ǧ>,E>{x4Gy=c^FWzI;&ΊL%cqvg|kc\q^Ob:Zj %ƙJst9\mS se\:?\Wo`{,;Eu|~WNl6ju:==/w@|3B-e?9>Gnf%εXb_}9qۍH}XJ} V-!I;&V\k*/.6 8ypuO{DO_NV7ᶩ1߂p ~lw۝eVc s5b}[bjGwy\/qٟ8/F1;>3 5cw/QpW%v^q='dzCMsfҎ3;T_O_Ra|X@</\4+Ɠy1 É޷;NQ]m??QGEO=-=nlj~f?GފAuO쫒zE9K^/O\QL?m͘1Y#vLyx0Vlb J}Y@}KWɂ->6)7m˯4Y1"u~mwݧeN1wۺH`cjqm0?[Mʩr:jd<PSs䭨i^>?)<@Me領7<iLӕ<|=vg}ZvtC;`H[n7WC< reOJaڏc,ONQc(t~"u.YT;:gic3:i4&UrEc>cfKioR;xt)ZX@sZa3G~r F{UR~ŮwQ/_֧#9cOan$DD.~ZY@y"O䚣=8β'u)CȺn.{EG{Fpq#Ϩ6W[=5t[7a~W-(/I'~4Ā$ua/D_ֳKRt9E5 Ҥ{ÊpRWxTzβ'u)-ZFmʟx9y"ugTq>:$F]#?mݯ6jþ*EyަEjJcO54GWM)%ʢ\(p6G}9}(B'e_oW{\}:t)]8Y2яgGZ Mvk|;˞eNh:yhOcJ+4=P.?F܏y<<%?G.w3D{ZՎfzܞ>t9)mF[T$\],hs2x*V }<j~|)Z}o*m9M4β'm)Sj=Z0W_PlՏqߚ+8\ǂ="K\uSs Eu_g_G 91Wi]4ڢZixC hI9;Vn_kwEj@TiestTtqFՎvgٓ)>] uNOHgɖ}>).b_0NYiQyw}h6><&!՞yyO|<ST/iՋ* hp<ӉNQR lw=)NQъۇscvb~-ծ,"q?:^ W^Rj3W?XZJ>ߗdzT߇U6jC7~-AEuum6VTo8LQ H>'1w@2j.3W-`sh^1蚌*]]vgٓ)ћ1N7Es-P7/w@MSsZEw0] L}Ӕ0՗h9z\y1VF^odu`^\YQEO,;ze1M7~Ӵ ٽ#Xܾ+\}UbP2my| hkɱꇝV^>=¯sӢ㏢:x}PT/ɰlweg=1ONO%&z?$}>14tPJ~RXT{wz6i0;>oh̢y7sI |gIKCQ ?P3vtkFՎYv<_S c&hezzbGgV9 WWz\=>HwJ7)\݇SM8i>gU7eNrQ]jKUiR ՍDRn確? 냢|\\kekMvgYv֣$Y@'_t[Tq}{y@T9 Wz,enHQ|>Y/y9Ʋ3=Fm6-]|Ϛj7GYz~-Eu5E5 2 -?QїβGI2GpUbP=3_}<+4J<.[TVGU[@}чIծx\ޯZzo KMT-ߙߴ6)7k.>(ɗE^TvgYvc1@u6HPm#xMTE+$}ÀVWao(v4.0+S|]gTV94vsuOEuE5 2mgklwe T\5:m{mG"ΎXzֹ)9vVñv*)5 } $z a>|OZQPW^1Goh9d)wRs9l:̊ -k񿢺z?AQ H-%lwe\[Ӵ]4+nmR9㼌bqlRNJ}շWDg>|6Ļ<6%>~:=0BQmsT*pXhwsmӻ6>s)\TWJۇfjQ'?5);qt菱YvGs5ħ;4|6ѷ=qEsF9qLTjcfξ|sǻ|hRYTz;󺏵v4'm|Hz`X\Vrk2Ta^Oxvs,\e)=^gc; U|żw\ߛT#i:݇mΊY-ѷT;1fWE=>jX@C8Qh+qx3jM2B+V;:3>s\TWjۇffQ=IRkz =lwe>u4Gw{OO2Nlbc/r̵*/K.P/W}?v~T;&0WVj̾]n.4GӣO^m%=ý=|feVTaYOIj:RmPTL,]$ _G=yβ'd]MjԴڮVʷb,NV(st";P=ώ}3j[=R)mi{6GŴ\M4W*SuXkrGOӭ㶯qul+a`sUb]TGTNqa+V+%zE;^F)6wHw.KCQ ?P3B|a@c:^O8n_;˞eOtYg=9HI_ zstiJ+O4oqZjYfObsҫR._&OZ6Xu%~߇}1Q}+nXjGy\3W[@V:)KZT>5ӊjGS<JH{phBu,{=9eǸm>7YEuSs+PmcZf)ˇgc_2svtE_)^TOA 﫶GS8=Ur-},+߷ElWT_/CF}(j^p^ZηWβ'h)w=_wyZI5J;+־>8)RNܴ۽:/;EuŠ+'}]i?6Wݸ\U]MjI2W>,k+ѷ#G,EmMe{ъ%>5j?w,O{pݫOSTz u"uaܢ:ߞ;>xsu@J>iRT}a2c1ɸձ(4GMa\imT;>5CJgw~Ԫο?mW1eNkZ@.RयthTRN8ޫ{Q-?o?]sUu:~)ݜТz56Wߧ{!:-j:RiPTL) n ^!IY˲STW~+5NtOs:1(0WQT{XErկ>,gP}2sum $ނI^w^\&cha:zQTj@eLQ.#}83:m<擲azMWR[@m9zӘ9˧_>ߗ1_sud?&s#1T\KdQ(IVi2&Nc"/uE5;@̀ҮxaW1eN9;)ovU$13U:+vd:8[|st`w%.K.TQ4.p:)xܡ`PT;ۇ(Rb{8:ÇAwU^|Җ:]+A gIysUbMXO9с$=SxIױcESokW/X s Ew%N6GY@U|'Eam//:0["#Ia`i^Y꘣=7_MRb{ԴDY|Bm<擺թטљ,R|1b9ZS-_y)1>w&؊&stO>U,s'Ȕ}9z)..T*xP9z5M sl&K;E5@w6㖈7{uI]vQWb1GK# (k$ /'}7$l>P?st9Ɨ+gtt*]Am: {M"홴8\ߞoh;n~2W_ey1w.Y[:ìH6 HonՏjaQh4Wg@Imm_/4|\Ll Dls:]c7Gh9}n6GX@X@-XpkZzibEs9ZU^ޯ3W?Js9Zj斟cՒ$3`.c[4\cm&E濻/ʯ~=&ϊ}zn|K1)\˕: hrcZ@'WlYHQhrep9 y*Ps4CY=[RڣXs>nq޶u Y@X_+R )\]C򕗐j1l]HAV^ytk Eٺ՗1/$l~ )Ƥ,sTC is1#`WEq7Wqڇ )}cY\@]1ԎɖwVVKY@癫<̓3زUKM!+&Ոz!sӼ옟-ZlYHQP;gzl]dFY@'!L hm^6Wk}Eu_*8sq2V&sulϙ4anM+_a+@1G-ZlAHC&4)כjLipA\]ij bEmPI]jڨ[29h4Gےp&s4܊ՌՈQs4=iViGZ@㭹$+V3+֟ts9z}h2WUiUeh9}nV7o+~tt5)5 I9&e&/'IBH8h\#!ER rB?9*JGIEB!{6KjɡT/T4>;fB!$d=pEZ@ejHZa9KfBH,۶:VPT2۳-ݲ"BH >P%jrh9YA%C|{tZJB!a3Cԫ% *AFT2ԬU% !V+//,@2%}&~;% !yy6>C+L IKT2 :JB!al6ܧOoZ@USPɰoϞtB!a3CԯKɡTfJڵ0BfJՒZph iv*j֪eszBH{}\Rot VPƒPpnBH<֢0gsh!iJAcRB e۶Y>}B7rhUNA%þ={-[(b!UR.&VP! *ۣkRB *WKjTکdY}-JB!a3V^^pIYҁZ@eKBAWMvJB!a?XPWS͡T *K C!$lmf URȡT: iKl!6??TIZ@e(dho]K C!$lfΟ^-VP6j!'7% !V+//.t VPFPpͣ!6O[g- C]M=C+L IKT2 ;BH,۶:C+ʵ *ӖnBC!$lN0!TIZ@e(dho]K C!$lfΟ^-VP6j!'7% !V+//.t VPFPpͣ!6O[g- C]M=C+L IKT2 ;BH,۶:C+ʵ *ӖnBC!$lN0!TIZDޝT2JfvT՝4yGJZ좣JtRrB!+wT=&UPɰoϞtB!as„ J% @QQ:B!>z{\a QP8?]BH̜??TIZR Ng訑S4B! N4$mN%CNn}MJB!a3V^^pI]*VꨒTrBv;d82dFPpͣ!6O[g- C]Mdku<19g,nuvq\p­BHJ戣Zîú\n=[~_]`[UoX IKT23f % !Ym߷oz.V뤳:tx:=gOkT2ÖnBC!$lN0!TIZjt=k9Z;7lڏ(5!d|μ s)++} QP8?]BH̜??TIZR @5^#tTIi'ػ6NЦ|҂BH͔ Zreui{·iکd͵oI C!$l .K%u T3M#GuTIYizB*ulEOE]XwTIYŷ)/t5ܤy(a!͓YPWS TcWRGUR|M]#!&SWhuY͕RX IKT23f % !Ym߷oz.VLoݭ$BH֭n3 *aKl!6'N~CRM@5Igu訑FzkS:B!Q悵EQV)dJڵ0ydjz2Wl?i|=a9~zZjΪQ# ol BbE۷ GSVΪǷ)کd͵oI S&qG2dרa;ѣG۴gIɈ{}+ԍC+Pu#:Q5҆^B)C^l".[ $k$ ͣNoe[^ !i'׭s9XG >(!sEQVp2@ԐDA%1cPı=;r[pBHZfٶm߾ms9X;ѢJGrbkBWNfq/YNg ?0[A%C=l-1Qy܈~κ%]N81> I59XG?gRS"B!MϬ-@ QP8?]&zƃOv͒%v楗Ze;dB&3/[-?~h$'~]};!\}=m@Q; 967)a|*tv[Ng֯gBR>Xacqh^U'5d]Oy@!$(]oݚO6 IBAey(aXT)w^}uLI;mޏ]k,%|yLuɣ>}*_s/yѐAc]3sg6p3fLoܹ3ۀXq֥_?;⬳촙3m]wŴܙ8H9qP%r9\VՉ.~( B~.v NRP8?]&E Z}{\cֲ(măl=mۋeeQ?at$n ZNK{of/]jn.OgDZ.jy| 3b wm9c|5YYW,Zd{w߸q}ϛӹsO}vr2BՒZphN*_I!=EI@!$9C.z*KڨJ\曔0I( tk7y1QAyg]vYL9}1Os>觟үի%3su]rӽ{|vÆǃ$x#+V^^.ԍC+4~'@A@!$9mZo9|H *u&͛G >w\{_áz*:~}{)*ϥaVyES?hzC+IR' ?+N(!"9a~6r% *=&EubnȔ)Us Y5)(Ǐ)wi׾MN;~x۽Yw?`֭Xj6ibǍg?vv#بٳmְE]s\GrjY~lȔ)6mvʅGau4}nݪ|^qէϚe99yvI'I'[SO>8|x䑕~QG٩ӧ믷'N=vkSs3ml6ۿoP&Q%/Vz;kMz!":UyQQ%/-X1GSaB*ˉxR$Ջ7o6[}uW{/B>!Cz5+a/|ͨٳ+<[8}γ}{g7l-!KV{ew\uZe{wϚVEup컯GMo56D'N 5ސ˷1:n'lDwC BR,}Toim`~5WFHUyeq~=v-%L;b׼=qc3thםrUNC޾ .<]_Zh6o =ztڣ|EuSNx=[mv_}6ʤḞ 5NWKj1uwA!@!Xμ*oA#mmԆDeyYKoI ᄈ&OYVJ_{k[ysDoTfZB^|;6}=^x'~}{#~oV{U}]tQjx 9ݑ$~ ކm .:JOrgi?n BR,~`GUu?ꀦRHXV# 9}Qg7._n,9*Vt? 駣}TxP~>y o[oC=S~wU{VEuNnQMkgmLo$Zg*M :|avS^mc%-[ٞ{V"mpʬWіeȳl۶D"&STlI )!I zvGQaXRU&5XÖRı6uѭ_,+:uvy}z'͛Wa>_qE?[vo5γW|[n߫Wwܸqo}MQm|ʕ!kgx#IBސ˷0m9RB!)TDZV,Dt*_Q}I7DCƼMF͞]N93-ꚵjE}eOPZR ST] BR4?^AQ=7z@QMʊֲϾ曔0)VT_paTӼW};T܃Ǝ[W^iOC3;=zZ ;6-w]&Nmgx#V^^.ԍo_ꤒѕ3fmF@!hfmfsFWQV["**?euh^C*HtE^j|qyKG1y]q+۷ʫ,iSԬ]tvGUtC Ƿ1i㍄ϲml}Cۛ8RG\]ىú^A@!xzEEuG\ͷH z%1)PTdfn1sw^2+h=Ds /Ds^y2jԬYiXܡCpO ĕWƼMf/]* :5{oc< 'M >ߐˡ@:i͕{صBHUqE!q~-\&e۶Y~Ux]h{o7n\R5k촙3mf–+-ґ$3~\?4iE/\aǏ۶ʹFf=z\-%V騑WvbRB!)#[#[$jT^FT27ߤI%3Bkʉ͛m5Tx^3Tx-[8K.~gq-饤ZUawo4WZyyTR7"Q%Wvb{ RB!)'Zխ)!h$ ]%3u^ۨeK{_*}C_]uM RjCVx+~:*ߑW_4w_ҊʬfڻOv|ۖ6Ȯyr:kQX@T( !jT(WC GE E/P0Tf{YuW>lJ>j֪딤zkuT,;u ?*O>v=zXVvv㭺uEaa8C+O( !jTV IKT25j%LշeegWxAcVY TZJ&ְE xa֪m[n!_Uš-:X;a;mL;j(;#C>YwPzbѢAeeg[m!XcG Fh/)-V{=rԱ82:~߷KͶ\TdfCMtԫg7j'ՙ8ު[mfj}̡gՄBE5j zֻ$l`IIrޫʺhMqmnm*pOr7 1–m񺟽l~I/mѤ:[uJ穿!)C+( !jTI *5k(a2~-ٻ[l979;^ee\4?պU۶QO9sbZs5l<ըYN1Ömjg^ziҋ3c.*3qU|PkZBFAQ]Q; 967)a+-P^,;gNw߭/ ZC?8t3eNMDTZa+uvŢE֥_*ojlium[F*/4~nXk/^0:|ǿN4*W^0LoWZyyTR7|CQM!PTVI\An!!xfWmCٸ&z3f<] vS?{?ٕ>kι;m5n}-ۺy߶Y ȫW^i.)qY}]KvM7Y]fcn2Zj>˞x&~lfSۮz9͛o'׭EE ZBFAQ]mԐDA%QFQB ml}C7sh;jB!EuqJv=zB&OUR!)C+QTB!ը>(T24jYC C!$lf>PzZE5!BQꃢ:㵗Q; 9v0B+V^^pI]*;VqCQM!PTh$}&v% !yr:kYTjZE5!BQꃢ:cՐXA%QFQB e۶Y~B7shwՄBE5uJv=zؒRB!asɡJ7$rhwՄBE5tJF-[5k(a!̇ URԒC+&B(Q}PTg6j!'7n\BHܾb .K%u a( !jTt5n!6O[g-B]M=C+&B(Q}PTg+d8jHJB!al6ү_9FKQ\B!$L'SB!ՠF.ݖRB &OUR/ˡ.[ҢB&_Ha&BE5(Ili ׬!63z(Թ5ZrhE&rBOia&BE5(QA{Iv k7._N C!$ln_j׭|TRwI*X!C@QM!jPT5y艷F C!$l\Z:;C+2A;IjHJB!al6ү_phE&h ?nb@!$l_o{8N/H9ՄBE5*:7>ݻےR3B &Ouv\Hwْ8?!yav6`@/Hwqh(Izdچ v櫯ZٳmSN޽[bۭE kܶ>wvy窫l=iK9|:$jPT#:'nԲ-\ BfւԒC+2m׮xfvB / I8$usQYts ]f}yiX\l'N3_{fm%jPT#6]*'7n\s B+vݺK%uЊLp͚ëV 6>)++ ҷ 8$u3쥗^{y*ånfElMoBE5_#Ip뭜c 6O[g-B/ЊL_v5jִ9BHGV^/G[$ PT̅b>;.uz%wBE5vȖ8Q#GrBH]ү_ܷphE&h ?nb@!$l_o{8N/H9$5sVн{JjIV6~f3JKY'PT:]]imIi)!͐)SB^.)C+28?!Jۀ š&{%u~׮ -m˺cm`5jc}׬[ !ՠNM'WjԲ-\ Bfւ~oԒC+2m׮xfvB / I8$5u̘ ,+:6z6ছiӬ[[c׵:u*}.uǜ˖nE5Eui/i\qr1B Wu+ԝC+2v7kfZBH\c/HJ* PT˗[VvvrVС6#~7)6DŽz8E5!ՠN9$}|̞p뭜c 6O[g-B~/ЊL_v996WB W:9*Vn#n޾3OӘmvqVU+kգ>vkтjGPTlIG9B!>vK~~¡Oy7BH,Z \7ZjO]fؒ:iS;﫯|R3wBnOjB(AQMQR >Vӽ-)-<!2eJ{%rhE˒t| !͋eemP_ PTαήY|US̙ՄPTST:|nԲ-\ Bfւ~뭑ԒC+2m׮xfvBfi #Vx9cFآ!4-:u&թk?Iv>Fڍ˗sBHYuJΡXIe;ݛ5Wb@!$l.}1SVVo%ph(IYg-{L:JÜjUmJQME5()σns BueQQy%Z ΃;;' ;B!asʕV^/G[% PT &?ȇܲ+;W\c;qB;{r;﫯|{fb{nz};mwXs>h/[fc>fmߞRmƍ6vJ;ub;;UW[n!O>i#}&y6ajv…9sWQى b+#BQ ̑-iqy`I ! ۷[~BƻC+2AIn!Y~n/H9$}pܸgMvmY=^~eeg]$+6]Yl?ռK>ajРn02g=_}7ZaޖS:kڟv ^b3p[ElFZUs|1i׿8n5iR4j԰of̰I|~PT:]ߧ{w[RZyB!a3dʔPKЊt%3B ʬ[+ PTJO[{} f6 ;O'n1iRt^'zŖ״itٽMvv櫯r^?޲je;n1ǩ?d{yjX\l'y9}2kޡCȩ]8眸E5z-m5g 6,`Z .mvś6 6CM I9$–b'NL<8n\yj}!qۭ^~~um̙Se)Ӗ. ;͚yyv?|EeeuظƒWT\O}RE_hܶWէ-Yb7m.]|߬]>|$kкM'cՠN} %}p뭜c 6O,* [C+2ޒ~ypg؜W^a@!$l^ԫh^Zj숿5)AMK-:48眸9uؔ~H颺ggz1_A7{vK4)?`o^>8cՠNmْpBHؼ}u/o[9"4I?w.;B!ahz+pP_ph(I梲2ۭEEXv͚v+$|ްTfm۴mhvz-]VY5jsvȬYvܹv…vOQY3[Cvĵk}/B&T{_R5+^!]d? &2jt^=zi+kV.h]v t=v%ؾ'l-:v۝:z͜Yi8ehw6m୷Z ,pVo="*O[}E5E5(SU}w%g 6CB!rIZ$=ھݗyii(4Gn}οlm7_c f|Q +/6Ǐʊnq䑕~654&q BQ j֒/X!CHjavb7m!6CM ]I9դzE,IXo͞m6lCf^+ϙv9RT˼v3ù~í{~թa$ !$;TdAv7kfZE C!$l.{qSVVo%ph(Iʅj_~yW4WM_RUeʕa߿~陿=c__o9k}d6#^=zr/\T=ۗysUaQY3IâfEQMQ q$%B̕:p(E&[/ ٜW^!6w\iu >UR/E5K.Z xEbֽz}ײaq]TVV{?JN6{| zf=Nm9fmn5rsNW_\T}9_uCͬa9*jjPT 4IsRB E[:9C+y(I,O窫q۶1N:){YxQ_Q¾ߡ\{S27nzkLW~bQaO, =Oe>Oy8pxGۣ&Ո E5!KҢ !6/YCC+(?u:,YneuΝm7D\:~Ϭ,׌3ʊ/wwǍ1~Euþ#׌nau̝wz*sr"rkQ}STj$E5?X.]lM1BC|WRm@f&~>onݢ*콷(-jZ'L~fΌ}䐰wU[(ת_ߦmr oPQZeՍ۶MHQ}wPTj$E5⥁O;BH,Z \71ZGQMY۷[+{w\T{}v'X돹W>ܗeנAS[x6RST7w_jBQj%iQÆQB ʬNt͡(I2,;'q_F~36o&MBOZlʺuwq>ܺOv~+KVv:ۑdz}{uXQ#ysbRZȒ(Gia(a!͋eemP'6N&jd5WV8aBQ E5Ite"[y5in]=䢋N~kܶme/Hai() Eu|~3Hxs\m @Q]oDCb#WՋG׮eA9uP'5ߕT;'js9 j .(I3bzte1iR<{򈞑]aCiSJs?4jvN~꩔(WAQMQM(P-*@Q]zG$ctflK C u3IV;.Fz:k+\Txy<﫯*{PTSTjo \MUfjRT%Ct+PjBHܽrթW/3URE5<|x"m;,{o2kViZOS~!l~xJ{v:PTG[{STST iH$o!ՄY~nct)[{ST$3}&kX\>{~衰;wb3k>!iv)fmn5k{ŋ?Czt_Ћ/;6() Eud"7E5E5"KҢ !Մy̺ Dq?)jwȐ%^ lo3JK-iӐ}E؏?;W_rj/<gaߣI'2j兝Nc׵ PT))靡IDATvb7m!Մ9uP'1ߕT;.'=ڛ&Izw;ج6C/_Ry=&O]Z+ [M\6I sOD1sV˪Q#{ԩc?_رnۢ}킟&;~3x7E5E5n|ެ-)aE5!$l.{P϶VR+O4skoj$-yIMgڐ'i7?>aB-/7}Sػ }9z]lfQoG~u;evy,kޡ4s3vkO=enz͘a:ˊj|jjEu\oMQMQ Ȓ(Gga(aH߬avGۥw`{ާbg_~{}ߦwϿm߆]|=4ۧ{w td=9Ǝ?<;lC٬ *ͣk2~HŲ26p`w{8vU&)QT#MǦ[(-͚E4=m#(tUqweԨخ2;鄚gbob{w\E5E5I:!@B\oDC2t]D|BjDZ.yz^m8ò}9A6/aɩ3fJ Dڛ&ժ޽_*.sʔHxQk:uV̷bELxEWc:T:@ TIflK C2~߬1ܧ-駨9V^'κ2I\nVH歽)jR->q.;~iE:2D՝Y۷{''=jկOQMQM2Nꭽ))W{K%/L C2~]ON-\&i^aOX֬UZﳏirԉx7oCW:ѭD+)j'鬳,ĿfMcm'dt~_/әwsTkuּCK-Z%̦[g]pլ[ҰzNX& T7E5E5⦁OhFJqEu0'VjY~lȔ)6mvʅGau4ue[V:GvV{e-o7}!'{>؞۸qCR"֯ 5^Vz"nMQ PTgo>hG:Yv͚ Ŝڵ{w6,w_3Ԏ?ߜ#Zyi:a^|Uԡr᯿ځGu+6coW\am=jƴ^mkOf#{/HDQ=w.)O?MQMҮN[{STST .$- 0JqEJW Wu gͪt'_pA[(mKJKþŲ2})^7jl IXVf ;ITwUqdIBVO)Iz&6xӜ9sx晶'^kΑGZQ߾裭ikf̰Fܺ5i~m- 4(ӟvڒ%kyYm!C8٩Z1c쐋..s?4.tb}fv⣏Z:qk{u3;殻l̇:i6g]gOfG jgځ̱۟SySINE s?p\aVis_Ri6RT e'O.]lM1$%ְy 9h=/ѣ+^k~Mw|R Wr{_u}>DEEe(np!-"($ĺꪣRpU+h֊mj~:PPYyo3HNNxisr\sqNE9өӦ:FޒMGjƶ'sISM{iuQ!Or8O,aTw_u4n,>lYƗu}.Is)իc ^ag֫_?>z猪KTwBjG{K*״K&[mU$j'Y=T-@ݱsᛐ7EKEu㤩S+|9S;rz_;N~͔)_׈+LUkNlf5 G؏dQ-UR'LH=aHEN COú1|2[aQ P75!=iQ_X¨N,_qEk6_.o)cvOZCƢYog>81o0wO5k<_՞\4nөSlA}4NQ*_0Aj.Zh犲#R='充5-ZJѤ'пs܀v_I ;^q)|iQmQ @riɒN{\i:'3ҙo^0Ve|$d!FujQ}o$]~#;K..+^}F m~;.PtcŐ,>+'0r-Zu;,x-▻w0 n~qT%E 7t_JEr--!o?Y_PEKչE}tt)IwSO=C΋???ϙ/ob焪{y'6l~Y?+ nQ ժ-!x7%YTWz y'K0b3:{ _#O߰ᆗ_${I__4k^9דKm:uJuj?Q nQ ղ@.$.|P nQmQ @!“'K94KEN^TrN٩g ]w׾}=ʹ旖G=9w# nQ ղΠ:~'ɢfG[T[TBᧉ'L:GrEzֿ-CƗW_&͛']Ƥ3=n]9W!Bh':`Q-2jآEp5qڪU;I9uC6>ܢڢ08PI[9|b :~!xg$]OV=W{q۝wN[A 4i"G㳫Vy]c1%>?!1'=-Zu{,:5vq}->ׯ׏:wL+NK$^T*?ܢڢBf%C~AA-aT#njfR-ت]ʯW/IUxΜhR˽}o=o nq]vwAv-y!sύn5yJwS=ׄ'@*-Z1N/-KKTCfÚK\/,aTc՛#+|2ؾs}b=zĂ´1pĈn߯?[+OlG[TE$I_3lG[T[TQ?M\2tիܿ+E 1ƻt$-&-Zı7ܐmO-޺Ro[o3mRCR=BhXOT-Z$E*ߧBK[9|b O>YF~;xɸǐWe%rE%K2]#*njܻwܢѢVBf%C~AA-a/|XiRG[TE$I_3uH{q7d #H;v 7F_ޞo [} Բe>~Cz'P?ܢE$I55CG[TYT y!'B’a,a>KZ|ᅛt_rIeʸϠA:9G P,%Iv 鮜ɢViHsWr>iXCtǞ{ne>l[7(Ւ!@u$ɢâB( -n|%C]_'-/MmަZ?'{,!@u$ɢâfB&d/(׿%c裤E 'o卼ꪤʨ{}76lК~@u$ɢâjBx/$,t%jn^ OV/-} JסՒ$YTSwXTL? KNzŹ+WZH9Kv%97k2nߵkqv-2B}rZT9BCV XTKdQMaQ P ! [s>F=ۗxH:*oܷ*K{,!@$ɢâfB&d/(׿%Tz)]hQzǽ;,KOeԽmYsiMa?V XTKdQMaQ Ps4 !VnFo810 l6o}ņMͷ*%=x)qƧ-s)Z,9?z jI,;,j!apa0R-l~iA\3hP%@.$ɢâ:M!$UBeuI⒡S^qʕ1 +.NcׄETZ'E`R$jj,ׄS$7!uB(]h޺u'0]c1%>!*q>Q/ $jj,!N\$UR'e%l+/(׿%$)mnlܬYkMa? jɢZ,jx IR%}mUKq7d #IJS˖;:x,%ɢjȺ-BJ:8%IJ\U} !չBx"6#IJgРT{&,%jYdkQE5d]s;ֹ,IR]w?չq$;\$mXqqC .,%jYdkQE5dگ_|nj$Ii2aBs/Wkpt+n޺u'IR.}\{MXTfEWxq5W]x+:ǾN$!͚];.{]7ߜrF'?TܿozIe^vo~QE˗/'???Ж3!gڵl^?ިEvQ6mO{n=Y:眜&_j,ʵ !|;{w8%IJc_~tj?\y!'a<0/-} JukB:!ذE /!h͛W១IxR]7纶oc=7|/(zO^aF}1^|9s{se94s}]Z^TWvyUXTC%Bx.Qc:%IJۼko+ԫWrGaũ\ !4EƖ_PZ |.emc2/̯u{~kkcǁ˼ 5kr1x?YN|}%OS^iEf~q[oe~_]zi >oծ]|db$Ii+5+OCm%,__ IR}8,kB{MXTznKCo1G/\Xuꪔ,x/n=R^_[s]\T|`mEXаa[nI =;Ny;noY5f;+^ZgpCiw><}|g/(wI?SJ-v9+}睤jUc۪עjȊ!./(,3^{͹,IRfVlиqU!,^ sn#IJS˖;:8kB:-߾%WI{ID H睗} 7,OsV\KKc-H~SOeGsOʿ^;B/پ_ToVqEݖ򿥲q%}o]cu-[S~iI_i)i۶I_{XTC6 !||̙eIؗ_6:z-6\y!'a<0/-} JukBf,ǾNƗ|gty -tMj͛F[s][T) |O[*o$E~ӧQoYuN\($뒒ձȼb͓.С_Ƹ#ׯtΝICo1k\sMʯMS,ƢZsǏ;ֹ,IR]w?Λq*INzŹ+Wzp$Ii;-?z]XT׌Euy\z*eTd1Z |9y{s~Mub-nݗe3}41I}c{qFw' >[̔'.^\vIs 7lҢ_"EuwƢگ_|nj$Ii2aBs/WBWܼu8=0]>¶^յsQ]n]|.;ҟ3G΃'ݞFq= L5rd6l|e"GeۛE^%]v={gQmQmQE5ى[kYع,IRڊfJu.B>pu !,[ /$wߍ5K

_4k;xV)iРii;{lw8s{lNŢڢڢjj!wO9ӹ,IR_$I_!U} !<׿nߥ}4Z?1~='k"sSKyoآڢڢjjz!o5vsY[6޿M7gUxŝzsWHv\ !4ꓞx"2ZwZM䓔\_TWV\?tqs)//rqq_Qn=H2ZuIW:noYUТڢڢjjgگ_|nj$Ii;Sfz9P\GJ׿[쁑$Ou¶^ugQ}w&]6}=S,w%ݞ{KJwO|x֫ X#mQ][s]_T}5jT|2^^!gW̞_O/-c~5fkoQmQmQE5dĤ ׮]|db$Ii+5+OCm%l+/(׿F{}76N5!&,֢z'= w;xUu>cF-+z{~Nn?rKcb7/s_~_'Eʿͻfչ뵨o;x{„X?Ey !͚Ţի+tpCAn$NXczIɓk"swN}쾲ƢeՓfδ$%KbR>/B}t$uׄEu\T52δ_c%n+3\TWVZTWI}w?cuւ 66l"r/? 7|'sAAlKIR]wOpF`X$UB !4{_|1e6~%7'޽+Wעzcnou]o][T[;~xR?B T׹UWU7ujl^qW']?ޒS^q=T)!?yծW/~-,UE5հɮKk~ի-b$Ii;Skx9P?[,W$IBzMXTמE/2Rz)tIӦg -g-VwTHqe]0`@_:Ӕq*̹t{Kb;|pep饕؞{)mzOxOċ΢:G_j,aUvŋ-a$Ii+5+kOCmyB8+ps۸%IH{MXT׮Evp7/$}3,R}w~q.ҵkcQR]:*'oou]o]\Tw6,AQAq뭕${ךۻI᠃*6th<˸h1Bܪ[8Αעj(C+\PXg%$)m3z+6h8q_B 5Hm\T-}׮M'=DנAGs{띸hQڅԯN}}qpi7'oou]o]\Ts#S},wa >v֬Zs{)/{ﭔwʗ_ƭ[g/Z?Vݻo} [>Eu<䐖!/O9FǗ,mR|:WaC+0uQB<07ǏսhŊ8bS~u7nx-^|xGV<.]E.9y{~;K/-T:}c~;'өn!n318gcN}M۶KfǢ5kjk)/3~رS:x8c}M:gˎwǼ䜿=;mZ%AW^iQ#E55E59^Ƀ>FykO5ph6/a|m{ QouS1etVfxݺy?zݗ1o7iϏM۶[[lv^aaگͯ_?:w&x%$)mEf? !qh6.O{ᅴ'|_zucAÆZP 9{ߡZ]:纺c}uy)=~&$]c{㏧sCl.͛cOZ_jrE5լ{a_ W_$m[o'BZi/書ǘ1Ei?Aȑqʗ_nqGŮ'PºQ˖qkթS>|x꼟O{ᅬ/Gqϱck$x֫Ɲ;Z^^z?\GW.6m6y*ﺺo=>w=m|shŊ _ϴᅬ^vYr]}nӻw4sfoʽub-7zQ}ԽѢ:;ע\dQM5jB0w3-a$Ii{|ɒئCTpVjz`ٲxK/g̈]ye<{Oq?OzxW_}x/Ǣիk͵ۯʖjӞ_Z<⁗_rK8;o%vM_=N3UGZT+,&B&N|ٖ0[60 P ԅEumno]kʂ-])ٜ,,ɔE5]ϭ^m#IJq矟5+!P XT[T-ҵIImr]-#jj0j[kYFYR4ơj jj;kVrQG3^zcy'4jyߑE55E5Y=bY0xUKIRfVlиqU!PXT[T?[u7n;dQM gQM !|8OL9FǗ,m:tHubC+2gPqƖ;|̏~dQM gQM !<8K >lKIR]{  V,-u_} b74Iww0 N/-uIpdɵD׾}sW[Ԑ~_IjO+!P YT[TvK;Ʃ_},,ɂgVG/xܹTկ?Һu귿uKfJzBV,-uW]Uz]w}%YTSKXTSźV?Kj S M*:?a%Ui3z+6h8wܪBoVj+b-no]Zxbu^~~;yY !|8SL9-o# [c Jz|ɒئCTK;@-W%1I}y Gno]Rbwڴm\Oo/tHBTz!gC’1c,ajآ-:Zo痖V)S 'ӦOjA֮ HC+5}Q-I,ɜE5Uڐdڷo|nj~!Ƹ}׮\ǩ_'gjR-S-_ !wh:Z$jj!aЪ]Ŗ05tQЧ3/,{s>'n#,ZTѬYԟ8@aQ-IE5uE5[aEXoPPXg%L ^TbKKcv,Z 'N[B uE$IT!Ct̙0ՕޭۢZ=dIlӡCwSwh:ƢZ$jj*I³!ap10Ui%%R-hڵ׀Կth:ȢZ$jj*ɵ!aеoU1`Q=c9?6*/Z'~{ZT9cvO<qG_{mv޼8o͚2//ï"6kjݰI ~nz饌?R?!:mZc}v-گ_GǓN׿b?gy㥏=?tizqqw=Գgc)]sOcJlKy$uGQ=T?qb%+!BVzQub#gXHvrգ7E5ℐdhծ]|dbKZyy\GSOori0"*eM7<:4Gܕ+8hԨXبQƗ~} *飏ƶ;mKK7~Ϗ]!7huwqqoc6X_q۝wN{}ZrٳS=n8@3@oוFb!j6YŠޒ0xUK:>K7j~ S7ZT?wu:*h_>\lFVDznC~_1Ǽ2g>} cƍU!P#.{\n IRwWzM2aHXMvK:yUTܦS }9&IU~0;>_{^(<ټMoEoʹ_<3^s5p{|R:~TωWB2 t *8$h];w 2ѢZJhvSv#[XT:+bEL)I^X=n]ʏ> 6jy{ 'd<旖.{ïB<3~NdT4{vħ!6&2۔wr{ԯl IR6`Q=|Sժh qކK8W-a, 1ƾG l,X0opv)oFNTۂ8O2o.Ϊ5{qDl=TݱpalиqsbUi @0ryY'Lz"@mȤ\R#vVE[c O=YYT4uj4rnQ}I'%Wu0?.N[Xkv%uvËYXT~i)> } W=L|~ݺjYT?I?vO sIsg|?nVur/soРIUS٩OCmM`@Z&=eu$XE&6XVVnb3;W-a,uc.h#~ؗ_fuQ}_϶qߏzEuϿl^_mr=|N-kmI&z^ !:3U;N$I܉ӟ#gn  ڵ/Zd cQ])KLJ-Cb-L\GW٢KcƏǫ:5fʩhT!@u#(Mǩʒ@jj꿿ԱG(GŠޒ0xUKJ_:8kbæMS.odQ}SOUck򢺦l=ݱpalиq*ۡh=86ICn L5@9Z> ˝n cQ]K}׮z,gqum6rQ]okOڴ_$!? laO~ce$IYn{M5@ KAG[XTge>4zzx`ƋFmVrm=t[Tۚ6yk^ZR Taԛդx⥖$e /zԛׄ%C׾}㳫VYXTgmةW ۾} IO.]uzI߾K8o͚Zɷ5['m|OjIJС=3yր[gy IR7}ݺ8gnj( !aЪ]E0Y]:n~m}~S$_zuo;co-kmIW٩[:Gu&'Ç$IUܐpI=~S Pn!a%CAaa꫖0t,3'N8+t:wqhQQ4ztc S;v^;{w{;6,4uj|ٱ۾9sʽOfy6;džM\^Tۚ2oڵ׀[Z*#z exg$Iթ̭z ׄ%C׾}+tXѶn8룏ʽWrzu $md׭oRqBHX2l6>h%E&/;vC9' \Tcǎ?{^^)uw,\4nx !qhG>G*罷<KI2lTwR0i !|:oFUփgO?'yg<Y_G_sM,5+qykTu=^n5ye_2^#—5㍿},=;/pC矏w/񉯾QM|>%KbvHF8զ[qb04=è8pmq꿿$)MSU8Xk0[qH^ِd4z%$)m֮ Hšvè]Vt|2 !| 7-a$Ii{|ɒvRzC+:37F=Iv/xq<`+9#<6IrÎp]<`+b/y\F=9(Cܐd4j%$)m֮{|p%@#3\Z'%Iۍ\#n2puHX2t7>jE$)m'LjIJСQX$iE}pzB’e۶E,a$Ii+=;ՒzQC+PcFi IRo7MBX[2ƛ,$펅 cƍԫB}ZZG9gAr?{A0r!B»&~%$)m/YCwSphja=Y=è,"$Iz B F$mڵqNաv 4#3\mA!I^v 43ꐰdإOU10yr%BC+Pgt Ct#F^#zGBT;]=8[lB’e۶E,a$Ii+=;ՒzQC+Pu ÷F33,F^3Gu0AIפLn?kRkOҦ4F^3,FF- 9a%CAaayKIRX06l$qI*ǡ2cX;a{ r^!pv%$)m/YCwSthE5xBsC’aШQ0[6qԷ:U΢jC’a>}ⳫVYHvɩ B@dhٶm|x"KIRڊI^Bh dE5XBzKx0ݱpalؤIzUC+5P# !| wmYH%;z7HV ,! KAFYHuqNա:jq K]ϮZe#IJۉSZR/!:YgQ 5!aвmE0ϙjI(֡Pct !,- XHv…a&KU!>@eაnoFǗ,mw!ջG:ʢr^~anHX21r%$)mϯ[8TKZjgQ 9敖dإOU18eJ%BC+P,  -۶/Zd #IJ[9ԋBmZ`Q 9kޒ0޼`%$)mwvlؤIzUC+3, ' !| wmYH%;z7HV XT@! K#F$ujI}C+s, \ ]"FLI^B(thrE5C’e۶E,a$Ii+3'ՒzQC+, gBX[2ƛ,$ηߎ 4I\R !qhrE5CwÝm0=W;z7HV YT@! K#F$ujI}C+,] ]"FLI^B(thrE5TC’e۶E,a$Ii+3'ՒzQC+P#XT@-`l fkl fk`6[>`5fk0[`6[p5|0[fkpw5|0[p`l fkp5|0[l >l 8l fk`l `l fk58fk0[`5fk5|0[p5`||0[p5l 8>`l fk`6[>l 8>`l fkl fk`6[>`5fk0[`6[p5|0[fkpw5|0[l >l 8l fk`l `l fk58fk0[`5fk5|0[p5^5|0[p>l 80[>l 8fkp`l fkfk0[`5fkl `5fk0[0[p5|0[l 8>58;>l 8fk0[`5fkl 8>lm|l fk`lm5fk0[`fk0[`5|0[p>l 80[>l 8fkp`l fkfk0[`5fk0[`l fkfk0[`5fkl `5fk0[0[p5|0[l 8>58;>lm|l fk`lm5fk0[`fk0[`5|0[p{ >l 8fkp`l fkfk0[`5fkl `5fk0[0[p5|0[l 8>58;>lm|l fk`lm5fk0[`fk0[`5l 8>58;>lm|l fk`lm5fk0[`fk0[`5|0[p縗l Y瘭Q2%lS\*O E%EᖒpIqxPudžwO&uWWO E˜pPɔ;`֒րZZ2[IBppzIQX^R$I|zqx}zQ82iRh%>`$l %I2[g_ ͊JAT%Ea~pVɸ)l-I0[Kd"EaVIQ΃-IҏY%N fkIրZ$uTI=/(.&֒$$IfT2-_RJT%I2[fkI9OmJ0I6K&6N#@>f$l %I2[gp/ KR$IԖN)@σ/I-ܾ 7栿(fkIրZ$uf%E[1-IZmŔ*~/ %EaSP+N%I2[fkI֙%.[:+IR-ec5fkIրZ$u9=2ؔkj$IkjRSwf2[Kd֒$3PTO/fsw7=x$UoﹻCENM@a$l %I2[g`ʔYIQx?+~}b4I6u3SSyʔS֒$$If 2—_$z8Ml-I0[Kd`_gzEI*?Ȋ NU@H3[Kd֒$38OmJL4IW^i) ifkIրZ$urÃ!IRVq'֒$$If \=ww{ $IJM!SC'֒$$Iff뒢fyvUML Hfb&|ʛN_@L3[Kd֒$˟L;_,dӊ@0%I2[fkI%EaAy2g.tI9w_-prfkIրZ$uyuqq8 ¸lw$IԲfp*fkIրZ$u&0o~a~lIWzQtT$Ifkl-I:ٺd\hVR+_}b;ZjSiEỒqS}fkIրZ$uupVygs'K#=;o2-dZ$5`$ll]RMK>,IR=Nk@5l-I0[Kd.4)4)) ޝ+IRu]ۗw_=iRhdZ$5`$ll]\)oc%Iʱ?F8dZ$5`$ll=(\UOϋ}7+IRwSc2ӋUNo@%I2[fkIӋe}᭷uJzK۲u7 '֒$$Iff뒢/L_w$I9g>;c$l %I2[g0[L ۔s;TwN(}L 8Y8f$l %IRFu(.E_|>*IRc=怪g$l %IRfu(Ɣǵkܡ$hkKJ>6-qp2l-I0[KfP<-7^;S7/dZifkIրZ$e0[puY_pܑ$xڮQizfkIրZ$e6[EᖲwGJse[怪g$ww0[Z$ea%_uvGJ=~9 +0[Kd֒$):⎔$){p.L{i4$Ifkl-I2%Ir2 p2M$l-I,%Iid$I2[l-IE$Ir2 p2M$֒$ɢZ$9NI$5%IE$Ir2 L3[Kd֒$-%Ir24p2M$l-I,%Iid$I2[fkI,%Ir2 p2M$֒$ɢZ$9NI$5֒$YTK$''$I2[fkIdQ-Iid$I2[Z$YTK$'4$Ifkl-IڢZ$''$I֒$ɢZ$9NI$5`$ɢ_$''$I2[l-I,%Iid$I2[l-IE$Ir2 p2M$5`$IՒ$9;NI$5%IE$Ir2 L3[Kd֒$-%Ir2 p2M$l-I,%Iid$I2[l-IE$INNId6[Z$YTK$'4Id$I$INifkIրZ$YTK = LtHr2 p2M$l-I,ʯcǖ1Ԯndd$I2[Z$YTK~Cv_OӟF%9N%y dƔdVbE׆Ǚ31>ۖq6m]w2C6{2_CC~wzrbQ}-c~~S^^#<ΒL'ue.Mw?kA^/Hfkl?`|\xb ufL%IRrJqРNaÂԭV?Z_}556n\?e5iR?~$$'4uk]ZoCmiw*pqܸbdݤIصkx1;Nj.'Ι3$._~S,UWsȑbaaJ;iXϞmW?`|/pZ?3Ϝd8fc5oޠfz9{t{dʍ9i8lX8oްk3$jr=bAA~[:.+sO-Q+L'Ne~ǧ>cy roffjnj)ɢZʠw;wntmV,ESO\y.h+W^Z5}"''TaOz=9 0[+wwo fLz%YT#6+s^}?9̟/-_~QlР?}#''T?t{ի<՜ 0[+6m 2c:+ɢZu$UV/'??$W<.pá!Cv{ulӦiӤI${)/kΞ9گ}B5o0LLS`+(ȏ]lQf;2j(֫W)xDI5dEuYs6McӦ1?f ٳ1dQܚ5EvxeK ]χ/صk˜ܳ]3sFOX8+ɢZ5+/]lQA>//ěn:,g~f[QѾ98ZVM]Vzj7$l:;G/[vAׯ}3{x,dQy.^P-n={144ժEu%OϏ}lS[z$l fk9z≱Yi-hpWEjJ>vmGmQ]7'}^ddj:ƒjմl[lVM|$l:=G_{eK^pWEjB{u#蔓?wmXT/[vA|gNsT:/Z59N Ɵ䩩jm룏΋6<>I8<tPXP_G;n8-Ι3Rѧ&N?.Į][3b !oe`\MYsI;7L8jȐ>\xb~cv9 {֍{?y=r2 Ll z7WY׆ю[G,U-]yAebᆪsgEu6ޝSq{y±vqCUM?'p3b˖bq~>KgcyPGffs|XRlݺq\۷&ޅ$'491c(s#kcaasC>4S=zaŊeX LsvN]2[ZT+1+smlRTox`d8&W}1 ^%_Lwċ/^mضmJ5oZl.5eNմi=H4u7phF(]sݻo;[ghޖE޻}p()?.)ٿJ~w*~y~=иn]qiiM8T.^U^(-NW%A/Ԋ.fsl-stK{\7nM:GSC?F3f^緝hޖE+s )SU/v6׹z]{q„UsMs٫̍TL'T}jVIDATկrfܹYu͚2A|T}^[􀬿^X(ֽ;ך.fsl-st>{M~V9wK{o|Qޟmzk9~>eQjK-s}Q]!ļwaxQ]86{>}M{}kWmnѢaړO-^qA?;N٥4Ŏ 6EQTDAҤ JS`hl j4(&Ƃ%FԈI1&ޘbMb)j{Ex~Ovgfg93y̜9>na1u~qDnٝ.eo>?f8Z;l<>)/*N_zfd-ZĈq=Ńo|ㄨg9 ?|6mEb1 ,Iڵk -^5u?w^WiiI򼼿_es6q=cܸc֬1nqcH۷;yEo.zk[>:]FJ-|:_?!zh\͎;vAǑG}nicZ=I7Q-(e_| yѺu/{M[֮-q1$&M7֯6T{-Smg ~Iwћ֢NwvNڤ:^}6R]ƲeC׿Ϧei|o[mk |oA4J^~y^̠w26 >X6딌W%iem;O~2%_Έ&k^EѲeƫ}VuM3LEǼy4&زʥ-W4{}K۷{zE}_׿;t'xY-?ɔO _]梷d{\IDY f}65z>ŠZ-_3(ߋ k/}z?`L\5k`vݵKsk=)2sk^~)iUxi`1Mom-?g{W׎O{}ٮ^SO:tibT}_XG[Vwћ֢N%bb0xÊn}65|>ŠZ-wzV1܇BToXgg?HrAM$z:?NO\r睧/9XLizk ly-ǂ}[MثѮ]˴կM_O~@^/rvR{Ao-T3]4\slQo7ަf_GTKFd<͜AuC׎[_z^+mڤQoŞTY%K yWG/!b[oG-ط=ԢUN{E6}Ҵe$3S~ᦛFe\wq.ji,]zXceP]m;~]rxsOtЫOLU_-OM{9XLizk lr{ڷoGyUx" v)6nln=!4i߼>Ὃ\֠}L/4I[5s~jPuQ-h9}TuPK6J۷?7vءc ض^{矟^PKsϭ/"b[޳(EݫnڴŠZ-'ϴiյUWv.^<ƍѺu߼xꩳ|Ыz\qŰew:j:NĠ:dwR87;vl7c"VuץO8O/\xioςTL]梷4>!өSDsY#5k`ѯo7ަfPGTKdN]:=?^{^]|]z/8^oM7J{]=4?ٴhٲ&o߭k_;>ĠAөS$zաC{v =_tVn;9ϧbz75Š:Sz>;טz|5b1 ,AuΊW}%i9ޢEIN)ιj ;16JJuMwћbPiڴig<]cnݺEXnkMe>GTK^/nmiP]muغ zW\1Ny:T.::ڶmо}8qOxi`1M _bzƾ/h2)ʕ{FyСC-6.zs[ZU8䐝Fo,sC6vs_fo-\ @s-'}Q)--jˠ:1Ao߭ ee{GII!Cv~4&?Z6^'~mʴ߾Q{|q _NEo.zk[K%%It&eڪ]k0`2d8>1s7N|Z|᲼1Mb}965|<bP-y_dݺ 9l\dzM}Po3/JKKjyyAXLTq^imrj߾[o~S͜90寻nDU^OEo.zk[>֘Mmךp1g.;ڠ:mߋ WRռ4pΉ;8ukcԢEI,]zXlXH,4Uq$1wAMW첣n[m.ߋGǠgϮy}>{Ao-hjk͡-A~[ n[}9~9lkʴ,rv>Zys𣏖]wG3S7{o"[s2wzUEӦi/cN۳{lX_zy}N{Ao-hjk͡-A~[ 2~|/[lѺ]MLi͡ kꪋ y*;kXLib-c,8$'EMW1w~L{Q{tϩ6mZ}Ao.zk[>ڠZos{ yPjd:$I_HիW״T7Ay bOg?H,4Tu[l:m/є{ջ>5z]v>ꂼb{>Eo zkGT[m}o zkj2p■Au:G)7n&OKK[ew5o4&?_<4ch&۫~Ѳ5ݵk۔[jtda;vioߣ-\֠}A۠Z6:eӦѯ_/O=uAuۘ:u[3.AyѷVs&4&6k-nڧڴiyMWXs6Y33߿ҜFҕW-\֠}AަZ6Z2{?\fPECɠ#[n.~^bXL leÆ8?m["{촷yܸ7cAhQo=t3:o75EmPm>o%ѩS`nMD}lq?W3 yYpOSxcK/=XLib̝{PƾeҜ?^lgԧرu|Ҭ樂i|Nu>梷 6德) RK$JKK?dPEv޹s8iҾм|'4"dC\%%oH,4$jԫ~k96m3>wJ7:w޹ z *2fʕo75EmPm)MeP6vKsuöܲ]M6!+Vvn"{l:6/kזҥ{z7VFV)ox-i`1M/ڧ!vW^9VQLyi߫g`׿vd„;w޺}b|>Eo zkGT[m}oST %\~5.o*yIh׶^}aoIU|9sn,cK:$b4_{MxVLGԫ~>cv66lk=.}zɉ;S3>Ƴf 75EmPmך~[ ~|Y-]qŰei2kpذ[Tײyٰ"Z(IvZyEa[ouH,4i l=wvOI|ҫ~>>8.]nBy)_^]cӦ;xn}[Ao-hjkbP-M0rnt6E=OOA~m'/+/Kې}&soGcjKĊu. "??=vءc̜90yfJ?#tV$b4m~y)Y}H9~K|Iel)oԩ.xhA.II^{\kXfl?梷 6A~>ŠZ .FlY{lhcÆwĺuKݺl>8wzVN1_r}~p6f/Gki^ v{%qjmpد_է}wIpȌߒ7no=b1 ,IZ`e,nvM.=zti8 O>V j,Z48VZZmgWQRz;iP]*Gk|e׿砺i/~1=niT̚50Yo$8G,^<8xK6;}k-_jFY36v2?8nut75ŠڠZo]zŠZ4=wvlЖmʠ+w~kNqQR}8g?Vƨti2}^obXL"T#}sz*=1Sc-5>eP]Ao-zk^-RžngP]}iXxAu4/??VT6&[.i4{u|G>r*_ Y]W.m /̊]vB}>Eo zk16[^km1"ƍqm'GϞ] UFGNm yX R)S+ƈ4&՟eȐ]5ʛBUS孷Ώ[5'wAB|>Eo zk16[߃jkm1"W׏oӁUb;_Z>ٳk,Z48~ͮyXk֌vTok-ŋ[4&ѵk8>qeGsϝzt)/_u>̔[ܴiyqǘի> |=75ŠڠZoC ~[ ('L.-[Ūw-cڴ6^^ ^ol K#[@ɒ괨σޚ5c뵁w޹ =(5I͏>Zw1&F]krv1u~ni`1Mɠ$vmzuwcgW̜90nadW3 gҫOs."?[n91N:_ݱ^pd4\֠4mkbP-M:ou~6zh\M=~,~qァxTLO 3ƌ=f^xDrˉ_yۯ }t|\y尘7oPL_q^q}bذ1jT(/3g{9-~9b1 ,UVٰ"~ókeˆg'{SO#90.7GC_lzs[ZDe T4&"""zk[EDD EDDb`1MDDDo EDDĠZDDbXL5EDDĠZDDD,4[ EDD,DDDDo zk1i`1MDDD֠[T;XL,֠jDDDDo zk1i`1Mo-"""""bP-""o1 ,"""bP-"""bA4bAo-""""""b1 ,zkjXL[zk1i`1MDDD֠j"""zk@o-""6XL5EDDĠZDDD,4[zkj|i4"""bP-"""bAo1i45j|i`1MDDD֠j"""zk@o-""6XL5EDDĠZDDD,4[zkj|i4"""bP-"""bAo5KDDĠZDDD,DDDրZDDD EDD-4[ZDDD EDDbXL%"""""zkji4[ZDDD EDDbXL5"""v4bZo zk1i`1MDDD֠j"""zk@o-""6bG-__s||~qD}uƍ,\[Ao-cODĠZw$I9,o1 ,?y^}ѧVK"zk5ElTFA/ϋK.92-,4&nAu-<[Z>UD EQhDiif{G{D,4AuE_/zk@o-cѧ E E>gm-=n"bڠ5SERT51g9@Є{n]zKJx%z7qgQaCk1 &>A^}T}x| EGQhy}< ;U۷O?=XL}ˠZ_/;[ZDOշנZDO!Cv\oO4G?ZL,OmPf_Ao-?֧kP-Q#bEʹ?Рbڠ_Eojj(fPmP XLT֠M<"Y_i`1MDgP-[zkĠ\?O+_~tzXnB|^wz\qŰXrx|{g/9#y&wzSqce<ԸG%I}v֐w{Nk=..ȸψ^G'wsr\vqţ_6-נbZ|IeǓN+\slZ{oImyy1ѭ[ݡCCszǎ+Mzh\A{inbXL_׹ŃcHoKJիksAKuPݐ'!O'Sm{[QO֠nZ?nJqu&TvYޛ}[Gǡ\cO//gy/7,w%sm~ zZOnlڴ:>}u뭣S>i)'zk[ΠZ?nJϩG#~IѩSnSOUܘq>Wr_Xk-K'g}rb/{MCnjotiBɸ;vl]*IQrC3^O%5JJxZ5(YO?XV_Lo6 i|-4Z]s~9ѡCz߹ opBc:]mg܃ oO||zk[֠Z?nJ|T6mZdpY˵m2xcqS37|?k5 sP?7Ausbt)=t;v;v1w-Z(vZC5F\ׁ?J_nP kƛo\slO=|͟ S~8R^3L)ۘ-mP}lV5.·uRhQ}Qiz?7nڴ<;l生.S^Ŕ)|>siujPnS^z?t^?Ѥ&ZLikSȭ[~t͠1͍+Sv 7z} GB}^5x|zk[ޠZ?nJ|%1fMWUF۠2g΁նw;˿9sl%bElujx3-,bZ뮫8sFmՍo~xRoFoP2|4ti[7ǹ7~|zk[ߠZ?~biiINnbZ.ОWrꜶ3a€jXlHnӇ._Mo}9sz΍(~neU=3P0 9dj;=go#Gnemm㡇YXr-4Z]%;V,렺ϧ=cdSqV}}c*Wuq cj(ٵɬ{7uB y/7i;CmNx91wAѩS?^S(z i?7Fᬳv?,9̠wݶњү_oPw;j ibZ"Fk3n~ᆑ9v{;zTZo zTT7uB 86)˾¬8ݢ(z a?7F!էoaAu=_o]Ҿ}+ 4Z.ǽ7?߾ :vle[,W_]PoѢEfNO* oO֠.AXAuS].ؠڠ_Xx`lm5^/97\oc(wjP][M}dZ4vӚӠZ. ֳ ߬3VtLcE -?㫿[޺c{MuPcj~a7GUwxkQi CXqDtӨΈ?˴i7ZmվQ][ZZbPmPmP T͠}nw{z߬_rF6YlYy7By|zk[נZ?n :pEº|ն1n/s){q?iny#Q6~nNBX[oTnej7NW_]PB 4Z.k=Սof;O~{jw/?㫿[޺xcqsgPtׁ ?66.I /VJ]f܃bӦY]gc6 ƺ(\vDŽ  zS}bHjli`19 By\~,\xHL|jeƌ>sT?S!=k5譋gP?7{M{Pcj~asP7G?߷i">`i٘B5 wzѯ_7zۭ=v7jjiAZnY~v >n~3U>hYlunK.mWTe֋ߍjO֠.AXܜT7uB ''ƺu^ .Qmmno~33>zɉ9mb/yxhQf7ٳkm}M4_̔藿Ѡ7v{n}L|{rX_{>T7uB鏛f7?eTѺuv}.8pzk66FᣏvmQ~uu׍0wS> G6mZ<06iQ6(ӫڶ.xh=`7k1 ,O٦Mjz=4.Auc?΍Ғn˰a=w-\jO֠C?٠iJܜЗ7AiPhٲo}Lo˥:JVKsjO>?=_Aƺ(;=aVeYuq? qOgw]^pN5(S7 :j&{nbXL_כgݗvZf۷:?z󠺱L1w/?㫿[Z?٠BܜЗ7AO>L{%xyA;o,N{_Gkc͠1Lyq57yX _Ao? lPׁ ?n΃b˛:I8]キ${l|,^<8Z*My1cvw٥sw>я&ş47~qݧƩFO~ 6~~YqC3f/ k/oSS-JbHʹ 㷿'-Z\">{2Q_جA{ٱlِ/~jP~))oSV1kfWDĊxSOYƲeC4_믫g֬i{SoŇ.vV,_~xt>͠1Lٸ2$Ibキ>nUc*WX?.sc m7Au1vPiڶm;9%%IVןm:vl7Vmnѡ>oTMȑOWZZ;1v߽[o*} z?7)۷o={v-hN9eQ Jߕ|JJwءcQkuʹ~f;wYS.۾ a|<S!>k5cq!(6ׁۜ?n΃b˛:t>~zrN׿`!uZHiws?7DžOlِ:5KV?\hoSqw\ AGmjuEwnbXL_שKsR"tԦN|5WwҡC{B{^S!>k5cq? :pc}P]}yT_?!zл90{ĊXh۶eN /8;첣 6~n i桇}.֭PPߟtj9_GmN.&4iO)=6(MZLik:vYߟ&^ziNյփ|dؽ]ԩ`c*WX?.AuYn /ov?\o}A̘q@kܹMLt;yq{flܲ]L_s>\>[U)S+ۘy y?Sc֬ |Oӧl!}iժ46QOSH17ݧ#cqۃuo|x rzcQM:>Z>W7(;9s`vkٲ4>{\w݈x%BwbkuS?hY,YrXlE=cܡ΃|䩧Ϊv]9]c!xZo zTWbc2~өq=u׍/|O矟AN2Ś5c㦛F 7k=.V>5yfJ&}{oIwyJ|Ge]7"Oƻ.)G u-aP_7O*G?IqeGW UF[7~3_iO ֠.AX\lqsR8}[Sz/7 kSIc涟FĿ#}k}@Aoo;ZD`/4"wd1 ,}Gzk[T E沟cLݯsI?<ˋ4wd1 ,Tg}GzkZl?qݠZD_nPQi`1M_j=[T;""jZDD֠ ED jDDbXL_EDDo zkj"bPU&"b1 0_""zk5TgAFA ""bO-""zk[b?Au>{KK慵os""Ӏ:_EDDo zkxWDĠ:foM7os""Ӏ8_EDDo zkxWDĠZDDD,4[Zo-""bP-"""i"""zk5EDDĠZDDD,4[ZDDD EDDbXL[ڠZDDbe&"""zk[AXLi""""""vP}[?;}H[bm9 /izk5,zdyErC?Xus;RDD+,s@[d['+*+2n)|j9 /[譁<_[}oT.K*25+ڑ"""kVvx\TX怆[zkɮN*%32+JO*L'Tą+JkZLa򲘦[zkuuRYUf١"""_Uө 292a1Mo-"""""UoXt~vH/^Ţehxzk5z+w3GCEDD 4k9$"yqAMo-"""""zz3CEDD 47ް}?zk5[g[/H.8^^￿N)X$"q1Mo-"""""zzdxM'ڱ"""~~bQY [zkuIǙ{ر"""[nQ||`AZDDDo EDD9+*u5M|c+""R yy5~*mEEZDDDo EDDY֕˒5]ѵ"""GRS%-k@EDD@>{^DDD^1'鴢"y?Ӆ.BxEvH#%_h]ӧ_1'dY[zk5[g[/Hi½~vH#gkTKx"""zk@o-""βLtK:z\;[DD֛$V,KGo-"""""zz5m;EDD)wݹGiK"""zk@o-""βLFָ$^zNs^z,9NWV&#-c@A|Co-"""""zl{s5m;;oϷEDDwޞW^!O=g  zk5[gu_{-Ʀ=""" MǪ[vSi~? l1Mo-"""""z{mli<;W&Y\P[:dɛlG?iò;W$oXlg p1Mo-"""""zɔ,򠈈s~Q~"-VT$S,Y@/EDDրZDDDoөTn~#PGò?W&["XP[:;%WT$/e{|شqJDDٴqy.+-J:Z§[zku**+wU=߃&""cy{~G.w**(xEDDրZDDDodlow^*d^*+/d (>zk5[`EErƊdS#z\Hqם{"ٴ"9Ò/[s;OZu-_oQ_wx\ֵ9OMbAMo-"""""z\>T>txyhiyy)Mzk5[g~\o觹O_)s??1nGع2yAӤ[zku**+*j{WT&byIxC/6+>“CDD6|R6+~CK.ɋKtEDDրZDDDoE+*tЯ W5+o!V#nNX}"""ۿ7V#~qʮqҨE[Zo EDD9ZQLYQYwPDD"ysEE24?zk5[r_l2̓$""Rܶba%h jzk5[x_"y΃%""ў[,92[zku-UV&#WT$O{EDDj;ȿ"2j[޺t\L^Q[Q|fxxEErY2yŜe@o-"""""zD7A5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^TWA5yeP @^T@:eeݓq3򊤬l IymI"Rv?:bHa'$Rߖ!)+")/HƕHʎJʺ;@!9f|dܸIYIgw!""ҴRnR6dܸ1;h :%ONƯKlx!""0'exXIs eܸIyIY""";#4 P_G&e6ih=餼|j|s"""99߰\zvIym """uMmɩnL˧$eo,oXOd@UFuLoυ3GΊ*DKF/{\;]DD KgGKSygE>=5$IN/Tcg.Y] RHb%q gK'G:_LN/y[>,)+ =05N|XnRJ&_߭~u=n_)*jRʠʠZ)22VJ)  RJ)j+jRʠʠZ)22VJ)  RJ)j+jRʠʠZ)22VJ)  RJ)j+jRʠʠZ)22VJ)  RJ)j+jRʠʠZ)22VJ)  RJ)j+jRʠʠZ)2(r%ILL+I\/""cV$I̠Z)2(r$I"""uȫIl ydPRJTs ydPRJT#$ِHF$Z! ju}/i(eP PvNC$=8q""")s RoJV3jEm{˔fm N$I1‰^)JSk?X}jҡARJ)j"skEO88mZg R*ms1HġAj&u_΍spN,bjA5@Wuc;Ńo=hR*mZ9+"$ ĠZ5$)7 2h Od7ulV#Rik+ei$ ȠZ5:SRijj NIFIRJu_[w8)IZT+jjA5@k$ɳU6&^8F)TZs@TP AARʠ1An:cRik$IR ZTT+  ؼ t5o1QJ)fjQ$I:;B0VJ)jux$>ѮcX*#Rik+ei$ݡ A26VJT$yuŌ$.b#Ri뮿]Ru1|S$':B1VJ)j6Ig.dLpRJ>+ZT+jjA5@YUucbݦu0J) S-Iġ A26VJTyU0k\cR*mZ9+"ItvheP R$I6|~]vUF0J)'WFiҪ o'IC+0jePݬ3D)jS$nxQ_lR*m6].~oJDV(pʠ';#N 1aqܵ)wM>\(߾.碘111qΈkԟNy/ϋ V|\5otܱc|Zuh}O'|ㄘyTwԽ.ߴ<&?=9;(:ܹ޶K{q]o_Р _?!vStܡcԼKhuaqxq_2(0tXi1RJ5|Tk$)qh"`P o8mY ӥUi4XE >Н7 'V}mo۵mz} 0XqDߺ}ңK :wPYQҿeP ЈU}޽OX#RikY_LC+ jeP]oѩ{z&{ͱ :]?~ni?V/}HŐhٶeW IlvŪV(J[+\-K.$PD Au? 6DM$onP~q[vlWﷻM61Ǔj}oZg l}$IuQ(jFS$&U~^lR*m6].nJdC+jeP]V~Ro!7 5I;o,}i:c1G[:V6~mٮe,"JA5@M٪o'`R*m`m9OP Au;x송m[Fȯ1o<>^448uhөMVwRzTה::8c[No;9hplvY|?t{gzf!/iQZyx~Dvi1qq7N!CbbwO߿eP VU}>An:cRikz$IR EȠZTjMҒԎ;vuR,yoImU|\MCv|K[As;yj*q9gbX2z8hAiG|of ln7t5o1QJ)f]=+ՂItvh"eP sޢ-N;e0X["]9,UV1wlPe-cKsrMg-mYu)LǠyPy6$I ߮cX*#Ri\j$Ivwh"fP s?̧tn'rbauϣ{6Ƞz[ͯhݱuo6YmgO1WA5PvJ$qw/6QJ)]e.U7%I2ڡA2i[K[mmߑ|yɑ3~9~%ILyfJoI:)L:=^o 6IgџbRJ>;B`P s_;>i;srn抏+b=N{]N޷^P/{~g<%zMՑi/JTEhU763QJ)OjxM$%T+ꜶmnkHŐzk^W˶-c?۠zϧ>3^OmbCE˟zi/=yF+eP Uw=ּF)TښuT/&I١jePvsQa98^oMˣ.ehiPyz},oUOZF{n}gR@$ɆϿoױ]zaRJuҖUNdwVhB AufltԽAnaKK{T/X{3J{}9*ehٶe|αJTn$I^}Ib#Ri뮿]RuaxS$Z1V24>b r'?=9ux2>v~D8s`_?m.F::*7Vz+eP I<[ `RJ>I(ܡ jePvv=j״9/ywIVuTO~zr &5|Ϧ:Klw8kz+jj&~bݦu0J) S-Iġ jePv-S9 vUǹuT/z u5m _9AiۥmtA13=AA5V}޽OX#RikճR-$IgVh Aub-RnEI,ߴn~>N[4nѦEl߭}8Vx|ʠڠȳ!IlvŪV(J[WT|;IZ 3VuҶAok: mٮnwڥ=ey.}hٶeV[>)?_2ȃ$yu7%I\݋`RJrWt٦KMIvh&ΠZTg/MN;uj?p}S4nyiow)=scﲽ#)IrX'I %=4οeP @&Il7O0QJ)~6+Z0VYmck 3>rCC=8N-{op7ok/|ma9ӣ~QRZRG3~5ÿ eP PVU}>An:cRikr$IR ̀A2Fu]8=1uTwޥs|w.Ӷs<.iQ-=,*7Vס ܪok\cR*mͺzV$ ̈́A2z;m<1`€eu%-J:},NytϜO wTXBA5@ Idf ŪV(J[W(mYZu$Iv/\$IIJn[2<'[6;jP NݻFiNOɔ: [m`EO޵aN>s␅DۮmV:w!ʠ'IfoK(J[_Y]RuvS$ hH+z'EDO^'waIVGdd$I&yG Au1Gʹ vg;uT'pT5TsA _9<:1ӀO4JA5@&Il7O0QJ)~6'bF vK:n0$Y wdߣwre٤A2z;{+v=4An{ņhѺE]uTO}vj383myyܗ4hվUavn7-/ET`U7؃63QJ)OjvM$%2 L}oGjjjIAlj7VYoQ }ƯfήfL꓾}Rr\}~qvlw_29UXw=ּI Sq+'WͿ9=õQJ)Uf_3;ItW[N.5Š|?G3ɇ;O nP 5cn={ͱi2hpN#nDޟs~?'m.mXRA5@$Ioul^X* ˔mw6OoYw.#*Tpu,Zv$mMʠ:,hY&oͯ۾;#oY/nwۼezioO4`cx {$n${quP]uhC㛿qRWVGmT}mܔ$e{joj L-yjpjePӶv[\uLw3Ǚ2N$fvV3wFiѺE,`i<sQ$%oס_2Ƞm$V}C=a&;XAuNTC~8}Mu)FzM\ѠC\OmPM㠺xN nP sҟ6bv~isVM;~PoyڞS.#,K?[t)>qx5I`.6)ʠ:Z&X/eϜr):nѺEyiNozז7`/\r)ʠ sR ZS ݾ,?㺧iOO88Znv1m㺧3R*9uAukR2$I:0NmPMT AuvŰ?94u~;+:l!utܱcTlAu$~}S.N,-Ԅ ~6'k݊Z ujoj ʠz-Zݎ-QIe,~cq~qvvi1`€hUթ{XE >4[uǹqD'1cϦő߶픶* }S=bq]gǒwoo-c׌g[M(j*nzAn::ݺwzx{nsrN 0<S]xm]^^|3bȱG^=}Go6}_qԸb{A#1ļ΋k^O}OoeqiGD!c׽wth]<6V>2ߐs}51r8hA{ޱ۾Ő1ClIY,^8ٿS.wh[9̏='}~' }7v{cCNWMVS->$IINC|۠95A2v+6T~Sy\>>;ַ5ݠ;n>;͠: mQҢ$v QUS t>c͛kհ!c}|reEYzt\L}ʻ;Ɗ{V>\{9-~qUe_Vo}~nW?C3}R7$?P{nڶ/}ٿgV8|`\tE}mv&ܶ$W>qxj*>Η'}BnWE˃O88voz>axl2mmu6~X\tE5kfڏ/&I9\c۠95A2Z IMמ]cKstS [o:*6T''Fv-fH}݈okfIM Q3$I ݮcXªf7piAX{ ޫڶuH֗Gb)Cj(Ś7r>O\K vP$I|˫mn:*>={tUV sϏHg=|MzTC:k祽i/ՠzœ꼘X7{]e_|;I3 T7Ω Au\GˢM6>LqDX:߾T^l|tܡcm[ _?V5of+eP 4ݓ$yu7%I\݋Х!wQmw!D}ĞYő]5=9N[u>~#5cTQ-ƽ7p 8b@ݾqY=ѳFȵV:Շ6%I2:NmPAuܠZT8pr*:>0n[AQߜq9GsZօ-GzƄPUMUHYyf4zkS~}2ꐔmڷ9 XtF,mITQ.w`t!o4⠌>|iyZ9+NZzҡE->gr4@1|~[Ϗ珍vM\u۱[ %QjwZ;ݩ U3uR갶>ꂷ/r\t-!j-^gc}S 'xRʿ8ˮٟM6ev},1&z[lElM/&[ʠ [4rPaC}նf̹7rc~;e<=WVl?p }1x\2.7T=}hg~%3:5Aj5C v7-9/͉q׎Ö{;z=q@^yxnDL9D=ώ_C*~S>ݎ-,<$11QqEߖ.񏎏aWA~S+'z}Gćq{N9c8 8mAu)V?ऋ'G?z47y\˧:-u-3n:woֶ qP=a~;3gz,?>~,u~˻j]vtˍ`\{b)O>WCO~h׻nӺw`jrT<)ǿf_3;$I:nI\T495!>" U3}'훶gs{H)eP 4[C$ŒvevAuToz7&_ }N:DoK4 4UxG{ rqւTkfgM%vk?:㯤|L3Teq_WW :z\.j~xiNjtgOr}۴.Zk61Acv^X_YVoe'"Y|-icPC-ǂhվUAͷR@6I&bz']vվ{zfm]=`C]IiIۚ:ә\pt*TVz;2=[9mhxN|iy}洯n9?F;fztPŇXvw޺s\5^jYk?X}Mܿ0%cdrI~h'"QԊ8pi]vb) f閪 %FuiPTGvDS=WCO>j[%n v\HZoUV>@6xoVGZ9^tAuI}X{qV7NO-Z1WV{˱Oj$IJ>^n;oT4ߨ~Z5ǚYѢugR@3"I>c͛k`8uj=ktʿWۣˎ_1_z>voM׹Cr;m6OڿwW_K-))ڽvOmZ=Fth^c}/+O@ܢUrʐ㉍Ox嫡\;'~|1I#.LIDAT8$%39*$ߎ{w""nɬlYcWjPh퓢M6i{$IeO) feH$>@ҮcX*#<WNLn^T[뮼2!U5A^4dA5O]q.9W}S4'6>go˶1q? QڲڷNdl@E}j#'Y-N^k6VM$/<}8la1Q1aQv=w}ooo* f{$&UN{}4 zAն _QoϹZ_uO_Wm{G;*o}ϺzVTquT/eq E3*m{l[~hնU{ֱqonJe.Uզ$IN)vKzթwKFO;yI۠Z5y׹*Iϴ7R@&IV]$9L#FT?ХC_?)vk}7j;a n%_uT]~t uoTzLcP]}؃>ј{e]j-J9'>ܬ_ ~6j]X/øb95A5յ;A2C?ԞTJT-UH7YiP-oj?*'N_5 ՏHE&8N-#N;"~gi'UOe=ѢUɽ{qo}->qx`$>+STcP]S{T+Ͳǩ{Dņ {R)eP 4s.t=ּ8Ǟulm}i5fV߼>S'WV1s,|jsNnMqPi{b|TjεsR$CB;5A5qP]6VS囖ۋJ)j$ǟ_ iױ^mAWV '̍XuHo 'Vޤ/LJy[a'VW6A^) s;+W{L?bz Wzz;I;-STӼwjojePSb) f{$nPRE]d Hua'Vmڴzw}.Sj};.;.}ҔUV]={~W-g_qvY%bP}wVݺw3ZgFzWV$INj]cܠ?nS{T&VW.=D-ڴ;tݎ-]9,fr26I3qP=)eqkl߁}].o {qosR;WޕO|7UrΨŠz}mvަ}˚͠]v ;hf'ox[ڔ7I\XPC^ny=5A5MqPS{TfPK_>?.x{C)ePmP $IrK!à≍OA7Ҡzӣ$巩]5^+ήS>=lniSw`y)MxP7wS<&9rʐjw͛k5}6C7׾R?$IiAujpj֠:6VJ) feN!Îw`Ǡ:i88o-^8;R^ސmyDmc7ןz!e[Suݶfnٺe|O[pZ}T_)שׂg5AΨv_W5`띶:>l&79[/%Iҹ({ yjpjP86VJ) f$I>N&bAͿ9FL򛾟f̼19ݦTߤU걫j쭿5i;o9~6e9jkߩ}\%9}s:k^նgmڷiV>;zjEi/-G?z4~G./rϽjuCʻ*cõ9 ーu0I]5ѢU$)mܠbT© RJ)jY$ɫI辋iP}gͿy|\wTČ/͈! v*p'ykRnEiL|Z-7kf7ዥV:lӥ}ޔ$IE?쫯STnՅsjT+R@k$OVW7rAu}UVO]S]Ҕu$IN}wA#E}zE-ޖSGf}eK2ޯvE:ŀ#.{Տ~homݮu;_ =ch{侱sJ撚uSTmAݢv]cN}wv{?M!ةoG1gcg^zx6^zš'wj'L?!qP3 |~M~6~_u95A5~>.S{ĠZ)2-U F'6>a\(<&7|.}Ҵߦ6#ݏox4f_3;s\ꉍOĥk.9Nv%e}׾-8-vmMNcq/O;֩~#sO[v{V44:?Sr]UGvDXi]gE >08M^w_j}_AXW괧O>w:*?ԭSV[n' oM5I[o'If<5A5~>VT穽kbPRJTE{$&Uy}37ú|5*戴8;^65~sa\%qǟŷᶸcM ✯|Xjq\XZoWWƼ΋IO_wTĵ?6QzkΊ8+y_;/f]=+ݾ,ė_wX~e{1 bWŅ^7挗[㲇/\/ӯՏ;_3x~eut٦K!$INrh!aST`&qjT+R@i$O*_1ޘN)T27շ/rhM1,;$ -$N]jRʠ(:$U F'6>a R*m 8<Ր$IJZ3VJ) 2;2dر5F0J)9_9'Ր$I:; Z)2aI||nЮc7(J[UB>qx#UTo[Q 2oD#""9sfPyC+Pr".z{^}=oEZQ tZR3%>~q5ܕs]|,P EZՊjE5Х[5~T|GU0""3 7d@RaC+P"jQT+@.poN_89ԁXѷE#!@RT_v)n hafYΊ xbE}//?5;_n˾,nȞݹ^Eugw_/.gjv\u7ԢXs\q/sg~bfp~LUՊjE5!WNxCU❏ܩ9X=!E@ISTwmrq˽qrXU] !Uǝvwp6׹qCcy !xQl>\[L7էuzeӆa_x {W׌_&ZT79N?}ĭ?Z%/)gju4PT@TBv7IIc9x ]hۡ(y.ӳiƆ5ȻW_?r%.~xqW]p4¡|跷so, /zQt)N2u8ݦS.WWkuѿg^zZQ tZAR3%>~q5ܕs]d{,P '[cѦbgj℅r>fZwcE;yxu#.Q۝77]o=WC瞟g~|f훷w_틷>ykq׌YsrQݧO\?9۵q/?o[Uy7W]ow3pwJ;GE5*CV|#w`DD$g|(V!E@.ze#o.q.iuȕ(H}o#?82zW?(;so.ˆn谢:9^5yу>v+NqK?e`sbKpRre}x7o[^Q Oeᙌ7 FDDrۇㄫQuC+(2fH;}/,Z^Z{i7{\zu^{ˏ~e<~]3r{s?+;nus֏vѰ_}t[>/Y5\TWUWŭ5VV1FQ-jhiJ͔0""3sWvBC+(>e7缬wFW0,1z]^ʢϊ/s}z!5!E.*ޜzg5SWT+P[i5~T|GU0""3 7dxRaC+#)n|1e~62 )O&Ze~VTZQ=aA' `hl9ޒ2mK&\T|ugXVQPQ-j! !}ӫ`DD$g֜c}n^>ds Ež'_To7? 0b@8& f=CQ-jF^9 SYw>r FDDrˇbCZMQ]rE?NZ1)c7^RϩƸ'Vą_Zg~bfjUuW0RT~VTwLQ'[_XYwk.k϶wjQ=̏|jQT@ !UEu[LYQݱEڿ^.}so<*}~^2E5ZQR3%>~q5ܕs]t|,P x,o8szlPN]\TŰ՝ST'e ?S?XĊN-SXFjE(շ~4j돪`DD$govVWPTdQ31>nOe-BՅnowgEu|Y2F\6⤟Ӱ 2WXm FE(D]B87HUWŃ/THx@["ۑDVPTdQGhtڮ8|#͋˾,\n+ ϊN/30bI?gX^uZTgۆ!c(EQ %hT x#w`DD$gӡX=!E@.ɢzZ˴e\Z{i%-Koiw5鶢ܢzͳkW=ٿNeĘcբSBx#c19g%c]hǡu%YT_~˸nu9WweJl9ޒ:.t{k/xK׶{|Bq?mԢ:@\~jQT@ijcz<B(whzEuI/yWVTV}., Zcͱ*c9_o|rFGe|+EQ %Ab1UWRT\Q}듷f]Ɗ'V.,G_]w-wWQ}2]UTw~Q?|ArF\6eo.]1QToXV^E^q_Ы41ƘS7BR^KQ]Eߕ廦+Uĝ,9^gѦ8r+;{{k)oY_p2jVu ࢺ35y=fKn^GL3/=36ࢺrPe=.V ?r~ɽSdlؙcWT@EQTJ߶J_]+*+2WV^7}S;sԔQq빋5؜-*n^ޖq~-k#;WQB}z6|oZRʧVkyܘ/.^Q yPTMQBc?<6}.o{cYsZsg}iNs~N훷-??>.%q⒉Y*~ 0(;sq3ڜ8;cCiqAQ(3(_g}l>[YkFWs[EuxS XsE\울{ْxb>朩k[soMG~5q z*;}4}[ܺ@- y?7_l>\t۝yɡ%? PQjy*oS5*n|a)ՅNYEYLUt,~”?[Gt4PT)gNvZ\?'~Y趷;qlQC;Ö;2ϫ.6nO*ڢznWm9aJl|1,Ew|w7{\8r`w{}eYqqqWcͱ',o1~LG̹%m?VzqɱSGht&^GE5h;㎸oVƚę|aA{.վ}>ǚcÏG9?'o6~Lki{gu\f~lN9K'qYΊ>9+.‚Nvuk|9q}s̏ό>?/&{z{㺿_o[Ǒ>9+_o[o{TN+EYT^'?r =E5OQVT+2ȜT7)cWooE(uՊjEugË:~N+fe-QT+)Պj97g!kf~b='RT+r ־ C̏-[=E5K{cՐ^Svܝ[+BT7)|u&STwyno:azݥݵm?wշ-q'geeK]sm\qͳk#{-E5GQ-"".QT@RT(K)EDDХ"""jRI`[/o"""E [ c>"ϘQTPBRɧں=i"""EM$ijv.yTTPBj m].KǦw[T"""Ew[by]ZQ @ Ifya;IUi7w,w@qIFwq{;"""ETTPͶ.nO}h@DDH3MɛNv(>3m]>{*5H}.SgP|R{ۺ]No6DDD,lev>;ŧr"""EOnokkop@Ѷ.rg:@DD25GÜ';T>Hdڿ:~Iū6;I+R[m].:Ҥ:kn䭐J r@qKۻ3k# nδGڿ:Ivr@񫭝E~U"""ݔm57oNn( ]x`@DD2KTi'5$;1NUH'Θq:$ɍNj(-I.xkx1xk>EsNf(=I2=~|ml9֢:kc?6߻;4%Ƀ\ Ѓ"""=)ߒA'1%K |.H'e׶WR7’%g9%JyخJ8$j'/ |/VHI5:<c޼!=)kQ/dZ¼yг,M&Tr$ c?6xQ ""R`v|lM!wR KINV虖'C*y' 6ST9;」RR'гզTr"^swHl{1N RCx)K+Ȧq ['~z1Ss}☏#V+cy]߻0o@'Z0o1I%o$Y$ڲdY!IT.c1:Ƀaɒ\@dzH)1Ƙ繐$ӝLJC*cicO$t!I-e1~+$áv,ԠP^R'B>0 hH05t9JRgB0&y3τTP[{C#@eXIfMM*V^(if,,;ؙ%tEXtdate:create2020-12-03T15:47:23+00:00o%tEXtdate:modify2020-12-03T15:47:23+00:002putEXtexif:ColorSpace1ItEXtexif:ExifOffset102sB) tEXtexif:PhotometricInterpretation2+tEXtexif:PixelXDimension1962p tEXtexif:PixelYDimension1595AIENDB`pappl-1.0.3/doc/pappl-body.md000066400000000000000000001264311403603036100157700ustar00rootroot00000000000000Introduction ============ PAPPL is a simple C-based framework/library for developing CUPS Printer Applications, which are the recommended replacement for printer drivers. It is sufficiently general purpose to support any kind of printer or driver that can be used on desktops, servers, and in embedded environments. PAPPL embeds a multi-threaded HTTP / [IPP Everywhere™](https://www.pwg.org/ipp/everywhere.html) server and provides callbacks for a variety of events that allows a GUI or command-line application to interact with both the local user that is running the Printer Application and any network clients that are submitting print jobs, querying printer status and capabilities, and so forth. PAPPL provides a simple driver interface for raster graphics printing, and developers of CUPS Raster drivers will readily adapt to it. PAPPL can also be used with printers that support vector graphics printing although you'll have to develop more code to support them. Drivers provide configuration and capability information to PAPPL, and PAPPL then calls the driver to print things as needed. PAPPL automatically supports printing of JPEG, PNG, PWG Raster, Apple Raster, and "raw" files to printers connected via USB and network (AppSocket/JetDirect) connections. Other formats can be supported through "filter" callbacks you register. PAPPL is Copyright © 2019-2020 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used *freely* in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. Requirements ------------ PAPPL requires a POSIX-compliant host operating system such as Linux®, macOS®, QNX®, or VxWorks®, a C99 compiler like Clang or GCC, a `make` program that supports the `include` directive, and the following support libraries: - CUPS 2.2 or later for the CUPS libraries (libcups2/libcupsimage2) - GNU TLS 2.8 or later (except on macOS) for TLS support - JPEGLIB 9 or later for JPEG image support - LIBPNG 1.6 or later for PNG image support - PAM for authentication support - ZLIB 1.1 or later for compression support Run the following commands to install the prerequisites on CentOS 7, Fedora 22, and Red Hat Enterprise Linux 7: sudo yum groupinstall 'Development Tools' sudo yum install avahi-devel cups-devel gnutls-devel libjpeg-turbo-devel \ libpam-devel libpng-devel libusbx-devel pam-devel zlib-devel Run the following commands to install the prerequisites on CentOS 8, Fedora 23 or later, and Red Hat Enterprise Linux 8: sudo dnf groupinstall 'Development Tools' sudo dnf install avahi-devel cups-devel gnutls-devel libjpeg-turbo-devel \ libpam-devel libpng-devel libusbx-devel pam-devel zlib-devel Run the following commands to install the prerequisites on Debian GNU/Linux, Raspbian, and Ubuntu: sudo apt-get install build-essential libavahi-client-dev libcups2-dev \ libcupsimage2-dev libgnutls28-dev libjpeg-dev libpam-dev libpng-dev \ libusb-1.0-0-dev zlib1g-dev Finally, after installing Xcode from the AppStore run the following commands to install the prerequisites on macOS: (install brew if necessary) brew install libjpeg brew install libpng brew install libusb or download, build, and install libjpeg, libpng, and libusb from source. Building PAPPL -------------- PAPPL uses the usual `configure` script to generate a `make` file: ./configure [options] make Use `./configure --help` to see a full list of options. There is also an Xcode project under the `xcode` directory that can be used on macOS: open xcode/pappl.xcodeproj You can test the build by running the PAPPL test program: testsuite/testpappl Installing PAPPL ---------------- Once you have successfully built PAPPL, install it using: sudo make install By default everything will be installed under `/usr/local`. Use the `--prefix` configure option to override the base installation directory. Set the `DESTDIR`, `DSTROOT`, or `RPM_BUILD_ROOT` environment variables to redirect the installation to a staging area, as is typically done for most software packaging systems (using one of those environment variables...) Detecting PAPPL --------------- PAPPL can be detected using the `pkg-config` command, for example: if pkg-config --exists pappl; then ... fi In a makefile you can add the necessary compiler and linker options with: ```make CFLAGS += `pkg-config --cflags pappl` LIBS += `pkg-config --libs pappl` ``` Header Files ------------ PAPPL provides a top-level header file that should be used: ```c #include ``` This header includes all of the base object headers in PAPPL as well as the CUPS header files that provide the HTTP and IPP support functions. API Overview ============ PAPPL provides five main objects: - [The System](@) (`pappl_system_t`): The main object that manages the whole printer application; - [Clients](@) (`pappl_client_t`): The objects that manage client connections; - [Devices](@) (`pappl_device_t`): The objects that manage printer connections; - [Printers](@) (`pappl_printer_t`): The objects that manage printers; and - [Jobs](@) (`pappl_job_t`): The objects that manage print jobs. ![PAPPL Block Diagram::100%](../doc/pappl-block.png) The System ---------- The system is an object of type [`pappl_system_t`](@@) that manages client and device connections, listeners, the log, printers, and resources. It implements a subset of the IPP System Service ([PWG 5100.22](https://ftp.pwg.org/pub/pwg/candidates/cs-ippsystem10-20191122-5100.22.pdf)) with each printer implementing IPP Everywhere™ ([PWG 5100.14](https://ftp.pwg.org/pub/pwg/candidates/cs-ippeve11-20200515-5100.14.pdf)) and some extensions to provide compatibility with the full range of mobile and desktop client devices. In addition, it provides an optional embedded web interface, raw socket printing, and USB printer gadget (Linux only). A system object is created using the [`papplSystemCreate`](@@) function and deleted using the [`papplSystemDelete`](@@) function. Each system manages zero or more [printers](@) which can be accessed using either of the following functions: - [`papplSystemFindPrinter`](@@): Finds the named or numbered print queue, and - [`papplSystemIteratePrinters`](@@): Iterates all print queues managed by the system. The [`papplSystemLoadState`](@@) function is often used to load system values and printers from a prior run which used the [`papplSystemSaveState`](@@) function. IP and domain socket listeners are added using the [`papplSystemAddListeners`](@@) function. The `papplSystemGet` functions get various system values: - [`papplSystemGetAdminGroup`](@@): Gets the administrative group name, - [`papplSystemGetAuthService`](@@): Gets the PAM authorization service name, - [`papplSystemGetContact`](@@): Gets the contact information for the system, - [`papplSystemGetDefaultPrinterID`](@@): Gets the default printer's ID number, - [`papplSystemGetDefaultPrintGroup`](@@): Gets the default print group name, - [`papplSystemGetDNSSDName`](@@): Gets the system's DNS-SD service instance name, - [`papplSystemGetFooterHTML`](@@): Gets the HTML to use at the bottom of the web interface, - [`papplSystemGetGeoLocation`](@@): Gets the geographic location as a "geo:" URI, - [`papplSystemGetHostname`](@@): Gets the hostname for the system, - [`papplSystemGetLocation`](@@): Gets the human-readable location, - [`papplSystemGetLogLevel`](@@): Gets the current log level, - [`papplSystemGetMaxLogSize`](@@): Gets the maximum log file size (when logging to a file), - [`papplSystemGetName`](@@): Gets the name of the system that was passed to [`papplSystemCreate`](@@), - [`papplSystemGetNextPrinterID`](@@): Gets the ID number that will be used for the next printer that is created, - [`papplSystemGetOptions`](@@): Gets the system options that were passed to [`papplSystemCreate`](@@), - [`papplSystemGetOrganization`](@@): Gets the organization name, - [`papplSystemGetOrganizationalUnit`](@@): Gets the organizational unit name, - [`papplSystemGetPassword`](@@): Gets the web interface access password, - [`papplSystemGetPort`](@@): Gets the port number assigned to the system, - [`papplSystemGetServerHeader`](@@): Gets the HTTP "Server:" header value, - [`papplSystemGetSessionKey`](@@): Gets the current cryptographic session key, - [`papplSystemGetTLSOnly`](@@): Gets the "tlsonly" value that was passed to [`papplSystemCreate`](@@), - [`papplSystemGetUUID`](@@): Gets the UUID assigned to the system, and - [`papplSystemGetVersions`](@@): Gets the firmware version numbers that are reported to clients. Similarly, the `papplSystemSet` functions set various system values: - [`papplSystemSetAdminGroup`](@@): Sets the administrative group name, - [`papplSystemSetContact`](@@): Sets the contact information for the system, - [`papplSystemSetDefaultPrinterID`](@@): Sets the ID number of the default printer, - [`papplSystemSetDefaultPrintGroup`](@@): Sets the default print group name, - [`papplSystemSetPrinterDrivers`](@@): Sets the list of printer drivers, - [`papplSystemSetDNSSDName`](@@): Sets the DNS-SD service instance name, - [`papplSystemSetFooterHTML`](@@): Sets the HTML to use at the bottom of the web interface, - [`papplSystemSetGeoLocation`](@@): Sets the geographic location of the system as a "geo:" URI, - [`papplSystemSetHostname`](@@): Sets the system hostname, - [`papplSystemSetLocation`](@@): Sets the human-readable location, - [`papplSystemSetLogLevel`](@@): Sets the current log level, - [`papplSystemSetMaxLogSize`](@@): Sets the maximum log file size (when logging to a file), - [`papplSystemSetMIMECallback`](@@): Sets a MIME media type detection callback, - [`papplSystemSetNextPrinterID`](@@): Sets the ID to use for the next printer that is created, - [`papplSystemSetOperationCallback`](@@): Sets an IPP operation callback, - [`papplSystemSetOrganization`](@@): Sets the organization name, - [`papplSystemSetOrganizationalUnit`](@@): Sets the organizational unit name, - [`papplSystemSetPassword`](@@): Sets the web interface access password, - [`papplSystemSetSaveCallback`](@@): Sets a save callback, usually [`papplSystemSaveState`](@@), that is used to save configuration and state changes as the system runs, - [`papplSystemSetUUID`](@@): Sets the UUID for the system, and - [`papplSystemSetVersions`](@@): Sets the firmware versions that are reported to clients, ### Logging ### The PAPPL logging functions record messages to the configured log file. The [`papplLog`](@@) records messages applying to the system as a whole while [`papplLogClient`](@@), [`papplLogJob`](@@), and [`papplLogPrinter`](@@) record messages specific to a client connection, print job, or printer respectively. The "level" argument specifies a log level from debugging (`PAPPL_LOGLEVEL_DEBUG`) to fatal (`PAPPL_LOGLEVEL_FATAL`) and is used to determine whether the message is recorded to the log. The "message" argument specifies the message using a `printf` format string. ### Navigation Links ### Navigation links can be added to the web interface using the [`papplSystemAddLink`](@@) function and removed using the [`papplSystemRemoveLink`](@@) function. ### Run Loops ### PAPPL provides two functions to manage its run loops: - [`papplSystemRun`](@@): Runs a system object that you have created and configured, and - [`papplMainloop`](@@): Handles processing standard command-line arguments and sub-commands for a printer application. You can learn more about the second function in the chapter on the [HP Printer Application Example](@). The system will run until it receives a Shutdown-System request, a termination signal, or you call the [`papplSystemShutdown`](@@) function. You can test whether is system is running with the [`papplSystmeIsRunning`](@@) function and whether it has been shut down with the [`papplSystemIsShutdown`](@@) function. ### Resources ### PAPPL provides several functions for adding static and dynamic resources to the embedded HTTP server: - [`papplSystemAddResourceCallback`](@@): Adds a callback to serve dynamic content (HTTP GET or POST requests), - [`papplSystemAddResourceData`](@@): Adds a static resource using an `unsigned char` array, - [`papplSystemAddResourceDirectory`](@@): Adds static resource files from a directory, - [`papplSystemAddResourceFile`](@@): Adds a static resource file, - [`papplSystemAddResourceString`](@@): Adds a static resource using a constant string, - [`papplSystemAddStringsData`](@@): Adds a localization file from a constant string, and - [`papplSystemAddStringsFile`](@@): Adds a localization file from a file. Resources may be removed using the [`papplSystemRemoveResource`](@@) function. Clients ------- The PAPPL client functions provide access to client connections. Client connections and the life cycle of the [`pappl_client_t`](@@) objects are managed automatically by the system object for the printer application. The `papplClientGet` functions get the current values for various client- supplied request data: - [`papplClientGetCSRFToken`](@@): Get the current CSRF token. - [`papplClientGetCookie`](@@): Get the value of a named cookie. - [`papplClientGetForm`](@@): Get form data from the client request. - [`papplClientGetHTTP`](@@): Get the HTTP connection associate with a client. - [`papplClientGetHostName`](@@): Get the host name used for the client request. - [`papplClientGetHostPort`](@@): Get the host port used for the client request. - [`papplClientGetJob`](@@): Get the job object associated with the client request. - [`papplClientGetMethod`](@@): Get the HTTP request method. - [`papplClientGetOperation`](@@): Get the IPP operation code. - [`papplClientGetOptions`](@@): Get any options from the HTTP request URI. - [`papplClientGetPrinter`](@@): Get the printer object associated with the client request. - [`papplClientGetRequest`](@@): Get the IPP request. - [`papplClientGetResponse`](@@): Get the IPP response. - [`papplClientGetSystem`](@@): Get the system object associated with the client. - [`papplClientGetURI`](@@): Get the HTTP request URI. - [`papplClientGetUsername`](@@): Get the authenticated username, if any. ### Responding to Client Requests ### The [`papplClientRespond`](@@) function starts a HTTP response to a client request. The [`papplClientHTMLHeader`](@@) and [`papplClientHTMLFooter`](@@) functions send standard HTML headers and footers for the printer application's configured web interface while the [`papplClientHTMLEscape`](@@), [`papplClientHTMLPrintf`](@@), [`papplClientHTMLPuts`](@@), and [`papplClientHTMLStartForm`](@@) functions send HTML messages or strings. Use the [`papplClientGetHTTP`](@@) and (CUPS) `httpWrite2` functions to send arbitrary data in a client response. Cookies can be included in web browser requests using the [`papplClientSetCookie`](@@) function. The [`papplClientRespondIPP`](@@) function starts an IPP response. Use the various CUPS `ippAdd` functions to add attributes to the response message. The [`papplClientRespondRedirect`](@@) function sends a redirection response to the client. ### HTML Forms ### PAPPL provides the [`papplClientGetCSRFToken`](@@), [`papplClientGetForm`](@@), [`papplClientHTMLStartForm`](@@), and [`papplClientValidateForm`](@@) functions to securely manage HTML forms. The [`papplClientHTMLStartForm`](@@) function starts a HTML form and inserts a hidden variable containing a CSRF token that was generated by PAPPL from a secure session key that is periodically updated. Upon receipt of a follow-up form submission request, the [`papplClientGetForm`](@@) and [`papplClientValidateForm`](@@) functions can be used to securely read the form data (including any file attachments) and validate the hidden CSRF token. ### Authentication and Authorization ### PAPPL supports both user-based authentication using PAM modules and a simple cookie-based password authentication mechanism that is used to limit administrative access through the web interface. The [`papplHTMLAuthorize`](@@) function authorizes access to the web interface and handles displaying an authentication form on the client's web browser. The return value indicates whether the client is authorized to access the web page. The [`papplIsAuthorized`](@@) function can be used to determine whether the current client is authorized to perform administrative operations and is normally only used for IPP clients. Local users are always authorized while remote users must provide credentials (typically a username and password) for access. This function will return an HTTP status code that can be provided to the [`httpClientSendResponse`](@@) function. The value `HTTP_STATUS_CONTINUE` indicates that authorization is granted and the request should continue. The [`papplGetUsername`](@@) function can be used to obtain the authenticated user identity. Devices ------- The PAPPL device functions provide access to output device connections and to list available output devices. Output devices are accessed using Uniform Resource Identifier (URI) strings such as "file:///path/to/file-or-directory", "socket://11.22.33.44", and "usb://make/model?serial=number". The follow URI schemes are supported by PAPPL: - "dnssd": Network (AppSocket) printers discovered via DNS-SD/mDNS (Bonjour), - "file": Local files and directories, - "snmp": Network (AppSocket) printers discovered via SNMPv1, - "socket": Network (AppSocket) printers using a numeric IP address or hostname and optional port number, and - "usb": Local USB printer. Custom device URI schemes can be registered using the [`papplDeviceAddScheme`](@@) function. The [`papplDeviceList`](@@) function lists available output devices, providing each available output device to the supplied callback function. The list only contains devices whose URI scheme supports discovery, at present USB printers and network printers that advertise themselves using DNS-SD/mDNS and/or SNMPv1. The [`papplDeviceOpen`](@@) function opens a connection to an output device using its URI. The [`papplDeviceClose`](@@) function closes the connection. The [`papplDevicePrintf`](@@), [`papplDevicePuts`](@@), and [`papplDeviceWrite`](@@) functions send data to the device, while the [`papplDeviceRead`](@@) function reads data from the device. The `papplDeviceGet` functions get various device values: - [`papplDeviceGetID`](@@): Gets the current IEEE-1284 device ID string, - [`papplDeviceGetMetrics`](@@): Gets statistical information about all communications with the device while it has been open, and - [`papplDeviceGetStatus`](@@): Gets the hardware status of a device mapped to the [`pappl_preason_t`](@@) bitfield. Printers -------- Printers are managed by the system and are represented by the [`pappl_printer_t`](@@) type. Each printer is connected to a device and uses a driver to process document data and produce output. PAPPL supports raster printers out-of-the-box and provides filter callbacks to support other kinds of printers. Printers are created using the [`papplPrinterCreate`](@@) function and deleted using the [`papplPrinterDelete`](@@) function. Each printer has zero or more jobs that are pending, processing (printing), or completed which can be access using any of the following functions: - [`papplPrinterFindJob`](@@): Finds the numbered print job, - [`papplPrinterIterateActiveJobs`](@@): Iterates active print jobs managed by the printer, - [`papplPrinterIterateAllJobs`](@@): Iterates all print jobs managed by the printer, and - [`papplPrinterIterateCompletedJobs`](@@): Iterates completed print jobs managed by the printer. The `papplPrinterGet` functions get various printer values: - [`papplPrinterGetContact`](@@): Gets the contact information, - [`papplPrinterGetDeviceID`](@@): Gets the IEEE-1284 device ID, - [`papplPrinterGetDeviceURI`](@@): Gets the device URI, - [`papplPrinterGetDNSSDName`](@@): Gets the DNS-SD service instance name, - [`papplPrinterGetDriverAttributes`](@@): Gets the driver IPP attributes, - [`papplPrinterGetDriverData`](@@): Gets the driver data, - [`papplPrinterGetDriverName`](@@): Gets the driver name, - [`papplPrinterGetGeoLocation`](@@): Gets the geographic location as a "geo:" URI, - [`papplPrinterGetID`](@@): Gets the ID number, - [`papplPrinterGetImpressionsCompleted`](@@): Gets the number of impressions (sides) that have been printed, - [`papplPrinterGetLocation`](@@): Gets the human-readable location, - [`papplPrinterGetMaxActiveJobs`](@@): Gets the maximum number of simultaneous active (queued) jobs, - [`papplPrinterGetMaxCompletedJobs`](@@): Gets the maximum number of completed jobs for the job history, - [`papplPrinterGetName`](@@): Gets the name, - [`papplPrinterGetNextJobID`](@@): Gets the ID number of the next job that is created, - [`papplPrinterGetNumberOfActiveJobs`](@@): Gets the current number of active jobs, - [`papplPrinterGetNumberOfCompletedJobs`](@@): Gets the current number of completed jobs in the job history, - [`papplPrinterGetNumberOfJobs`](@@): Gets the total number of jobs in memory, - [`papplPrinterGetOrganization`](@@): Gets the organization name, - [`papplPrinterGetOrganizationalUnit`](@@): Gets the organizational unit name, - [`papplPrinterGetPath`](@@): Gets the path of a printer web page, - [`papplPrinterGetPrintGroup`](@@): Gets the print authorization group name, - [`papplPrinterGetReasons`](@@): Gets the "printer-state-reasons" bitfield, - [`papplPrinterGetState`](@@): Gets the "printer-state" value, - [`papplPrinterGetSupplies`](@@): Gets the current supply levels, and - [`papplPrinterGetSystem`](@@): Gets the system managing the printer. Similarly, the `papplPrinterSet` functions set those values: - [`papplPrinterSetContact`](@@): Sets the contact information, - [`papplPrinterSetDNSSDName`](@@): Sets the DNS-SD service instance name, - [`papplPrinterSetDriverData`](@@): Sets the driver data and attributes, - [`papplPrinterSetDriverDefaults`](@@): Sets the driver defaults, - [`papplPrinterSetGeoLocation`](@@): Sets the geographic location as a "geo:" URI, - [`papplPrinterSetImpressionsCompleted`](@@): Sets the number of impressions that have been printed, - [`papplPrinterSetLocation`](@@): Sets the human-readable location, - [`papplPrinterSetMaxActiveJobs`](@@): Sets the maximum number of jobs that can be queued, - [`papplPrinterSetMaxCompletedJobs`](@@): Sets the maximum number of completed jobs that are kept in the job history, - [`papplPrinterSetNextJobID`](@@): Sets the ID number of the next job that is created, - [`papplPrinterSetOrganization`](@@): Sets the organization name, - [`papplPrinterSetOrganizationalUnit`](@@): Sets the organizational unit name, - [`papplPrinterSetPrintGroup`](@@): Sets the print authorization group name, - [`papplPrinterSetReadyMedia`](@@): Sets the ready (loaded) media, - [`papplPrinterSetReasons`](@@): Sets or clears "printer-state-reasons" values, - [`papplPrinterSetSupplies`](@@): Sets supply level information, and - [`papplPrinterSetUSB`](@@): Sets the USB vendor ID, product ID, and configuration options. ### Accessing the Printer Device ### When necessary, the device associated with a printer can be opened with the [`papplPrinterOpenDevice`](@@) function and subsequently closed using the [`papplPrinterCloseDevice`](@@) function. ### Controlling Printers ### Printers are stopped using the [`papplPrinterPause`](@@) function and started using the [`papplPrinterResume`](@@) function. ### Navigation Links ### Navigation links can be added to the web interface using the [`papplPrinterAddLink`](@@) function and removed using the [`papplPrinterRemoveLink`](@@) function. Jobs ---- Jobs are managed by the system and are represented by the [`pappl_job_t`](@@) type. Jobs are created and deleted automatically by the system object for the printer application. The `papplJobGet` functions get the current values associated with a job: - [`papplJobGetAttribute`](@@): Gets a named Job Template attribute, - [`papplJobGetData`](@@): Gets driver-specific processing data, - [`papplJobGetFilename`](@@): Gets the filename of the document data, - [`papplJobGetFormat`](@@): Gets the MIME media type for the document data, - [`papplJobGetID`](@@): Gets the job's numeric ID, - [`papplJobGetImpressions`](@@): Gets the number of impressions (sides) in the document, - [`papplJobGetImpressionsCompleted`](@@): Gets the number of impressions (sides) that have been printed, - [`papplJobGetMessage`](@@): Gets the current processing message (if any), - [`papplJobGetName`](@@): Gets the job name/title, - [`papplJobGetPrinter`](@@): Gets the printer for the job, - [`papplJobGetReasons`](@@): Gets the "job-state-reasons" bitfield, - [`papplJobGetState`](@@): Gets the "job-state" value, - [`papplJobGetTimeCompleted`](@@): Gets the UNIX time when the job completed, aborted, or was canceled, - [`papplJobGetTimeCreated`](@@): Gets the UNIX time when the job was created, - [`papplJobGetTimeProcessed`](@@): Gets the UNIX time when processing started, and - [`papplJobGetUsername`](@@): Gets the name of the user that created the job. Similarly, the `papplJobSet` functions set the current values associated with a job: - [`papplJobSetData`](@@): Sets driver-specific processing data, - [`papplJobSetImpressions`](@@): Sets the number of impressions (sides) in the job, - [`papplJobSetImpressionsCompleted`](@@): Updates the number of impressions (sides) that have been completed, - [`papplJobSetMessage`](@@): Set the current processing message, and - [`papplJobSetReasons`](@@): Sets or clears bits in the "job-state-reasons" bitfield. ### Controlling Jobs ### The [`papplJobCancel`](@@) function cancels processing of a job while the [`papplJobIsCanceled`](@@) function returns whether a job is in the canceled state (`IPP_JSTATE_CANCELED`) or is in the process of being canceled (`IPP_JSTATE_PROCESSING` and `PAPPL_JREASON_PROCESSING_TO_STOP_POINT`). ### Processing Jobs ### PAPPL stores print options in [`pappl_pr_options_t`](@@) objects. The [`papplJobCreatePrintOptions`](@@) function creates a new print option object and initializes it using the job's attributes and printer defaults. The creator of a print options object must free it using the [`papplJobDeletePrintOptions`](@@) function. The [`papplJobOpenFile`](@@) function opens a file associated with the job. The file descriptor must be closed by the caller using the `close` function. The primary document file for a job can be retrieved using the [`papplJobGetFilename`](@@) function, and its format using the [`papplJobGetFormat`](@@) function. Filters allow a printer application to support different file formats. PAPPL includes raster filters for PWG and Apple raster documents (streamed) as well as JPEG and PNG image files. Filters for other formats or non-raster printers can be added using the [`papplSystemAddMIMEFilter`](@@) function. The [`papplJobFilterImage`](@@) function converts raw image data to raster data suitable for the printer, and prints using the printer driver's raster callbacks. Raster filters that output a single page can use this function to handle the details of scaling, cropping, color space conversion, and dithering for raster printers. A raster filter that needs to print more than one image must use the raster callback functions in the [`pappl_pr_driver_data_t`](@@) structure directly. Filters that produce non-raster data can call the `papplDevice` functions to directly communicate with the printer in its native language. The HP Printer Application Example ================================== This chapter will guide you through creating a simple PCL printer application based on the old CUPS "rastertohp" filter. The complete code can be found in the [hp-printer-app](https://github.com/michaelrsweet/hp-printer-app) project and serves as an overgrown "Hello, World!" program for PAPPL. The Main Loop ------------- All printer applications require some sort of a main loop for processing IPP requests, printing files, and so forth. PAPPL provides the [`papplMainloop`](#papplMainLoop) convenience function that provides a standard command-line interface, and in the "hp-printer-app" project the `main` function just calls `papplMainloop` to do all of the work: ```c int main(int argc, char *argv[]) { return (papplMainloop(argc, argv, /*version*/"1.0", /*footer_html*/NULL, (int)(sizeof(pcl_drivers) / sizeof(pcl_drivers[0])), pcl_drivers, pcl_callback, pcl_autoadd, /*subcmd_name*/NULL, /*subcmd_cb*/NULL, /*system_cb*/NULL, /*usage_cb*/NULL, /*data*/NULL)); } ``` As you can see, we pass in the command-line arguments, a version number ("1.0") for our printer application, a list of drivers supported by the printer application, a callback for the driver that will configure a printer for a named driver, and a callback for automatically adding printers. The "footer_html" argument can be provided to override the default footer text that appears at the bottom of the web interface. In this case we are passing `NULL` to use the default. The drivers list is a collection of names, descriptions, IEEE-1284 device IDs, and extension pointers. Ours looks like this for HP DeskJet, HP LaserJet, and a generic PCL driver: ```c static pappl_pr_driver_t pcl_drivers[] =// Driver information { /* name */ /* description */ /* device ID */ /* extension */ { "hp_deskjet", "HP Deskjet", NULL, NULL }, { "hp_generic", "Generic PCL", "CMD:PCL;", NULL }, { "hp_laserjet", "HP LaserJet", NULL, NULL } }; ``` [The driver callback](@) is responsible for providing the data associated with each driver, while [the auto-add callback](@) tells PAPPL which driver to use for the printers it finds. The "subcmd\_name" and "subcmd\_cb" arguments specify a custom sub-command for the printer application along with a function to call. Since this printer application does not have a custom sub-command, we pass `NULL` for both. The "system_cb" argument specifies a callback for creating the [system object](#the-system). We pass `NULL` because we want the default system object for this printer application, which supports multiple printers and a web interface. The "usage_cb" argument specifies a callback from displaying the program usage message. Passing `NULL` yields the default usage message. The "data" argument specifies a pointer that will be passed to any of the callback functions. We pass `NULL` since we do not have any extra contextual information to provide to the callbacks. The `papplMainloop` function runs until all processing for the current sub-command is complete, returning the exit status for the program. The Driver Callback ------------------- The PAPPL driver callback is called when the system is creating a new printer object. It receives pointers to the system, driver name, device URI, a driver data structure, an IPP attributes pointer, and the callback data, and it returns a boolean indicating whether the driver callback was successful: ```c typedef bool (*pappl_pr_driver_cb_t)(pappl_system_t *system, const char *driver_name, const char *device_uri, pappl_pr_driver_data_t *driver_data, ipp_t **driver_attrs, void *data); ``` A driver callback can communicate with the printer via its device URI as needed to configure the driver, however our printer application doesn't need to do that. The first thing our `pcl_callback` function does is to set the printer callbacks in the driver data structure: ```c driver_data->printfile_cb = pcl_print; driver_data->rendjob_cb = pcl_rendjob; driver_data->rendpage_cb = pcl_rendpage; driver_data->rstartjob_cb = pcl_rstartjob; driver_data->rstartpage_cb = pcl_rstartpage; driver_data->rwriteline_cb = pcl_rwriteline; driver_data->status_cb = pcl_status; ``` The `pcl_print` function prints a raw PCL file while the `pcl_r` functions print raster graphics. The `pcl_status` updates the printer status. Next is the printer's native print format as a MIME media type, in this case HP PCL: ```c driver_data->format = "application/vnd.hp-pcl"; ``` The default orientation and print quality follow: ```c driver_data->orient_default = IPP_ORIENT_NONE; driver_data->quality_default = IPP_QUALITY_NORMAL; ``` Then the values for the various drivers. Here are the HP DeskJet driver settings: ```c if (!strcmp(driver_name, "hp_deskjet")) { /* Make and model name */ strncpy(driver_data->make_and_model, "HP DeskJet", sizeof(driver_data->make_and_model) - 1); /* Pages-per-minute for monochrome and color */ driver_data->ppm = 8; driver_data->ppm_color = 2; /* Three resolutions - 150dpi, 300dpi (default), and 600dpi */ driver_data->num_resolution = 3; driver_data->x_resolution[0] = 150; driver_data->y_resolution[0] = 150; driver_data->x_resolution[1] = 300; driver_data->y_resolution[1] = 300; driver_data->x_resolution[2] = 600; driver_data->y_resolution[2] = 600; driver_data->x_default = driver_data->y_default = 300; /* Four color spaces - black (1-bit and 8-bit), grayscale, and sRGB */ driver_data->raster_types = PAPPL_PWG_RASTER_TYPE_BLACK_1 | PAPPL_PWG_RASTER_TYPE_BLACK_8 | PAPPL_PWG_RASTER_TYPE_SGRAY_8 | PAPPL_PWG_RASTER_TYPE_SRGB_8; /* Color modes: auto (default), monochrome, and color */ driver_data->color_supported = PAPPL_COLOR_MODE_AUTO | PAPPL_COLOR_MODE_AUTO_MONOCHROME | PAPPL_COLOR_MODE_COLOR | PAPPL_COLOR_MODE_MONOCHROME; driver_data->color_default = PAPPL_COLOR_MODE_AUTO; /* Media sizes with 1/4" left/right and 1/2" top/bottom margins*/ driver_data->num_media = (int)(sizeof(pcl_hp_deskjet_media) / sizeof(pcl_hp_deskjet_media[0])); memcpy(driver_data->media, pcl_hp_deskjet_media, sizeof(pcl_hp_deskjet_media)); driver_data->left_right = 635; // 1/4" left and right driver_data->bottom_top = 1270; // 1/2" top and bottom /* 1-sided printing only */ driver_data->sides_supported = PAPPL_SIDES_ONE_SIDED; driver_data->sides_default = PAPPL_SIDES_ONE_SIDED; /* Three paper trays (MSN names) */ driver_data->num_source = 3; driver_data->source[0] = "tray-1"; driver_data->source[1] = "manual"; driver_data->source[2] = "envelope"; /* Five media types (MSN names) */ driver_data->num_type = 5; driver_data->type[0] = "stationery"; driver_data->type[1] = "bond"; driver_data->type[2] = "special"; driver_data->type[3] = "transparency"; driver_data->type[4] = "photographic-glossy"; } ``` Finally, we fill out the ready and default media for each media source (tray), putting US Letter paper in the regular trays and #10 envelopes in any envelope tray: ```c // Fill out ready and default media (default == ready media from the first source) for (i = 0; i < driver_data->num_source; i ++) { pwg_media_t *pwg; /* Media size information */ /* Use US Letter for regular trays, #10 envelope for the envelope tray */ if (!strcmp(driver_data->source[i], "envelope")) strncpy(driver_data->media_ready[i].size_name, "env_10_4.125x9.5in", sizeof(driver_data->media_ready[i].size_name) - 1); else strncpy(driver_data->media_ready[i].size_name, "na_letter_8.5x11in", sizeof(driver_data->media_ready[i].size_name) - 1); /* Set margin and size information */ if ((pwg = pwgMediaForPWG(driver_data->media_ready[i].size_name)) != NULL) { driver_data->media_ready[i].bottom_margin = driver_data->bottom_top; driver_data->media_ready[i].left_margin = driver_data->left_right; driver_data->media_ready[i].right_margin = driver_data->left_right; driver_data->media_ready[i].size_width = pwg->width; driver_data->media_ready[i].size_length = pwg->length; driver_data->media_ready[i].top_margin = driver_data->bottom_top; strncpy(driver_data->media_ready[i].source, driver_data->source[i], sizeof(driver_data->media_ready[i].source) - 1); strncpy(driver_data->media_ready[i].type, driver_data->type[0], sizeof(driver_data->media_ready[i].type) - 1); } } driver_data->media_default = driver_data->media_ready[0]; return (true); ``` The Auto-Add Callback --------------------- The PAPPL auto-add callback is called when processing the "autoadd" sub-command. It is called for each new device and is responsible for returning the name of the driver to be used for the device or `NULL` if no driver is available: ```c typedef const char *(*pappl_ml_autoadd_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data); ``` Our `pcl_autoadd` function uses the IEEE-1284 device ID string to determine whether one of the drivers will work. The [`papplDeviceParseID`](@@) function splits the string into key/value pairs that can be looked up using the `cupsGetOption` function: ```c const char *ret = NULL; // Return value int num_did; // Number of device ID key/value pairs cups_option_t *did; // Device ID key/value pairs const char *cmd, // Command set value *pcl; // PCL command set pointer // Parse the IEEE-1284 device ID to see if this is a printer we support... num_did = papplDeviceParseID(device_id, &did); ``` The two keys we care about are the "COMMAND SET" (also abbreviated as "CMD") for the list of document formats the printer supports and "MODEL"/"MDL" for the model name. We are looking for the "PCL" format and one of the common model names for HP printers: ```c // Look at the COMMAND SET (CMD) key for the list of printer languages,,, if ((cmd = cupsGetOption("COMMAND SET", num_did, did)) == NULL) cmd = cupsGetOption("CMD", num_did, did); if (cmd && (pcl = strstr(cmd, "PCL")) != NULL && (pcl[3] == ',' || !pcl[3])) { // Printer supports HP PCL, now look at the MODEL (MDL) string to see if // it is one of the HP models or a generic PCL printer... const char *mdl; // Model name string if ((mdl = cupsGetOption("MODEL", num_did, did)) == NULL) mdl = cupsGetOption("MDL", num_did, did); if (mdl && (strstr(mdl, "DeskJet") || strstr(mdl, "Photosmart"))) ret = "hp_deskjet"; // HP DeskJet/Photosmart printer else if (mdl && strstr(mdl, "LaserJet")) ret = "hp_laserjet"; // HP LaserJet printer else ret = "hp_generic"; // Some other PCL laser printer } cupsFreeOptions(num_did, did); return (ret); ``` The File Printing Callback -------------------------- The file printing callback is used when printing a "raw" (printer-ready) file from a client: ```c typedef bool (*pappl_pr_printfile_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); ``` This callback will sometimes send some printer initialization commands followed by the job file and then any cleanup commands. It may also be able to count the number of pages (impressions) in the file, although that is not a requirement. For the HP Printer Application our `pcl_print` function just copies the file from the job to the device and assumes that the file contains only one page: ```c int fd; // Job file ssize_t bytes; // Bytes read/written char buffer[65536]; // Read/write buffer papplJobSetImpressions(job, 1); fd = open(papplJobGetFilename(job), O_RDONLY); while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) { if (papplDeviceWrite(device, buffer, (size_t)bytes) < 0) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to send %d bytes to printer.", (int)bytes); close(fd); return (false); } } close(fd); papplJobSetImpressionsCompleted(job, 1); return (true); ``` The Raster Printing Callbacks ----------------------------- The PAPPL raster printing callbacks are used for printing PWG and Apple raster documents, JPEG and PNG images, and other formats that end up as raster data: ```c typedef bool (*pappl_pr_rstartjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); typedef bool (*pappl_pr_rstartpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); typedef bool (*pappl_pr_rwriteline_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned y, const unsigned char *line); typedef bool (*pappl_pr_rendpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); typedef bool (*pappl_pr_rendjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); ``` Each of the raster printing callbacks is expected to send data to the printer using the provided "device" pointer. The "job" argument provides the current job object and the "options" pointer provides the current [print job options](#pappl_pr_options_s). The "page" argument specifies the current page number staring at `0`. The `pappl_pr_rstartjob_cb_t` function is called at the beginning of a job to allow the driver to initialize the printer for the current job. The `pappl_pr_rstartpage_cb_t` function is called at the beginning of each page to allow the driver to do any per-page initialization and/or memory allocations and send any printer commands that are necessary to start a new page. The `pappl_pr_rwriteline_cb_t` function is called for each raster line on the page and is typically responsible for dithering and compressing the raster data for the printer. The `pappl_pr_rendpage_cb_t` function is called at the end of each page where the driver will typically eject the current page. The `pappl_pr_rendjob_cb_t` function is called at the end of a job to allow the driver to send any cleanup commands to the printer. The Identification Callback --------------------------- The PAPPL identification callback is used to audibly or visibly identify the printer being used: ```c typedef void (*pappl_pr_identify_cb_t)(pappl_printer_t *printer, pappl_identify_actions_t actions, const char *message); ``` The most common identification method is `PAPPL_IDENTIFY_ACTIONS_SOUND` which should have the printer make a sound. The `PAPPL_IDENTIFY_ACTIONS_DISPLAY` and `PAPPL_IDENTIFY_ACTIONS_SPEAK` methods use the "message" argument to display or speak a message. Finally, the `PAPPL_IDENTIFY_ACTIONS_FLASH` method flashes a light on the printer. The HP Printer Application does not have an identification callback since most PCL printers lack a buzzer or light that can be remotely activated. > *Note:* IPP Everywhere™ requires all printers to support identification. > A warning message is logged by PAPPL whenever a driver does not set the > `identify_cb` member of the printer driver data structure. The Status Callback ------------------- The PAPPL status callback is used to update the printer state, supply levels, and/or ready media for the printer: ```c typedef bool (*pappl_pr_status_cb_t)(pappl_printer_t *printer); ``` The callback can open a connection to the printer using the [`papplPrinterOpenDevice`](@@) function. The Self-Test Page Callback --------------------------- The PAPPL self-test page callback is used to generate a self-test page for the printer: ```c typedef const char *(*pappl_printer_testpage_cb_t)(pappl_printer_t *printer, char *buffer, size_t bufsize); ``` When the callback returns a filename (copied to the specified buffer), that file will be queued as a job for the printer. The callback can also try opening the device using the [`papplPrinterOpenDevice`](@@) function to send a printer self-test command instead - in this case the callback must return `NULL` to indicate there is no file to be printed. pappl-1.0.3/doc/pappl.html000066400000000000000000012676721403603036100154170ustar00rootroot00000000000000 PAPPL v1.1 Programmers Manual

PAPPL v1.1 Programmers Manual

Michael R Sweet

Copyright © 2020 by Michael R Sweet

Contents

Introduction

PAPPL is a simple C-based framework/library for developing CUPS Printer Applications, which are the recommended replacement for printer drivers. It is sufficiently general purpose to support any kind of printer or driver that can be used on desktops, servers, and in embedded environments.

PAPPL embeds a multi-threaded HTTP / IPP Everywhere™ server and provides callbacks for a variety of events that allows a GUI or command-line application to interact with both the local user that is running the Printer Application and any network clients that are submitting print jobs, querying printer status and capabilities, and so forth.

PAPPL provides a simple driver interface for raster graphics printing, and developers of CUPS Raster drivers will readily adapt to it. PAPPL can also be used with printers that support vector graphics printing although you'll have to develop more code to support them. Drivers provide configuration and capability information to PAPPL, and PAPPL then calls the driver to print things as needed. PAPPL automatically supports printing of JPEG, PNG, PWG Raster, Apple Raster, and "raw" files to printers connected via USB and network (AppSocket/JetDirect) connections. Other formats can be supported through "filter" callbacks you register.

PAPPL is Copyright © 2019-2020 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information.

Requirements

PAPPL requires a POSIX-compliant host operating system such as Linux®, macOS®, QNX®, or VxWorks®, a C99 compiler like Clang or GCC, a make program that supports the include directive, and the following support libraries:

  • CUPS 2.2 or later for the CUPS libraries (libcups2/libcupsimage2)

  • GNU TLS 2.8 or later (except on macOS) for TLS support

  • JPEGLIB 9 or later for JPEG image support

  • LIBPNG 1.6 or later for PNG image support

  • PAM for authentication support

  • ZLIB 1.1 or later for compression support

Run the following commands to install the prerequisites on CentOS 7, Fedora 22, and Red Hat Enterprise Linux 7:

sudo yum groupinstall 'Development Tools'
sudo yum install avahi-devel cups-devel gnutls-devel libjpeg-turbo-devel \
    libpam-devel libpng-devel libusbx-devel pam-devel zlib-devel

Run the following commands to install the prerequisites on CentOS 8, Fedora 23 or later, and Red Hat Enterprise Linux 8:

sudo dnf groupinstall 'Development Tools'
sudo dnf install avahi-devel cups-devel gnutls-devel libjpeg-turbo-devel \
    libpam-devel libpng-devel libusbx-devel pam-devel zlib-devel

Run the following commands to install the prerequisites on Debian GNU/Linux, Raspbian, and Ubuntu:

sudo apt-get install build-essential libavahi-client-dev libcups2-dev \
    libcupsimage2-dev libgnutls28-dev libjpeg-dev libpam-dev libpng-dev \
    libusb-1.0-0-dev zlib1g-dev

Finally, after installing Xcode from the AppStore run the following commands to install the prerequisites on macOS:

(install brew if necessary)
brew install libjpeg
brew install libpng
brew install libusb

or download, build, and install libjpeg, libpng, and libusb from source.

Building PAPPL

PAPPL uses the usual configure script to generate a make file:

./configure [options]
make

Use ./configure --help to see a full list of options.

There is also an Xcode project under the xcode directory that can be used on macOS:

open xcode/pappl.xcodeproj

You can test the build by running the PAPPL test program:

testsuite/testpappl

Installing PAPPL

Once you have successfully built PAPPL, install it using:

sudo make install

By default everything will be installed under /usr/local. Use the --prefix configure option to override the base installation directory. Set the DESTDIR, DSTROOT, or RPM_BUILD_ROOT environment variables to redirect the installation to a staging area, as is typically done for most software packaging systems (using one of those environment variables...)

Detecting PAPPL

PAPPL can be detected using the pkg-config command, for example:

if pkg-config --exists pappl; then
    ... 
fi

In a makefile you can add the necessary compiler and linker options with:

CFLAGS  +=      `pkg-config --cflags pappl`
LIBS    +=      `pkg-config --libs pappl`

Header Files

PAPPL provides a top-level header file that should be used:

#include <pappl/pappl.h>

This header includes all of the base object headers in PAPPL as well as the CUPS header files that provide the HTTP and IPP support functions.

API Overview

PAPPL provides five main objects:

  • The System (pappl_system_t): The main object that manages the whole printer application;

  • Clients (pappl_client_t): The objects that manage client connections;

  • Devices (pappl_device_t): The objects that manage printer connections;

  • Printers (pappl_printer_t): The objects that manage printers; and

  • Jobs (pappl_job_t): The objects that manage print jobs.

PAPPL Block Diagram::100%

The System

The system is an object of type pappl_system_t that manages client and device connections, listeners, the log, printers, and resources. It implements a subset of the IPP System Service (PWG 5100.22) with each printer implementing IPP Everywhere™ (PWG 5100.14) and some extensions to provide compatibility with the full range of mobile and desktop client devices. In addition, it provides an optional embedded web interface, raw socket printing, and USB printer gadget (Linux only).

A system object is created using the papplSystemCreate function and deleted using the papplSystemDelete function. Each system manages zero or more printers which can be accessed using either of the following functions:

The papplSystemLoadState function is often used to load system values and printers from a prior run which used the papplSystemSaveState function.

IP and domain socket listeners are added using the papplSystemAddListeners function.

The papplSystemGet functions get various system values:

Similarly, the papplSystemSet functions set various system values:

Logging

The PAPPL logging functions record messages to the configured log file. The papplLog records messages applying to the system as a whole while papplLogClient, papplLogJob, and papplLogPrinter record messages specific to a client connection, print job, or printer respectively.

The "level" argument specifies a log level from debugging (PAPPL_LOGLEVEL_DEBUG) to fatal (PAPPL_LOGLEVEL_FATAL) and is used to determine whether the message is recorded to the log.

The "message" argument specifies the message using a printf format string.

Navigation links can be added to the web interface using the papplSystemAddLink function and removed using the papplSystemRemoveLink function.

Run Loops

PAPPL provides two functions to manage its run loops:

  • papplSystemRun: Runs a system object that you have created and configured, and

  • papplMainloop: Handles processing standard command-line arguments and sub-commands for a printer application.

You can learn more about the second function in the chapter on the HP Printer Application Example.

The system will run until it receives a Shutdown-System request, a termination signal, or you call the papplSystemShutdown function. You can test whether is system is running with the papplSystmeIsRunning function and whether it has been shut down with the papplSystemIsShutdown function.

Resources

PAPPL provides several functions for adding static and dynamic resources to the embedded HTTP server:

Resources may be removed using the papplSystemRemoveResource function.

Clients

The PAPPL client functions provide access to client connections. Client connections and the life cycle of the pappl_client_t objects are managed automatically by the system object for the printer application.

The papplClientGet functions get the current values for various client- supplied request data:

Responding to Client Requests

The papplClientRespond function starts a HTTP response to a client request. The papplClientHTMLHeader and papplClientHTMLFooter functions send standard HTML headers and footers for the printer application's configured web interface while the papplClientHTMLEscape, papplClientHTMLPrintf, papplClientHTMLPuts, and papplClientHTMLStartForm functions send HTML messages or strings. Use the papplClientGetHTTP and (CUPS) httpWrite2 functions to send arbitrary data in a client response. Cookies can be included in web browser requests using the papplClientSetCookie function.

The papplClientRespondIPP function starts an IPP response. Use the various CUPS ippAdd functions to add attributes to the response message.

The papplClientRespondRedirect function sends a redirection response to the client.

HTML Forms

PAPPL provides the papplClientGetCSRFToken, papplClientGetForm, papplClientHTMLStartForm, and papplClientValidateForm functions to securely manage HTML forms.

The papplClientHTMLStartForm function starts a HTML form and inserts a hidden variable containing a CSRF token that was generated by PAPPL from a secure session key that is periodically updated. Upon receipt of a follow-up form submission request, the papplClientGetForm and papplClientValidateForm functions can be used to securely read the form data (including any file attachments) and validate the hidden CSRF token.

Authentication and Authorization

PAPPL supports both user-based authentication using PAM modules and a simple cookie-based password authentication mechanism that is used to limit administrative access through the web interface.

The papplHTMLAuthorize function authorizes access to the web interface and handles displaying an authentication form on the client's web browser. The return value indicates whether the client is authorized to access the web page.

The papplIsAuthorized function can be used to determine whether the current client is authorized to perform administrative operations and is normally only used for IPP clients. Local users are always authorized while remote users must provide credentials (typically a username and password) for access. This function will return an HTTP status code that can be provided to the httpClientSendResponse function. The value HTTP_STATUS_CONTINUE indicates that authorization is granted and the request should continue. The papplGetUsername function can be used to obtain the authenticated user identity.

Devices

The PAPPL device functions provide access to output device connections and to list available output devices. Output devices are accessed using Uniform Resource Identifier (URI) strings such as "file:///path/to/file-or-directory", "socket://11.22.33.44", and "usb://make/model?serial=number". The follow URI schemes are supported by PAPPL:

  • "dnssd": Network (AppSocket) printers discovered via DNS-SD/mDNS (Bonjour),

  • "file": Local files and directories,

  • "snmp": Network (AppSocket) printers discovered via SNMPv1,

  • "socket": Network (AppSocket) printers using a numeric IP address or hostname and optional port number, and

  • "usb": Local USB printer.

Custom device URI schemes can be registered using the papplDeviceAddScheme function.

The papplDeviceList function lists available output devices, providing each available output device to the supplied callback function. The list only contains devices whose URI scheme supports discovery, at present USB printers and network printers that advertise themselves using DNS-SD/mDNS and/or SNMPv1.

The papplDeviceOpen function opens a connection to an output device using its URI. The papplDeviceClose function closes the connection.

The papplDevicePrintf, papplDevicePuts, and papplDeviceWrite functions send data to the device, while the papplDeviceRead function reads data from the device.

The papplDeviceGet functions get various device values:

Printers

Printers are managed by the system and are represented by the pappl_printer_t type. Each printer is connected to a device and uses a driver to process document data and produce output. PAPPL supports raster printers out-of-the-box and provides filter callbacks to support other kinds of printers.

Printers are created using the papplPrinterCreate function and deleted using the papplPrinterDelete function. Each printer has zero or more jobs that are pending, processing (printing), or completed which can be access using any of the following functions:

The papplPrinterGet functions get various printer values:

Similarly, the papplPrinterSet functions set those values:

Accessing the Printer Device

When necessary, the device associated with a printer can be opened with the papplPrinterOpenDevice function and subsequently closed using the papplPrinterCloseDevice function.

Controlling Printers

Printers are stopped using the papplPrinterPause function and started using the papplPrinterResume function.

Navigation links can be added to the web interface using the papplPrinterAddLink function and removed using the papplPrinterRemoveLink function.

Jobs

Jobs are managed by the system and are represented by the pappl_job_t type. Jobs are created and deleted automatically by the system object for the printer application.

The papplJobGet functions get the current values associated with a job:

Similarly, the papplJobSet functions set the current values associated with a job:

Controlling Jobs

The papplJobCancel function cancels processing of a job while the papplJobIsCanceled function returns whether a job is in the canceled state (IPP_JSTATE_CANCELED) or is in the process of being canceled (IPP_JSTATE_PROCESSING and PAPPL_JREASON_PROCESSING_TO_STOP_POINT).

Processing Jobs

PAPPL stores print options in pappl_pr_options_t objects. The papplJobCreatePrintOptions function creates a new print option object and initializes it using the job's attributes and printer defaults. The creator of a print options object must free it using the papplJobDeletePrintOptions function.

The papplJobOpenFile function opens a file associated with the job. The file descriptor must be closed by the caller using the close function. The primary document file for a job can be retrieved using the papplJobGetFilename function, and its format using the papplJobGetFormat function.

Filters allow a printer application to support different file formats. PAPPL includes raster filters for PWG and Apple raster documents (streamed) as well as JPEG and PNG image files. Filters for other formats or non-raster printers can be added using the papplSystemAddMIMEFilter function.

The papplJobFilterImage function converts raw image data to raster data suitable for the printer, and prints using the printer driver's raster callbacks. Raster filters that output a single page can use this function to handle the details of scaling, cropping, color space conversion, and dithering for raster printers. A raster filter that needs to print more than one image must use the raster callback functions in the pappl_pr_driver_data_t structure directly.

Filters that produce non-raster data can call the papplDevice functions to directly communicate with the printer in its native language.

The HP Printer Application Example

This chapter will guide you through creating a simple PCL printer application based on the old CUPS "rastertohp" filter. The complete code can be found in the hp-printer-app project and serves as an overgrown "Hello, World!" program for PAPPL.

The Main Loop

All printer applications require some sort of a main loop for processing IPP requests, printing files, and so forth. PAPPL provides the papplMainloop convenience function that provides a standard command-line interface, and in the "hp-printer-app" project the main function just calls papplMainloop to do all of the work:

int
main(int  argc, char *argv[])
{
  return (papplMainloop(argc, argv,
                        /*version*/"1.0",
                        /*footer_html*/NULL,
                        (int)(sizeof(pcl_drivers) / sizeof(pcl_drivers[0])),
                        pcl_drivers, pcl_callback, pcl_autoadd,
                        /*subcmd_name*/NULL, /*subcmd_cb*/NULL,
                        /*system_cb*/NULL,
                        /*usage_cb*/NULL,
                        /*data*/NULL));
}

As you can see, we pass in the command-line arguments, a version number ("1.0") for our printer application, a list of drivers supported by the printer application, a callback for the driver that will configure a printer for a named driver, and a callback for automatically adding printers.

The "footer_html" argument can be provided to override the default footer text that appears at the bottom of the web interface. In this case we are passing NULL to use the default.

The drivers list is a collection of names, descriptions, IEEE-1284 device IDs, and extension pointers. Ours looks like this for HP DeskJet, HP LaserJet, and a generic PCL driver:

static pappl_pr_driver_t pcl_drivers[] =// Driver information
{   /* name */          /* description */       /* device ID */ /* extension */
  { "hp_deskjet",       "HP Deskjet",           NULL,           NULL },
  { "hp_generic",       "Generic PCL",          "CMD:PCL;",     NULL },
  { "hp_laserjet",      "HP LaserJet",          NULL,           NULL }
};

The driver callback is responsible for providing the data associated with each driver, while the auto-add callback tells PAPPL which driver to use for the printers it finds.

The "subcmd_name" and "subcmd_cb" arguments specify a custom sub-command for the printer application along with a function to call. Since this printer application does not have a custom sub-command, we pass NULL for both.

The "system_cb" argument specifies a callback for creating the system object. We pass NULL because we want the default system object for this printer application, which supports multiple printers and a web interface.

The "usage_cb" argument specifies a callback from displaying the program usage message. Passing NULL yields the default usage message.

The "data" argument specifies a pointer that will be passed to any of the callback functions. We pass NULL since we do not have any extra contextual information to provide to the callbacks.

The papplMainloop function runs until all processing for the current sub-command is complete, returning the exit status for the program.

The Driver Callback

The PAPPL driver callback is called when the system is creating a new printer object. It receives pointers to the system, driver name, device URI, a driver data structure, an IPP attributes pointer, and the callback data, and it returns a boolean indicating whether the driver callback was successful:

typedef bool (*pappl_pr_driver_cb_t)(pappl_system_t *system,
    const char *driver_name, const char *device_uri,
    pappl_pr_driver_data_t *driver_data, ipp_t **driver_attrs, void *data);

A driver callback can communicate with the printer via its device URI as needed to configure the driver, however our printer application doesn't need to do that.

The first thing our pcl_callback function does is to set the printer callbacks in the driver data structure:

driver_data->printfile_cb    = pcl_print;
driver_data->rendjob_cb      = pcl_rendjob;
driver_data->rendpage_cb     = pcl_rendpage;
driver_data->rstartjob_cb    = pcl_rstartjob;
driver_data->rstartpage_cb   = pcl_rstartpage;
driver_data->rwriteline_cb   = pcl_rwriteline;
driver_data->status_cb       = pcl_status;

The pcl_print function prints a raw PCL file while the pcl_r functions print raster graphics. The pcl_status updates the printer status.

Next is the printer's native print format as a MIME media type, in this case HP PCL:

driver_data->format          = "application/vnd.hp-pcl";

The default orientation and print quality follow:

driver_data->orient_default  = IPP_ORIENT_NONE;
driver_data->quality_default = IPP_QUALITY_NORMAL;

Then the values for the various drivers. Here are the HP DeskJet driver settings:

if (!strcmp(driver_name, "hp_deskjet"))
{
  /* Make and model name */
  strncpy(driver_data->make_and_model, "HP DeskJet", sizeof(driver_data->make_and_model) - 1);

  /* Pages-per-minute for monochrome and color */
  driver_data->ppm       = 8;
  driver_data->ppm_color = 2;

  /* Three resolutions - 150dpi, 300dpi (default), and 600dpi */
  driver_data->num_resolution  = 3;
  driver_data->x_resolution[0] = 150;
  driver_data->y_resolution[0] = 150;
  driver_data->x_resolution[1] = 300;
  driver_data->y_resolution[1] = 300;
  driver_data->x_resolution[2] = 600;
  driver_data->y_resolution[2] = 600;
  driver_data->x_default = driver_data->y_default = 300;

  /* Four color spaces - black (1-bit and 8-bit), grayscale, and sRGB */
  driver_data->raster_types = PAPPL_PWG_RASTER_TYPE_BLACK_1 | PAPPL_PWG_RASTER_TYPE_BLACK_8 | PAPPL_PWG_RASTER_TYPE_SGRAY_8 | PAPPL_PWG_RASTER_TYPE_SRGB_8;

  /* Color modes: auto (default), monochrome, and color */
  driver_data->color_supported = PAPPL_COLOR_MODE_AUTO | PAPPL_COLOR_MODE_AUTO_MONOCHROME | PAPPL_COLOR_MODE_COLOR | PAPPL_COLOR_MODE_MONOCHROME;
  driver_data->color_default   = PAPPL_COLOR_MODE_AUTO;

  /* Media sizes with 1/4" left/right and 1/2" top/bottom margins*/
  driver_data->num_media = (int)(sizeof(pcl_hp_deskjet_media) / sizeof(pcl_hp_deskjet_media[0]));
  memcpy(driver_data->media, pcl_hp_deskjet_media, sizeof(pcl_hp_deskjet_media));

  driver_data->left_right = 635;       // 1/4" left and right
  driver_data->bottom_top = 1270;      // 1/2" top and bottom

  /* 1-sided printing only */
  driver_data->sides_supported = PAPPL_SIDES_ONE_SIDED;
  driver_data->sides_default   = PAPPL_SIDES_ONE_SIDED;

  /* Three paper trays (MSN names) */
  driver_data->num_source = 3;
  driver_data->source[0]  = "tray-1";
  driver_data->source[1]  = "manual";
  driver_data->source[2]  = "envelope";

  /* Five media types (MSN names) */
  driver_data->num_type = 5;
  driver_data->type[0] = "stationery";
  driver_data->type[1] = "bond";
  driver_data->type[2] = "special";
  driver_data->type[3] = "transparency";
  driver_data->type[4] = "photographic-glossy";
}

Finally, we fill out the ready and default media for each media source (tray), putting US Letter paper in the regular trays and #10 envelopes in any envelope tray:

// Fill out ready and default media (default == ready media from the first source)
for (i = 0; i < driver_data->num_source; i ++)
{
  pwg_media_t *pwg;                   /* Media size information */

  /* Use US Letter for regular trays, #10 envelope for the envelope tray */
  if (!strcmp(driver_data->source[i], "envelope"))
    strncpy(driver_data->media_ready[i].size_name, "env_10_4.125x9.5in", sizeof(driver_data->media_ready[i].size_name) - 1);
  else
    strncpy(driver_data->media_ready[i].size_name, "na_letter_8.5x11in", sizeof(driver_data->media_ready[i].size_name) - 1);

  /* Set margin and size information */
  if ((pwg = pwgMediaForPWG(driver_data->media_ready[i].size_name)) != NULL)
  {
    driver_data->media_ready[i].bottom_margin = driver_data->bottom_top;
    driver_data->media_ready[i].left_margin   = driver_data->left_right;
    driver_data->media_ready[i].right_margin  = driver_data->left_right;
    driver_data->media_ready[i].size_width    = pwg->width;
    driver_data->media_ready[i].size_length   = pwg->length;
    driver_data->media_ready[i].top_margin    = driver_data->bottom_top;
    strncpy(driver_data->media_ready[i].source, driver_data->source[i], sizeof(driver_data->media_ready[i].source) - 1);
    strncpy(driver_data->media_ready[i].type, driver_data->type[0],  sizeof(driver_data->media_ready[i].type) - 1);
  }
}

driver_data->media_default = driver_data->media_ready[0];

return (true);

The Auto-Add Callback

The PAPPL auto-add callback is called when processing the "autoadd" sub-command. It is called for each new device and is responsible for returning the name of the driver to be used for the device or NULL if no driver is available:

typedef const char *(*pappl_ml_autoadd_cb_t)(const char *device_info,
    const char *device_uri, const char *device_id, void *data);

Our pcl_autoadd function uses the IEEE-1284 device ID string to determine whether one of the drivers will work. The papplDeviceParseID function splits the string into key/value pairs that can be looked up using the cupsGetOption function:

const char      *ret = NULL;            // Return value
int             num_did;                // Number of device ID key/value pairs
cups_option_t   *did;                   // Device ID key/value pairs
const char      *cmd,                   // Command set value
                *pcl;                   // PCL command set pointer


// Parse the IEEE-1284 device ID to see if this is a printer we support...
num_did = papplDeviceParseID(device_id, &did);

The two keys we care about are the "COMMAND SET" (also abbreviated as "CMD") for the list of document formats the printer supports and "MODEL"/"MDL" for the model name. We are looking for the "PCL" format and one of the common model names for HP printers:

// Look at the COMMAND SET (CMD) key for the list of printer languages,,,
if ((cmd = cupsGetOption("COMMAND SET", num_did, did)) == NULL)
  cmd = cupsGetOption("CMD", num_did, did);

if (cmd && (pcl = strstr(cmd, "PCL")) != NULL && (pcl[3] == ',' || !pcl[3]))
{
  // Printer supports HP PCL, now look at the MODEL (MDL) string to see if
  // it is one of the HP models or a generic PCL printer...
  const char *mdl;                      // Model name string

  if ((mdl = cupsGetOption("MODEL", num_did, did)) == NULL)
    mdl = cupsGetOption("MDL", num_did, did);

  if (mdl && (strstr(mdl, "DeskJet") || strstr(mdl, "Photosmart")))
    ret = "hp_deskjet";                 // HP DeskJet/Photosmart printer
  else if (mdl && strstr(mdl, "LaserJet"))
    ret = "hp_laserjet";                // HP LaserJet printer
  else
    ret = "hp_generic";                 // Some other PCL laser printer
}

cupsFreeOptions(num_did, did);

return (ret);

The File Printing Callback

The file printing callback is used when printing a "raw" (printer-ready) file from a client:

typedef bool (*pappl_pr_printfile_cb_t)(pappl_job_t *job,
    pappl_pr_options_t *options, pappl_device_t *device);

This callback will sometimes send some printer initialization commands followed by the job file and then any cleanup commands. It may also be able to count the number of pages (impressions) in the file, although that is not a requirement. For the HP Printer Application our pcl_print function just copies the file from the job to the device and assumes that the file contains only one page:

int     fd;                     // Job file
ssize_t bytes;                  // Bytes read/written
char    buffer[65536];          // Read/write buffer


papplJobSetImpressions(job, 1);

fd = open(papplJobGetFilename(job), O_RDONLY);

while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
{
  if (papplDeviceWrite(device, buffer, (size_t)bytes) < 0)
  {
    papplLogJob(job, PAPPL_LOGLEVEL_ERROR,
                "Unable to send %d bytes to printer.", (int)bytes);
    close(fd);
    return (false);
  }
}

close(fd);

papplJobSetImpressionsCompleted(job, 1);

return (true);

The Raster Printing Callbacks

The PAPPL raster printing callbacks are used for printing PWG and Apple raster documents, JPEG and PNG images, and other formats that end up as raster data:

typedef bool (*pappl_pr_rstartjob_cb_t)(pappl_job_t *job,
    pappl_pr_options_t *options, pappl_device_t *device);

typedef bool (*pappl_pr_rstartpage_cb_t)(pappl_job_t *job,
    pappl_pr_options_t *options, pappl_device_t *device, unsigned page);

typedef bool (*pappl_pr_rwriteline_cb_t)(pappl_job_t *job,
    pappl_pr_options_t *options, pappl_device_t *device, unsigned y,
    const unsigned char *line);

typedef bool (*pappl_pr_rendpage_cb_t)(pappl_job_t *job,
    pappl_pr_options_t *options, pappl_device_t *device, unsigned page);

typedef bool (*pappl_pr_rendjob_cb_t)(pappl_job_t *job,
    pappl_pr_options_t *options, pappl_device_t *device);

Each of the raster printing callbacks is expected to send data to the printer using the provided "device" pointer. The "job" argument provides the current job object and the "options" pointer provides the current print job options. The "page" argument specifies the current page number staring at 0.

The pappl_pr_rstartjob_cb_t function is called at the beginning of a job to allow the driver to initialize the printer for the current job.

The pappl_pr_rstartpage_cb_t function is called at the beginning of each page to allow the driver to do any per-page initialization and/or memory allocations and send any printer commands that are necessary to start a new page.

The pappl_pr_rwriteline_cb_t function is called for each raster line on the page and is typically responsible for dithering and compressing the raster data for the printer.

The pappl_pr_rendpage_cb_t function is called at the end of each page where the driver will typically eject the current page.

The pappl_pr_rendjob_cb_t function is called at the end of a job to allow the driver to send any cleanup commands to the printer.

The Identification Callback

The PAPPL identification callback is used to audibly or visibly identify the printer being used:

typedef void (*pappl_pr_identify_cb_t)(pappl_printer_t *printer,
    pappl_identify_actions_t actions, const char *message);

The most common identification method is PAPPL_IDENTIFY_ACTIONS_SOUND which should have the printer make a sound. The PAPPL_IDENTIFY_ACTIONS_DISPLAY and PAPPL_IDENTIFY_ACTIONS_SPEAK methods use the "message" argument to display or speak a message. Finally, the PAPPL_IDENTIFY_ACTIONS_FLASH method flashes a light on the printer.

The HP Printer Application does not have an identification callback since most PCL printers lack a buzzer or light that can be remotely activated.

Note: IPP Everywhere™ requires all printers to support identification. A warning message is logged by PAPPL whenever a driver does not set the identify_cb member of the printer driver data structure.

The Status Callback

The PAPPL status callback is used to update the printer state, supply levels, and/or ready media for the printer:

typedef bool (*pappl_pr_status_cb_t)(pappl_printer_t *printer);

The callback can open a connection to the printer using the papplPrinterOpenDevice function.

The Self-Test Page Callback

The PAPPL self-test page callback is used to generate a self-test page for the printer:

typedef const char *(*pappl_printer_testpage_cb_t)(pappl_printer_t *printer,
    char *buffer, size_t bufsize);

When the callback returns a filename (copied to the specified buffer), that file will be queued as a job for the printer. The callback can also try opening the device using the papplPrinterOpenDevice function to send a printer self-test command instead - in this case the callback must return NULL to indicate there is no file to be printed.

Functions

papplClientGetCSRFToken

Get a unique Cross-Site Request Forgery token string.

char *papplClientGetCSRFToken(pappl_client_t *client, char *buffer, size_t bufsize);

Parameters

client Client
buffer String buffer
bufsize Size of string buffer

Return Value

Token string

Discussion

This function generates and returns a unique Cross-Site Request Forgery token string to be used as the value of a hidden variable in all HTML forms sent in the response and then compared when validating the form data in the subsequent request.

The value is based on the current system session key and client address in order to make replay attacks infeasible.

Note: The papplClientHTMLStartForm function automatically adds the hidden CSRF variable, and the papplClientIsValidForm function validates the value.

papplClientGetCookie

Get a cookie from the client.

char *papplClientGetCookie(pappl_client_t *client, const char *name, char *buffer, size_t bufsize);

Parameters

client Client
name Name of cookie
buffer Value buffer
bufsize Size of value buffer

Return Value

Cookie value or NULL if not set

Discussion

This function gets a HTTP "cookie" value from the client request. NULL is returned if no cookie has been set by a prior request, or if the user has disabled or removed the cookie.

Use the papplClientSetCookie function to set a cookie in a response to a request.

Note: Cookies set with papplClientSetCookie will not be available to this function until the following request.

papplClientGetForm

Get form data from the web client.

int papplClientGetForm(pappl_client_t *client, cups_option_t **form);

Parameters

client Client
form Form variables

Return Value

Number of form variables read

Discussion

For HTTP GET requests, the form data is collected from the request URI. For HTTP POST requests, the form data is read from the client.

The returned form values must be freed using the cupsFreeOptions function.

Note: Because the form data is read from the client connection, this function can only be called once per request.

papplClientGetHTTP

Get the HTTP connection associated with a client object.

http_t *papplClientGetHTTP(pappl_client_t *client);

Parameters

client Client

Return Value

HTTP connection

Discussion

This function returns the HTTP connection associated with the client and is used when sending response data directly to the client using the CUPS httpXxx functions.

papplClientGetHostName

Get the hostname from the client-supplied Host: field.

const char *papplClientGetHostName(pappl_client_t *client);

Parameters

client Client

Return Value

Hostname or NULL for none

Discussion

This function returns the hostname that was used in the request and should be used in any URLs or URIs that you generate.

papplClientGetHostPort

Get the port from the client-supplied Host: field.

int papplClientGetHostPort(pappl_client_t *client);

Parameters

client Client

Return Value

Port number or 0 for none

Discussion

This function returns the port number that was used in the request and should be used in any URLs or URIs that you generate.

papplClientGetJob

Get the target job for an IPP request.

pappl_job_t *papplClientGetJob(pappl_client_t *client);

Parameters

client Client

Return Value

Target job or NULL if none

Discussion

This function returns the job associated with the current IPP request. NULL is returned if the request does not target a job.

papplClientGetMethod

Get the HTTP request method.

http_state_t papplClientGetMethod(pappl_client_t *client);

Parameters

client Client

Return Value

HTTP method

Discussion

This function returns the HTTP request method that was used, for example HTTP_STATE_GET for a GET request or HTTP_STATE_POST for a POST request.

papplClientGetOperation

Get the IPP operation code.

ipp_op_t papplClientGetOperation(pappl_client_t *client);

Parameters

client Client

Return Value

IPP operation code

Discussion

This function returns the IPP operation code associated with the current IPP request.

papplClientGetOptions

Get the options from the request URI.

const char *papplClientGetOptions(pappl_client_t *client);

Parameters

client Client

Return Value

Options or NULL if none

Discussion

This function returns any options that were passed in the HTTP request URI. The options are the characters after the "?" character, for example a request URI of "/mypage?name=value" will have an options string of "name=value".

NULL is returned if the request URI did not contain any options.

Note: HTTP GET form variables are normally accessed using the papplClientGetForm function. This function should only be used when getting non-form data.

papplClientGetPrinter

Get the target printer for an IPP request.

pappl_printer_t *papplClientGetPrinter(pappl_client_t *client);

Parameters

client Client

Return Value

Target printer or NULL if none

Discussion

This function returns the printer associated with the current IPP request. NULL is returned if the request does not target a printer.

papplClientGetRequest

Get the IPP request message.

ipp_t *papplClientGetRequest(pappl_client_t *client);

Parameters

client Client

Return Value

IPP request message

Discussion

This function returns the attributes in the current IPP request, for use with the CUPS ippFindAttribute, ippFindNextAttribute, ippFirstAttribute, and ippNextAttribute functions.

papplClientGetResponse

Get the IPP response message.

ipp_t *papplClientGetResponse(pappl_client_t *client);

Parameters

client Client

Return Value

IPP response message

Discussion

This function returns the attributes in the current IPP response, for use with the CUPS ippAddXxx and ippSetXxx functions. Use the papplClientRespondIPP function to set the status code and message, if any.

papplClientGetSystem

Get the containing system for the client.

pappl_system_t *papplClientGetSystem(pappl_client_t *client);

Parameters

client Client

Return Value

System

Discussion

This function returns the system object that contains the client.

papplClientGetURI

Get the HTTP request URI.

const char *papplClientGetURI(pappl_client_t *client);

Parameters

client Client

Return Value

Request URI

Discussion

This function returns the URI that was sent in the current HTTP request.

Note: Any options in the URI are removed and can be accessed separately using the papplClientGetOptions function.

papplClientGetUsername

Get the authenticated username, if any.

const char *papplClientGetUsername(pappl_client_t *client);

Parameters

client Client

Return Value

Authenticated username or NULL if none

Discussion

This function returns the current authenticated username, if any.

papplClientHTMLAuthorize

Handle authorization for the web interface.

bool papplClientHTMLAuthorize(pappl_client_t *client);

Parameters

client Client

Return Value

true if authorized, false otherwise

Discussion

The web interface supports both authentication against user accounts and authentication using a single administrative access password. This function handles the details of authentication for the web interface based on the system authentication service configuration (the "auth_service" argument to papplSystemCreate).

Note: IPP operation callbacks needing to perform authorization should use the papplClientIsAuthorized function instead.

papplClientHTMLEscape

Send a string to a web browser client.

void papplClientHTMLEscape(pappl_client_t *client, const char *s, size_t slen);

Parameters

client Client
s String to write
slen Number of characters to write (0 for nul-terminated)

Discussion

This function sends the specified string to the web browser client and escapes special characters as HTML entities as needed, for example "&" is sent as &amp;.

papplClientHTMLFooter

Show the web interface footer.

void papplClientHTMLFooter(pappl_client_t *client);

Parameters

client Client

Discussion

This function sends the standard web interface footer followed by a trailing 0-length chunk to finish the current HTTP response. Use the papplSystemSetFooterHTML function to add any custom HTML needed in the footer.

papplClientHTMLHeader

Show the web interface header and title.

void papplClientHTMLHeader(pappl_client_t *client, const char *title, int refresh);

Parameters

client Client
title Title
refresh Refresh time in seconds (0 for no refresh)

Discussion

This function sends the standard web interface header and title. If the "refresh" argument is greater than zero, the page will automatically reload after that many seconds.

Use the papplSystemAddLink function to add system-wide navigation links to the header. Similarly, use papplPrinterAddLink to add printer-specific links, which will appear in the web interface printer if the system is not configured to support multiple printers (the PAPPL_SOPTIONS_MULTI_QUEUE option to papplSystemCreate).

papplClientHTMLPrinterFooter

Show the web interface footer for printers.

void papplClientHTMLPrinterFooter(pappl_client_t *client);

Parameters

client Client

Discussion

This function sends the standard web interface footer for a printer followed by a trailing 0-length chunk to finish the current HTTP response. Use the papplSystemSetFooterHTML function to add any custom HTML needed in the footer.

papplClientHTMLPrinterHeader

Show the web interface header and title for printers.

void papplClientHTMLPrinterHeader(pappl_client_t *client, pappl_printer_t *printer, const char *title, int refresh, const char *label, const char *path_or_url);

Parameters

client Client
printer Printer
title Title
refresh Refresh time in seconds or 0 for none
label Button label or NULL for none
path_or_url Button path or NULL for none

Discussion

This function sends the standard web interface header and title for a printer. If the "refresh" argument is greater than zero, the page will automatically reload after that many seconds.

If "label" and "path_or_url" are non-NULL strings, an additional navigation link is included with the title header - this is typically used for an action button ("Change").

Use the papplSystemAddLink function to add system-wide navigation links to the header. Similarly, use papplPrinterAddLink to add printer-specific links, which will appear in the web interface printer if the system is not configured to support multiple printers (the PAPPL_SOPTIONS_MULTI_QUEUE option to papplSystemCreate).

papplClientHTMLPrintf

Send formatted text to the web browser client, escaping as needed.

void papplClientHTMLPrintf(pappl_client_t *client, const char *format, ...);

Parameters

client Client
format Printf-style format string
... Additional arguments as needed

Discussion

This function sends formatted text to the web browser client using printf-style formatting codes. The format string itself is not escaped to allow for embedded HTML, however strings inserted using the '%c' or %s codes are escaped properly for HTML - "&" is sent as &amp;, etc.

papplClientHTMLPuts

Send a HTML string to the web browser client.

void papplClientHTMLPuts(pappl_client_t *client, const char *s);

Parameters

client Client
s String

Discussion

This function sends a HTML string to the client without performing any escaping of special characters.

papplClientHTMLStartForm

Start a HTML form.

void papplClientHTMLStartForm(pappl_client_t *client, const char *action, bool multipart);

Parameters

client Client
action Form action URL
multipart true if the form allows file uploads, false otherwise

Discussion

This function starts a HTML form with the specified "action" path and includes the CSRF token as a hidden variable. If the "multipart" argument is true, the form is annotated to support file attachments up to 2MiB in size.

papplClientIsAuthorized

Determine whether a client is authorized for administrative requests.

http_status_t papplClientIsAuthorized(pappl_client_t *client);

Parameters

client Client

Return Value

HTTP status

Discussion

This function determines whether a client is authorized to submit an administrative request.

The return value is HTTP_STATUS_CONTINUE if access is authorized, HTTP_STATUS_FORBIDDEN if access is not allowed, HTTP_STATUS_UNAUTHORIZED if authorization is required, or HTTP_STATUS_UPGRADE_REQUIRED if the connection needs to be encrypted. All of these values can be passed to the papplClientRespond function.

papplClientIsValidForm

Validate HTML form variables.

bool papplClientIsValidForm(pappl_client_t *client, int num_form, cups_option_t *form);

Parameters

client Client
num_form Number of form variables
form Form variables

Return Value

true if the CSRF token is valid, false otherwise

Discussion

This function validates the contents of a HTML form using the CSRF token included as a hidden variable. When sending a HTML form you should use the papplClientStartForm function to start the HTML form and insert the CSRF token for later validation.

Note: Callers are expected to validate all other form variables.

papplClientRespond

Send a regular HTTP response.

bool papplClientRespond(pappl_client_t *client, http_status_t code, const char *content_encoding, const char *type, time_t last_modified, size_t length);

Parameters

client Client
code HTTP status of response
content_encoding Content-Encoding of response
type MIME media type of response
last_modified Last-Modified date/time or 0 for none
length Length of response or 0 for variable-length

Return Value

true on success, false on failure

Discussion

This function sends all of the required HTTP fields and includes standard messages for errors. The following values for "code" are explicitly supported:

  • HTTP_STATUS_OK: The request is successful.
  • HTTP_STATUS_BAD_REQUEST: The client submitted a bad request.
  • HTTP_STATUS_CONTINUE: An authentication challenge is not needed.
  • HTTP_STATUS_FORBIDDEN: Authenticated but not allowed.
  • HTTP_STATUS_METHOD_NOT_ALLOWED: The HTTP method is not supported for the given URI.
  • HTTP_STATUS_UNAUTHORIZED: Not authenticated.
  • HTTP_STATUS_UPGRADE_REQUIRED: Redirects the client to a secure page.

Use the papplClientRespondRedirect when you need to redirect the client to another page.

papplClientRespondIPP

Send an IPP response.

ipp_t *papplClientRespondIPP(pappl_client_t *client, ipp_status_t status, const char *message, ...);

Parameters

client Client
status status-code
message printf-style status-message
... Additional args as needed

Return Value

IPP response message

Discussion

This function sets the return status for an IPP request and returns the current IPP response message. The "status" and "message" arguments replace any existing status-code and "status-message" attribute value that may be already present in the response.

Note: You should call this function prior to adding any response attributes.

papplClientRespondIPPUnsupported

Respond with an unsupported IPP attribute.

void papplClientRespondIPPUnsupported(pappl_client_t *client, ipp_attribute_t *attr);

Parameters

client Client
attr Atribute

Discussion

This function returns a 'client-error-attributes-or-values-not-supported' status code and adds the specified attribute to the unsupported attributes group in the response.

papplClientRespondRedirect

Respond with a redirect to another page.

bool papplClientRespondRedirect(pappl_client_t *client, http_status_t code, const char *path);

Parameters

client Client
code HTTP_STATUS_MOVED_PERMANENTLY or HTTP_STATUS_FOUND
path Redirection path/URL

Return Value

true on success, false otherwise

Discussion

This function sends a HTTP response that redirects the client to another page or URL. The most common "code" value to return is HTTP_STATUS_FOUND.

papplClientSetCookie

Set a cookie for the web browser client.

void papplClientSetCookie(pappl_client_t *client, const char *name, const char *value, int expires);

Parameters

client Client
name Cookie name
value Cookie value
expires Expiration in seconds from now, 0 for a session cookie

Discussion

This function sets the value of a cookie for the client by updating the Set-Cookie header in the HTTP response that will be sent. The "name" and "value" strings must contain only valid characters for a cookie and its value as documented in RFC 6265, which basically means letters, numbers, "@", "-", ".", and "_".

The "expires" argument specifies how long the cookie will remain active in seconds, for example 3600 seconds is one hour and 86400 seconds is one day. If the value is zero or less, a "session" cookie is created instead which will expire as soon as the web browser is closed.

papplDeviceAddScheme

Add a device URI scheme.

void papplDeviceAddScheme(const char *scheme, pappl_devtype_t dtype, pappl_devlist_cb_t list_cb, pappl_devopen_cb_t open_cb, pappl_devclose_cb_t close_cb, pappl_devread_cb_t read_cb, pappl_devwrite_cb_t write_cb, pappl_devstatus_cb_t status_cb, pappl_devid_cb_t id_cb);

Parameters

scheme URI scheme
dtype Device type (PAPPL_DEVTYPE_CUSTOM_LOCAL or PAPPL_DEVTYPE_CUSTOM_NETWORK)
list_cb List devices callback, if any
open_cb Open callback
close_cb Close callback
read_cb Read callback
write_cb Write callback
status_cb Status callback, if any
id_cb IEEE-1284 device ID callback, if any

Discussion

This function registers a device URI scheme with PAPPL, so that devices using the named scheme can receive print data, report status information, and so forth. PAPPL includes support for the following URI schemes:

  • dnssd: Network printers discovered using DNS-SD.
  • file: Character device files, plain files, and directories.
  • snmp: Network printers discovered using SNMPv1.
  • socket: Network printers using a hostname or numeric IP address.
  • usb: Class 1 (unidirectional) or 2 (bidirectional) USB printers.

The "scheme" parameter specifies the URI scheme and must consist of lowercase letters, digits, "-", "_", and/or ".", for example "x-foo" or "com.example.bar".

The "dtype" parameter specifies the device type and should be PAPPL_DTYPE_CUSTOM_LOCAL for locally connected printers and PAPPL_DTYPE_CUSTOM_NETWORK for network printers.

Each of the callbacks corresponds to one of the papplDevice functions:

  • "list_cb": Implements discovery of devices (optional)
  • "open_cb": Opens communication with a device and allocates any device- specific data as needed
  • "close_cb": Closes communication with a device and frees any device- specific data as needed
  • "read_cb": Reads data from a device
  • "write_cb": Write data to a device
  • "status_cb": Gets basic printer state information from a device (optional)
  • "id_cb": Gets the current IEEE-1284 device ID from a device (optional)

The "open_cb" callback typically calls papplDeviceSetData to store a pointer to contextual information for the connection while the "close_cb", "id_cb", "read_cb", "write_cb", and "status_cb" callbacks typically call papplDeviceGetData to retrieve it.

papplDeviceClose

Close a device connection.

void papplDeviceClose(pappl_device_t *device);

Parameters

device Device to close

Discussion

This function flushes any pending write data and closes the connection to a device.

papplDeviceError

Report an error on a device.

void papplDeviceError(pappl_device_t *device, const char *message, ...);

Parameters

device Device
message Printf-style error message
... Additional arguments as needed

Discussion

This function reports an error on a device using the client-supplied callback function. It is normally called from any custom device URI scheme callbacks you implement.

papplDeviceFlush

Flush any buffered data to the device.

void papplDeviceFlush(pappl_device_t *device);

Parameters

device Device

Discussion

This function flushes any pending write data sent using the papplDevicePrintf, papplDevicePuts, or papplDeviceWrite functions to the device.

papplDeviceGetData

Get device-specific data.

void *papplDeviceGetData(pappl_device_t *device);

Parameters

device Device

Return Value

Device data pointer

Discussion

This function returns any device-specific data that has been set by the device open callback. It is normally only called from any custom device URI scheme callbacks you implement.

papplDeviceGetID

Get the IEEE-1284 device ID.

char *papplDeviceGetID(pappl_device_t *device, char *buffer, size_t bufsize);

Parameters

device Device
buffer Buffer for IEEE-1284 device ID
bufsize Size of buffer

Return Value

IEEE-1284 device ID or NULL on failure

Discussion

This function queries the IEEE-1284 device ID from the device and copies it to the provided buffer. The buffer must be at least 64 bytes and should be at least 1024 bytes in length.

Note: This function can block for up to several seconds depending on the type of connection.

papplDeviceGetMetrics

Get the device metrics.

pappl_devmetrics_t *papplDeviceGetMetrics(pappl_device_t *device, pappl_devmetrics_t *metrics);

Parameters

device Device
metrics Buffer for metrics data

Return Value

Metrics data

Discussion

This function returns a copy of the device metrics data, which includes the number, length (in bytes), and duration (in milliseconds) of read, status, and write requests for the current session. This information is normally used for performance measurement and optimization during development of a printer application. It can also be useful diagnostic information.

papplDeviceGetStatus

Get the printer status bits.

pappl_preason_t papplDeviceGetStatus(pappl_device_t *device);

Parameters

device Device

Return Value

IPP "printer-state-reasons" values

Discussion

This function returns the current printer status bits, as applicable to the current device.

The status bits for USB devices come from the original Centronics parallel printer "standard" which was later formally standardized in IEEE 1284-1984 and the USB Device Class Definition for Printing Devices. Some vendor extensions are also supported.

The status bits for network devices come from the hrPrinterDetectedErrorState property that is defined in the SNMP Printer MIB v2 (RFC 3805).

This function returns a pappl_preason_t bitfield which can be passed to the papplPrinterSetReasons function. Use the PAPPL_PREASON_DEVICE_STATUS value as the value of the "remove" argument.

Note: This function can block for several seconds while getting the status information.

papplDeviceIsSupported

Determine whether a given URI is supported.

bool papplDeviceIsSupported(const char *uri);

Parameters

uri URI

Return Value

true if supported, false otherwise

Discussion

This function determines whether a given URI or URI scheme is supported as a device.

papplDeviceList

List available devices.

bool papplDeviceList(pappl_devtype_t types, pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data);

Parameters

types Device types
cb Callback function
data User data for callback
err_cb Error callback or NULL for default
err_data Data for error callback

Return Value

true if the callback returned true, false otherwise

Discussion

This function lists the available devices, calling the "cb" function once per device that is discovered/listed. The callback function receives the device URI, IEEE-1284 device ID (if any), and "data" pointer, and returns true to stop listing devices and false to continue.

The "types" argument determines which devices are listed, for example PAPPL_DEVTYPE_ALL will list all types of devices while PAPPL_DEVTYPE_USB only lists USB printers.

Any errors are reported using the supplied "err_cb" function. If you specify NULL for this argument, errors are sent to stderr.

Note: This function will block (not return) until each of the device URI schemes has reported all of the devices or the supplied callback function returns true.

papplDeviceOpen

Open a connection to a device.

pappl_device_t *papplDeviceOpen(const char *device_uri, const char *name, pappl_deverror_cb_t err_cb, void *err_data);

Parameters

device_uri Device URI
name Job name
err_cb Error callback or NULL for default
err_data Data for error callback

Return Value

Device connection or NULL on error

Discussion

This function opens a connection to the specified device URI. The "name" argument provides textual context for the connection and is usually the name (title) of the print job.

Any errors are reported using the supplied "err_cb" function. If you specify NULL for this argument, errors are sent to stderr.

papplDeviceParseID

Parse an IEEE-1284 device ID string.

int papplDeviceParseID(const char *device_id, cups_option_t **pairs);

Parameters

device_id IEEE-1284 device ID string
pairs Key/value pairs

Return Value

Number of key/value pairs

Discussion

This function parses an IEEE-1284 device ID string and returns an array of key/value pairs as a cups_option_t array. The returned array must be freed using the cupsFreeOptions function.

papplDevicePrintf

Write a formatted string.

ssize_t papplDevicePrintf(pappl_device_t *device, const char *format, ...);

Parameters

device Device
format Printf-style format string
... Additional args as needed

Return Value

Number of characters or -1 on error

Discussion

This function buffers a formatted string that will be sent to the device. The "format" argument accepts all printf format specifiers and behaves identically to that function.

Call the papplDeviceFlush function to ensure that the formatted string is immediately sent to the device.

papplDevicePuts

Write a literal string.

ssize_t papplDevicePuts(pappl_device_t *device, const char *s);

Parameters

device Device
s Literal string

Return Value

Number of characters or -1 on error

Discussion

This function buffers a literal string that will be sent to the device. Call the papplDeviceFlush function to ensure that the literal string is immediately sent to the device.

papplDeviceRead

Read from a device.

ssize_t papplDeviceRead(pappl_device_t *device, void *buffer, size_t bytes);

Parameters

device Device
buffer Read buffer
bytes Max bytes to read

Return Value

Number of bytes read or -1 on error

Discussion

This function reads data from the device. Depending on the device, this function may block indefinitely.

papplDeviceSetData

Set device-specific data.

void papplDeviceSetData(pappl_device_t *device, void *data);

Parameters

device Device
data Device data pointer

Discussion

This function sets any device-specific data needed to communicate with the device. It is normally only called from the open callback that was registered for the device URI scheme.

papplDeviceWrite

Write to a device.

ssize_t papplDeviceWrite(pappl_device_t *device, const void *buffer, size_t bytes);

Parameters

device Device
buffer Write buffer
bytes Number of bytes to write

Return Value

Number of bytes written or -1 on error

Discussion

This function buffers data that will be sent to the device. Call the papplDeviceFlush function to ensure that the data is immediately sent to the device.

papplJobCancel

Cancel a job.

void papplJobCancel(pappl_job_t *job);

Parameters

job Job

Discussion

This function cancels the specified job. If the job is currently being printed, it will be stopped at a convenient time (usually the end of a page) so that the printer will be left in a known state.

papplJobCreatePrintOptions

Create the printer options for a job.

pappl_pr_options_t *papplJobCreatePrintOptions(pappl_job_t *job, unsigned num_pages, bool color);

Parameters

job Job
num_pages Number of pages (0 for unknown)
color Is the document in color?

Return Value

Job options data or NULL on error

Discussion

This function allocates a printer options structure and computes the print options for a job based upon the Job Template attributes submitted in the print request and the default values set in the printer driver data.

The "num_pages" and "color" arguments specify the number of pages and whether the document contains non-grayscale colors - this information typically comes from parsing the job file.

papplJobDeletePrintOptions

Delete a job options structure.

void papplJobDeletePrintOptions(pappl_pr_options_t *options);

Parameters

options Options

Discussion

This function frees the memory used for a job options structure.

papplJobFilterImage

Filter an image in memory.

bool papplJobFilterImage(pappl_job_t *job, pappl_device_t *device, pappl_pr_options_t *options, const unsigned char *pixels, int width, int height, int depth, int ppi, bool smoothing);

Parameters

job Job
device Device
options Print options
pixels Pointer to the top-left corner of the image data
width Width in columns
height Height in lines
depth Bytes per pixel (1 for grayscale or 3 for sRGB)
ppi Pixels per inch (0 for unknown)
smoothing true to smooth/interpolate the image, false for nearest-neighbor sampling

Return Value

true on success, false otherwise

Discussion

This function will print a grayscale or sRGB image using the printer's raster driver interface, scaling and positioning the image as necessary based on the job options, and printing as many copies as requested.

The image data is an array of grayscale ("depth" = 1) or sRGB ("depth" = 3) pixels starting at the top-left corner of the image.

The image resolution ("ppi") is expressed in pixels per inch and is used for some "print-scaling" modes. Pass 0 if the image has no explicit resolution information.

papplJobGetAttribute

Get an attribute from a job.

ipp_attribute_t *papplJobGetAttribute(pappl_job_t *job, const char *name);

Parameters

job Job
name Attribute name

Return Value

Attribute or NULL if not found

Discussion

This function gets the named IPP attribute from a job. The returned attribute can be examined using the ippGetXxx functions.

papplJobGetData

Get per-job driver data.

void *papplJobGetData(pappl_job_t *job);

Parameters

job Job

Return Value

Per-job driver data or NULL if none

Discussion

This function returns the driver data associated with the job. It is normally only called from drivers to maintain state for the processing of the job, for example to store bitmap compression information.

papplJobGetFilename

Get the job's filename.

const char *papplJobGetFilename(pappl_job_t *job);

Parameters

job Job

Return Value

Filename or NULL if none

Discussion

This function returns the filename for the job's document data.

papplJobGetFormat

Get the MIME media type for the job's file.

const char *papplJobGetFormat(pappl_job_t *job);

Parameters

job Job

Return Value

MIME media type or NULL for none

Discussion

This function returns the MIME media type for the job's document data.

papplJobGetID

Get the job ID value.

int papplJobGetID(pappl_job_t *job);

Parameters

job Job

Return Value

Job ID or 0 for none

Discussion

This function returns the job's unique integer identifier.

papplJobGetImpressions

Get the number of impressions (sides) in the job.

int papplJobGetImpressions(pappl_job_t *job);

Parameters

job Job

Return Value

Number of impressions in job

Discussion

This function returns the number of impressions in the job's document data. An impression is one side of an output page.

papplJobGetImpressionsCompleted

Get the number of completed impressions (sides) in the job.

int papplJobGetImpressionsCompleted(pappl_job_t *job);

Parameters

job Job

Return Value

Number of completed impressions in job

Discussion

This function returns the number of impressions that have been printed. An impression is one side of an output page.

papplJobGetMessage

Get the current job message string, if any.

const char *papplJobGetMessage(pappl_job_t *job);

Parameters

job Job

Return Value

Current "job-state-message" value or NULL for none

Discussion

This function returns the current job message string, if any.

papplJobGetName

Get the job name/title.

const char *papplJobGetName(pappl_job_t *job);

Parameters

job Job

Return Value

Job name/title or NULL for none

Discussion

This function returns the name or title of the job.

papplJobGetPrinter

Get the printer for the job.

pappl_printer_t *papplJobGetPrinter(pappl_job_t *job);

Parameters

job Job

Return Value

Printer

Discussion

This function returns the printer containing the job.

papplJobGetReasons

Get the current job state reasons.

pappl_jreason_t papplJobGetReasons(pappl_job_t *job);

Parameters

job Job

Return Value

IPP "job-state-reasons" bits

Discussion

This function returns the current job state reasons bitfield.

papplJobGetState

Get the current job state.

ipp_jstate_t papplJobGetState(pappl_job_t *job);

Parameters

job Job

Return Value

IPP "job-state" value

Discussion

This function returns the current job processing state, which is represented as an enumeration:

  • IPP_JSTATE_ABORTED: Job has been aborted by the system due to an error.
  • IPP_JSTATE_CANCELED: Job has been canceled by a user.
  • IPP_JSTATE_COMPLETED: Job has finished printing.
  • IPP_JSTATE_HELD: Job is being held for some reason, typically because the document data is being received.
  • IPP_JSTATE_PENDING: Job is queued and waiting to be printed.
  • IPP_JSTATE_PROCESSING: Job is being printed.
  • IPP_JSTATE_STOPPED: Job is paused, typically when the printer is not ready.

papplJobGetTimeCompleted

Get the job completion time, if any.

time_t papplJobGetTimeCompleted(pappl_job_t *job);

Parameters

job Job

Return Value

Date/time when the job completed or 0 if not completed

Discussion

This function returns the date and time when the job reached the completed, canceled, or aborted states. 0 is returned if the job is not yet in one of those states.

papplJobGetTimeCreated

Get the job creation time.

time_t papplJobGetTimeCreated(pappl_job_t *job);

Parameters

job Job

Return Value

Date/time when the job was created

Discussion

This function returns the date and time when the job was created.

papplJobGetTimeProcessed

Get the job processing time.

time_t papplJobGetTimeProcessed(pappl_job_t *job);

Parameters

job Job

Return Value

Date/time when the job started processing (printing) or 0 if not yet processed

Discussion

This function returns the date and time when the job started processing (printing).

papplJobGetUsername

Get the name of the user that submitted the job.

const char *papplJobGetUsername(pappl_job_t *job);

Parameters

job Job

Return Value

Username or NULL for unknown

Discussion

This function returns the name of the user that submitted the job.

papplJobIsCanceled

Return whether the job is canceled.

bool papplJobIsCanceled(pappl_job_t *job);

Parameters

job Job

Return Value

true if the job is canceled or aborted, false otherwise

Discussion

This function returns true if the job has been canceled or aborted.

papplJobOpenFile

Create or open a file for the document in a job.

int papplJobOpenFile(pappl_job_t *job, char *fname, size_t fnamesize, const char *directory, const char *ext, const char *mode);

Parameters

job Job
fname Filename buffer
fnamesize Size of filename buffer
directory Directory to store in (NULL for default)
ext Extension (NULL for default)
mode Open mode - "r" for reading or "w" for writing

Return Value

File descriptor or -1 on error

Discussion

This function creates or opens a file for a job. The "fname" and "fnamesize" arguments specify the location and size of a buffer to store the job filename, which incorporates the "directory", printer ID, job ID, job name (title), and "ext" values. The job name is "sanitized" to only contain alphanumeric characters.

The "mode" argument is "r" to read an existing job file or "w" to write a new job file. New files are created with restricted permissions for security purposes.

papplJobSetData

Set the per-job driver data pointer.

void papplJobSetData(pappl_job_t *job, void *data);

Parameters

job Job
data Data pointer

Discussion

This function sets the driver data for the specified job. It is normally only called from drivers to maintain state for the processing of the job, for example to store bitmap compression information.

papplJobSetImpressions

Set the number of impressions (sides) in a job.

void papplJobSetImpressions(pappl_job_t *job, int impressions);

Parameters

job Job
impressions Number of impressions/sides

Discussion

This function sets the number of impressions in a job. An impression is one side of an output page.

papplJobSetImpressionsCompleted

Add completed impressions (sides) to the job.

void papplJobSetImpressionsCompleted(pappl_job_t *job, int add);

Parameters

job Job
add Number of impressions/sides to add

Discussion

This function updates the number of completed impressions in a job. An impression is one side of an output page.

papplJobSetMessage

Set the job message string.

void papplJobSetMessage(pappl_job_t *job, const char *message, ...);

Parameters

job Job
message Printf-style message string
... Additional arguments as needed

Discussion

This function sets the job message string using a printf-style format string.

Note: The maximum length of the job message string is 1023 bytes.

papplJobSetReasons

Set the job state reasons bit values.

void papplJobSetReasons(pappl_job_t *job, pappl_jreason_t add, pappl_jreason_t remove);

Parameters

job Job
add IPP "job-state-reasons" bit value(s) to add
remove IPP "job-state-reasons" bit value(s) to remove

Discussion

This function updates the job state reasons bitfield. The "remove" bits are cleared first, then the "add" bits are set.

papplLog

Log a message for the system.

void papplLog(pappl_system_t *system, pappl_loglevel_t level, const char *message, ...);

Parameters

system System
level Log level
message Printf-style message string
... Additional arguments as needed

Discussion

This function sends a message to the system's log file. The "level" argument specifies the urgency of the message:

  • PAPPL_LOGLEVEL_DEBUG: A debugging message.
  • PAPPL_LOGLEVEL_ERROR: An error message.
  • PAPPL_LOGLEVEL_FATAL: A fatal error message.
  • PAPPL_LOGLEVEL_INFO: An informational message.
  • PAPPL_LOGLEVEL_WARN: A warning message.

The "message" argument specifies a printf-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters.

papplLogClient

Log a message for a client.

void papplLogClient(pappl_client_t *client, pappl_loglevel_t level, const char *message, ...);

Parameters

client Client
level Log level
message Printf-style message string
... Additional arguments as needed

Discussion

This function sends a client message to the system's log file. The "level" argument specifies the urgency of the message:

  • PAPPL_LOGLEVEL_DEBUG: A debugging message.
  • PAPPL_LOGLEVEL_ERROR: An error message.
  • PAPPL_LOGLEVEL_FATAL: A fatal error message.
  • PAPPL_LOGLEVEL_INFO: An informational message.
  • PAPPL_LOGLEVEL_WARN: A warning message.

The "message" argument specifies a printf-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters.

papplLogDevice

Log a device error for the system...

void papplLogDevice(const char *message, void *data);

Parameters

message Message
data System

Discussion

This function sends a device error message to the system's log file.

papplLogJob

Log a message for a job.

void papplLogJob(pappl_job_t *job, pappl_loglevel_t level, const char *message, ...);

Parameters

job Job
level Log level
message Printf-style message string
... Additional arguments as needed

Discussion

This function sends a job message to the system's log file. The "level" argument specifies the urgency of the message:

  • PAPPL_LOGLEVEL_DEBUG: A debugging message.
  • PAPPL_LOGLEVEL_ERROR: An error message.
  • PAPPL_LOGLEVEL_FATAL: A fatal error message.
  • PAPPL_LOGLEVEL_INFO: An informational message.
  • PAPPL_LOGLEVEL_WARN: A warning message.

The "message" argument specifies a printf-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters.

papplLogPrinter

Log a message for a printer.

void papplLogPrinter(pappl_printer_t *printer, pappl_loglevel_t level, const char *message, ...);

Parameters

printer Printer
level Log level
message Printf-style message string
... Additional arguments as needed

Discussion

This function sends a printer message to the system's log file. The "level" argument specifies the urgency of the message:

  • PAPPL_LOGLEVEL_DEBUG: A debugging message.
  • PAPPL_LOGLEVEL_ERROR: An error message.
  • PAPPL_LOGLEVEL_FATAL: A fatal error message.
  • PAPPL_LOGLEVEL_INFO: An informational message.
  • PAPPL_LOGLEVEL_WARN: A warning message.

The "message" argument specifies a printf-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters.

papplMainloop

Run a standard main loop for printer applications.

int papplMainloop(int argc, char *argv[], const char *version, const char *footer_html, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_driver_cb_t driver_cb, const char *subcmd_name, pappl_ml_subcmd_cb_t subcmd_cb, pappl_ml_system_cb_t system_cb, pappl_ml_usage_cb_t usage_cb, void *data);

Parameters

argc Number of command line arguments
argv[] Command line arguments
version Version number
footer_html Footer HTML or NULL for none
num_drivers Number of drivers
drivers Drivers
autoadd_cb Auto-add callback or NULL for none
driver_cb Driver callback
subcmd_name Sub-command name or NULL for none
subcmd_cb Sub-command callback or NULL for none
system_cb System callback or NULL for default
usage_cb Usage callback or NULL for default
data Context pointer

Return Value

Exit status

Discussion

This function runs a standard main loop for a printer application. The "argc" and "argv" arguments are those provided to the main function.

The "version" argument specifies a numeric version number for the printer application that conforms to semantic versioning guidelines with up to four numbers, for example "1.2.3.4".

The "footer_html" argument specifies HTML text to use in the footer of the web interface. If NULL, the footer is omitted.

The "num_drivers", "drivers", and "driver_cb" arguments specify a list of drivers and the driver callback for printers. Specify 0 and NULL if the drivers are configured in the system callback. The "autoadd_cb" argument specifies a callback for automatically adding new printers with the "autoadd" sub-command and for auto-detecting the driver when adding manually.

The "usage_cb" argument specifies a callback that displays a usage/help summary. If NULL, a generic summary is shown as needed.

The "subcmd_name" and "subcmd_cb" arguments specify the name and a callback for a custom sub-command. If NULL, no custom sub-commands will be supported.

The "system_cb" argument specifies a function that will create a new pappl_system_t object. If NULL, a default system object is created.

The "data" argument provides application-specific data for each of the callbacks.

papplPrinterAddLink

Add a printer link to the navigation header.

void papplPrinterAddLink(pappl_printer_t *printer, const char *label, const char *path_or_url, pappl_loptions_t options);

Parameters

printer Printer
label Label string
path_or_url Path or URL
options Link options

Discussion

This function adds a navigation link for a printer. The "path_or_url" argument specifies a absolute path such as "/ipp/print/example/page" or an absolute URL such as "https://www.example.com/". The "options" argument specifies where the link is shown and whether the link should redirect an absolute path to the secure ("https://.../path") web interface.

papplPrinterCancelAllJobs

Cancel all jobs on the printer.

void papplPrinterCancelAllJobs(pappl_printer_t *printer);

Parameters

printer Printer

Discussion

This function cancels all jobs on the printer. If any job is currently being printed, it will be stopped at a convenient time (usually the end of a page) so that the printer will be left in a known state.

papplPrinterCloseDevice

Close the device associated with the printer.

void papplPrinterCloseDevice(pappl_printer_t *printer);

Parameters

printer Printer

Discussion

This function closes the device for a printer. The device must have been previously opened using the papplPrinterOpenDevice function.

papplPrinterCreate

Create a new printer.

pappl_printer_t *papplPrinterCreate(pappl_system_t *system, int printer_id, const char *printer_name, const char *driver_name, const char *device_id, const char *device_uri);

Parameters

system System
printer_id printer-id value or 0 for new
printer_name Human-readable printer name
driver_name Driver name
device_id IEEE-1284 device ID
device_uri Device URI

Return Value

Printer or NULL on error

Discussion

This function creates a new printer (service) on the specified system. The "type" argument specifies the type of service to create and must currently be the value PAPPL_SERVICE_TYPE_PRINT.

The "printer_id" argument specifies a positive integer identifier that is unique to the system. If you specify a value of 0 a new identifier will be assigned.

The "driver_name" argument specifies a named driver for the printer, from the list of drivers registered with the papplSystemSetPrinterDrivers function.

The "device_id" and "device_uri" arguments specify the IEEE-1284 device ID and device URI strings for the printer.

On error, this function sets the errno variable to one of the following values:

  • EEXIST: A printer with the specified name already exists.
  • EINVAL: Bad values for the arguments were specified.
  • EIO: The driver callback failed.
  • ENOENT: No driver callback has been set.
  • ENOMEM: Ran out of memory.

papplPrinterDelete

Delete a printer.

void papplPrinterDelete(pappl_printer_t *printer);

Parameters

printer Printer

Discussion

This function deletes a printer from a system, freeing all memory and canceling all jobs as needed.

papplPrinterFindJob

Find a job.

pappl_job_t *papplPrinterFindJob(pappl_printer_t *printer, int job_id);

Parameters

printer Printer
job_id Job ID

Return Value

Job or NULL if not found

Discussion

This function finds a job submitted to a printer using its integer ID value.

papplPrinterGetContact

Get the "printer-contact" value.

pappl_contact_t *papplPrinterGetContact(pappl_printer_t *printer, pappl_contact_t *contact);

Parameters

printer Printer
contact Contact

Return Value

Contact

Discussion

This function copies the current printer contact information to the buffer pointed to by the "contact" argument.

papplPrinterGetDNSSDName

Get the current DNS-SD service name.

char *papplPrinterGetDNSSDName(pappl_printer_t *printer, char *buffer, size_t bufsize);

Parameters

printer Printer
buffer String buffer
bufsize Size of string buffer

Return Value

DNS-SD service name or NULL for none

Discussion

This function copies the current DNS-SD service name to the buffer pointed to by the "buffer" argument.

papplPrinterGetDeviceID

Get the IEEE-1284 device ID of the printer.

const char *papplPrinterGetDeviceID(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

IEEE-1284 device ID string

Discussion

This function returns the IEEE-1284 device ID of the printer.

papplPrinterGetDeviceURI

Get the URI of the device associated with the printer.

const char *papplPrinterGetDeviceURI(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Device URI string

Discussion

This function returns the device URI for the printer.

papplPrinterGetDriverAttributes

Get a copy of the current driver attributes.

ipp_t *papplPrinterGetDriverAttributes(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Copy of driver attributes

Discussion

This function returns a copy the current driver attributes. Use the ippDelete function to free the memory used for the attributes when you are done.

papplPrinterGetDriverData

Get the current print driver data.

pappl_pr_driver_data_t *papplPrinterGetDriverData(pappl_printer_t *printer, pappl_pr_driver_data_t *data);

Parameters

printer Printer
data Pointer to driver data structure to fill

Return Value

Driver data or NULL if none

Discussion

This function copies the current print driver data, defaults, and ready (loaded) media information into the specified buffer.

papplPrinterGetDriverName

Get the driver name for a printer.

const char *papplPrinterGetDriverName(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Driver name or NULL for none

Discussion

This function returns the driver name for the printer.

papplPrinterGetGeoLocation

Get the current geo-location as a "geo:" URI.

char *papplPrinterGetGeoLocation(pappl_printer_t *printer, char *buffer, size_t bufsize);

Parameters

printer Printer
buffer String buffer
bufsize Size of string buffer

Return Value

"geo:" URI or NULL for unknown

Discussion

This function copies the currently configured geographic location as a "geo:" URI to the buffer pointed to by the "buffer" argument.

papplPrinterGetID

Get the printer ID.

int papplPrinterGetID(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

"printer-id" value or 0 for none

Discussion

This function returns the printer's unique positive integer identifier.

papplPrinterGetImpressionsCompleted

Get the number of impressions (sides) that have been printed.

int papplPrinterGetImpressionsCompleted(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Number of printed impressions/sides

Discussion

This function returns the number of impressions that have been printed. An impression is one side of an output page.

papplPrinterGetLocation

Get the location string.

char *papplPrinterGetLocation(pappl_printer_t *printer, char *buffer, size_t bufsize);

Parameters

printer Printer
buffer String buffer
bufsize Size of string buffer

Return Value

Location or NULL for none

Discussion

This function copies the printer's human-readable location to the buffer pointed to by the "buffer" argument.

papplPrinterGetMaxActiveJobs

Get the maximum number of active (queued) jobs allowed by the printer.

int papplPrinterGetMaxActiveJobs(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Maximum number of active jobs

Discussion

This function returns the maximum number of active jobs that the printer supports, as configured by the papplPrinterSetMaxActiveJobs function.

papplPrinterGetMaxCompletedJobs

Get the maximum number of jobs retained for history by the printer.

int papplPrinterGetMaxCompletedJobs(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Maximum number of completed jobs

Discussion

This function returns the maximum number of jobs that are retained in the job history as configured by the papplPrinterSetMaxCompletedJobs function.

papplPrinterGetName

Get the printer name.

const char *papplPrinterGetName(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Printer name

Discussion

This function returns the printer's human-readable name.

papplPrinterGetNextJobID

Get the next job ID.

int papplPrinterGetNextJobID(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Next job ID or 0 for none

Discussion

This function returns the positive integer identifier that will be used for the next job that is created.

papplPrinterGetNumberOfActiveJobs

Get the number of active print jobs.

int papplPrinterGetNumberOfActiveJobs(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Number of active print jobs

Discussion

This function returns the number of print jobs that are either printing or waiting to be printed.

papplPrinterGetNumberOfCompletedJobs

Get the number of completed print jobs.

int papplPrinterGetNumberOfCompletedJobs(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Number of completed print jobs

Discussion

This function returns the number of print jobs that have been aborted, canceled, or completed.

papplPrinterGetNumberOfJobs

Get the total number of print jobs.

int papplPrinterGetNumberOfJobs(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Total number of print jobs

Discussion

This function returns the number of print jobs that are printing, waiting to be printed, have been aborted, have been canceled, or have completed.

papplPrinterGetOrganization

Get the organization name.

char *papplPrinterGetOrganization(pappl_printer_t *printer, char *buffer, size_t bufsize);

Parameters

printer Printer
buffer String buffer
bufsize Size of string buffer

Return Value

Organization name or NULL for none

Discussion

This function copies the printer's organization name to the buffer pointed to by the "buffer" argument.

papplPrinterGetOrganizationalUnit

Get the organizational unit name.

char *papplPrinterGetOrganizationalUnit(pappl_printer_t *printer, char *buffer, size_t bufsize);

Parameters

printer Printer
buffer String buffer
bufsize Size of string buffer

Return Value

Organizational unit name or NULL for none

Discussion

This function copies the printer's organizational unit name to the buffer pointed to by the "buffer" argument.

papplPrinterGetPath

Get the URL path for a printer web page.

char *papplPrinterGetPath(pappl_printer_t *printer, const char *subpath, char *buffer, size_t bufsize);

Parameters

printer Printer
subpath Sub-path or NULL for none
buffer String buffer
bufsize Size of string buffer

Return Value

URI path or NULL on error

Discussion

This function generates and returns the URL path for the printer's web page. The "subpath" argument specifies an optional sub-path for a specific printer web page.

papplPrinterGetPrintGroup

Get the print authorization group, if any.

char *papplPrinterGetPrintGroup(pappl_printer_t *printer, char *buffer, size_t bufsize);

Parameters

printer Printer
buffer String buffer
bufsize Size of string buffer

Return Value

Print authorization group name or NULL for none

Discussion

This function copies the printer's authorization group name to the buffer pointed to by the "buffer" argument.

papplPrinterGetReasons

Get the current "printer-state-reasons" bit values.

pappl_preason_t papplPrinterGetReasons(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

"printer-state-reasons" bit values

Discussion

This function returns the current printer state reasons bitfield, which can be updated by the printer driver and/or by the papplPrinterSetReasons function.

papplPrinterGetState

Get the current "printer-state" value.

ipp_pstate_t papplPrinterGetState(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

"printer-state" value

Discussion

This function returns the current printer state as an enumeration:

  • IPP_PSTATE_IDLE: The printer is idle and has no jobs to process.
  • IPP_PSTATE_PROCESSING: The printer is processing a job and/or producing output.
  • IPP_PSTATE_STOPPED: The printer is stopped for maintenance.

papplPrinterGetSupplies

Get the current "printer-supplies" values.

int papplPrinterGetSupplies(pappl_printer_t *printer, int max_supplies, pappl_supply_t *supplies);

Parameters

printer Printer
max_supplies Maximum number of supplies
supplies Array for supplies

Return Value

Number of values

Discussion

This function copies the current printer supply values to the specified array. The "max_supplies" and "supplies" arguments can be 0 and NULL to query the number of supplies used.

The return value is the actual number of supplies used by the printer, regardless of the size of the array.

papplPrinterGetSystem

Get the system associated with the printer.

pappl_system_t *papplPrinterGetSystem(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

System

Discussion

This function returns a pointer to the system object that contains the printer.

papplPrinterIterateActiveJobs

Iterate over the active jobs.

void papplPrinterIterateActiveJobs(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int job_index, int limit);

Parameters

printer Printer
cb Callback function
data Callback data
job_index First job to iterate (1-based)
limit Maximum jobs to iterate or 0 for no limit

Discussion

This function iterates over jobs that are either printing or waiting to be printed. The specified callback "cb" will be called once per job with the data pointer "data".

The "job_index" argument specifies the first job in the list to iterate, where 1 is the first job, etc. The "limit" argument specifies the maximum number of jobs to iterate - use 0 to iterate an unlimited number of jobs.

papplPrinterIterateAllJobs

Iterate over all the jobs.

void papplPrinterIterateAllJobs(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int job_index, int limit);

Parameters

printer Printer
cb Callback function
data Callback data
job_index First job to iterate (1-based)
limit Maximum jobs to iterate, 0 for no limit

Discussion

This function iterates over all jobs. The specified callback "cb" will be called once per job with the data pointer "data".

The "job_index" argument specifies the first job in the list to iterate, where 1 is the first job, etc. The "limit" argument specifies the maximum number of jobs to iterate - use 0 to iterate an unlimited number of jobs.

papplPrinterIterateCompletedJobs

Iterate over the completed jobs.

void papplPrinterIterateCompletedJobs(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int job_index, int limit);

Parameters

printer Printer
cb Callback function
data Callback data
job_index First job to iterate (1-based)
limit Maximum jobs to iterate, 0 for no limit

Discussion

This function iterates over jobs that are aborted, canceled, or completed. The specified callback "cb" will be called once per job with the data pointer "data".

The "job_index" argument specifies the first job in the list to iterate, where 1 is the first job, etc. The "limit" argument specifies the maximum number of jobs to iterate - use 0 to iterate an unlimited number of jobs.

papplPrinterOpenDevice

Open the device associated with a printer.

pappl_device_t *papplPrinterOpenDevice(pappl_printer_t *printer);

Parameters

printer Printer

Return Value

Device or NULL if not possible

Discussion

This function opens the printer's device. NULL is returned if the device is already in use, for example while a job is being printed.

The returned device must be closed using the papplPrinterCloseDevice function.

papplPrinterPause

Pause (stop) a printer.

void papplPrinterPause(pappl_printer_t *printer);

Parameters

printer Printer

Discussion

This function pauses a printer. If the printer is currently processing (printing) a job, it will be completed before the printer is stopped.

papplPrinterRemoveLink

Remove a printer link from the navigation header.

void papplPrinterRemoveLink(pappl_printer_t *printer, const char *label);

Parameters

printer Printer
label Label string

Discussion

This function removes the named link for the printer.

papplPrinterResume

Resume (start) a printer.

void papplPrinterResume(pappl_printer_t *printer);

Parameters

printer Printer

Discussion

This function resumes a printer and starts processing any pending jobs.

papplPrinterSetContact

Set the "printer-contact" value.

void papplPrinterSetContact(pappl_printer_t *printer, pappl_contact_t *contact);

Parameters

printer Printer
contact Contact

Discussion

This function sets the printer's contact information.

papplPrinterSetDNSSDName

Set the DNS-SD service name.

void papplPrinterSetDNSSDName(pappl_printer_t *printer, const char *value);

Parameters

printer Printer
value DNS-SD service name or NULL for none

Discussion

This function sets the printer's DNS-SD service name. If NULL, the printer will stop advertising the printer.

papplPrinterSetDriverData

Set the driver data.

bool papplPrinterSetDriverData(pappl_printer_t *printer, pappl_pr_driver_data_t *data, ipp_t *attrs);

Parameters

printer Printer
data Driver data
attrs Additional capability attributes or NULL for none

Return Value

true on success, false on failure

Discussion

This function validates and sets the driver data, including all defaults and ready (loaded) media.

Note: This function regenerates all of the driver-specific capability attributes like "media-col-database", "sides-supported", and so forth. Use the papplPrinterSetDriverDefaults or papplPrinterSetReadyMedia functions to efficiently change the "xxx-default" or "xxx-ready" values, respectively.

papplPrinterSetDriverDefaults

Set the default print option values.

bool papplPrinterSetDriverDefaults(pappl_printer_t *printer, pappl_pr_driver_data_t *data, int num_vendor, cups_option_t *vendor);

Parameters

printer Printer
data Driver data
num_vendor Number of vendor options
vendor Vendor options

Return Value

true on success or false on failure

Discussion

This function validates and sets the printer's default print options.

Note: Unlike papplPrinterSetPrintDriverData, this function only changes the "xxx_default" members of the driver data and is considered lightweight.

papplPrinterSetGeoLocation

Set the geo-location value as a "geo:" URI.

void papplPrinterSetGeoLocation(pappl_printer_t *printer, const char *value);

Parameters

printer Printer
value "geo:" URI or NULL for unknown

Discussion

This function sets the printer's geographic location as a "geo:" URI. If NULL, the location is cleared to the 'unknown' value.

papplPrinterSetImpressionsCompleted

Add impressions (sides) to the total count of printed impressions.

void papplPrinterSetImpressionsCompleted(pappl_printer_t *printer, int add);

Parameters

printer Printer
add Number of impressions/sides to add

Discussion

This function adds to the printer's impressions counter. An impression is one side of an output page.

papplPrinterSetLocation

Set the location string.

void papplPrinterSetLocation(pappl_printer_t *printer, const char *value);

Parameters

printer Printer
value Location ("Bob's Office", etc.) or NULL for none

Discussion

This function sets the printer's human-readable location string. If NULL, the location is cleared.

papplPrinterSetMaxActiveJobs

Set the maximum number of active jobs for the printer.

void papplPrinterSetMaxActiveJobs(pappl_printer_t *printer, int max_active_jobs);

Parameters

printer Printer
max_active_jobs Maximum number of active jobs, 0 for unlimited

Discussion

This function sets the maximum number of jobs that can be spooled on the printer at one time.

Note: This limit does not apply to streaming raster formats such as PWG Raster since they are not spooled.

papplPrinterSetMaxCompletedJobs

Set the maximum number of completed jobs for the printer.

void papplPrinterSetMaxCompletedJobs(pappl_printer_t *printer, int max_completed_jobs);

Parameters

printer Printer
max_completed_jobs Maximum number of completed jobs, 0 for unlimited

Discussion

This function sets the maximum number of aborted, canceled, or completed jobs that are retained in the job history.

papplPrinterSetNextJobID

Set the next "job-id" value.

void papplPrinterSetNextJobID(pappl_printer_t *printer, int next_job_id);

Parameters

printer Printer
next_job_id Next "job-id" value

Discussion

This function sets the next unique positive integer identifier that will be used for a job.

Note: This function is normally only called once to restore the previous state of the printer.

papplPrinterSetOrganization

Set the organization name.

void papplPrinterSetOrganization(pappl_printer_t *printer, const char *value);

Parameters

printer Printer
value Organization name or NULL for none

Discussion

This function sets the printer's organization name. If NULL the value is cleared.

papplPrinterSetOrganizationalUnit

Set the organizational unit name.

void papplPrinterSetOrganizationalUnit(pappl_printer_t *printer, const char *value);

Parameters

printer Printer
value Organizational unit name or NULL for none

Discussion

This function sets the printer's organizational unit name. If NULL the value is cleared.

papplPrinterSetPrintGroup

Set the print authorization group, if any.

void papplPrinterSetPrintGroup(pappl_printer_t *printer, const char *value);

Parameters

printer Printer
value Print authorization group or NULL for none

Discussion

This function sets the printer's authorization group. If NULL, the group is cleared.

Note: The authorization group is only used if the system is created with a named authorization service.

papplPrinterSetReadyMedia

Set the ready (loaded) media.

bool papplPrinterSetReadyMedia(pappl_printer_t *printer, int num_ready, pappl_media_col_t *ready);

Parameters

printer Printer
num_ready Number of ready media
ready Array of ready media

Return Value

true on success or false on failure

Discussion

This function validates and sets the printer's ready (loaded) media.

papplPrinterSetReasons

Add or remove values from "printer-state-reasons".

void papplPrinterSetReasons(pappl_printer_t *printer, pappl_preason_t add, pappl_preason_t remove);

Parameters

printer Printer
add "printer-state-reasons" bit values to add or PAPPL_PREASON_NONE for none
remove "printer-state-reasons" bit values to remove or PAPPL_PREASON_NONE for none

Discussion

This function updates the printer state reasons bitfield by clearing any bit values in the "remove" argument and setting any bit values in the "add" argument.

papplPrinterSetSupplies

Set/update the supplies for a printer.

void papplPrinterSetSupplies(pappl_printer_t *printer, int num_supplies, pappl_supply_t *supplies);

Parameters

printer Printer
num_supplies Number of supplies
supplies Array of supplies

Discussion

This function updates the supply information for the printer.

papplPrinterSetUSB

Set the USB vendor and product IDs for a printer.

void papplPrinterSetUSB(pappl_printer_t *printer, unsigned vendor_id, unsigned product_id, pappl_uoptions_t options, const char *storagefile);

Parameters

printer Printer
vendor_id USB vendor ID
product_id USB product ID
options USB gadget options
storagefile USB storage file, if any

Discussion

This function sets the USB vendor and product IDs for a printer as well as specifying USB gadget options when the printer is registered with the USB device controller.

Note: USB gadget functionality is currently only available when running on Linux with compatible hardware such as the Raspberry Pi.

papplSystemAddLink

Add a link to the navigation header.

void papplSystemAddLink(pappl_system_t *system, const char *label, const char *path_or_url, pappl_loptions_t options);

Parameters

system System
label Label string
path_or_url Path or URL
options Link options

Discussion

This function adds a navigation link for the system. The "path_or_url" argument specifies a absolute path such as "/page" or an absolute URL such as "https://www.example.com/". The "options" argument specifies where the link is shown and whether the link should redirect an absolute path to the secure ("https://.../path") web interface.

papplSystemAddListeners

Add network or domain socket listeners.

bool papplSystemAddListeners(pappl_system_t *system, const char *name);

Parameters

system System
name Hostname, domain socket filename, or NULL

Return Value

true on success, false on failure

Discussion

This function adds socket listeners. The "name" parameter specifies the listener address. Names starting with a slash (/) specify a UNIX domain socket path, otherwise the name is treated as a fully-qualified domain name or numeric IPv4 or IPv6 address. If name is NULL, the "any" addresses are used ("0.0.0.0" and "[::]").

Listeners cannot be added after papplSystemRun is called.

papplSystemAddMIMEFilter

Add a file filter to the system.

void papplSystemAddMIMEFilter(pappl_system_t *system, const char *srctype, const char *dsttype, pappl_mime_filter_cb_t cb, void *data);

Parameters

system System
srctype Source MIME media type (constant) string
dsttype Destination MIME media type (constant) string
cb Filter callback function
data Filter callback data

Discussion

This function adds a file filter to the system to be used for processing different kinds of document data in print jobs. The "srctype" and "dsttype" arguments specify the source and destination MIME media types as constant strings. A destination MIME media type of "image/pwg-raster" specifies a filter that uses the driver's raster interface. Other destination types imply direct submission to the output device using the papplDeviceXxx functions.

Note: This function may not be called while the system is running.

papplSystemAddResourceCallback

Add a dynamic resource that uses a callback function.

void papplSystemAddResourceCallback(pappl_system_t *system, const char *path, const char *format, pappl_resource_cb_t cb, void *data);

Parameters

system System object
path Resource path
format MIME media type for content such as "text/html"
cb Callback function
data Callback data

Discussion

This function adds a dynamic resource at the specified path. When a client GET or POST request is received at the specified path, the "cb" function will be called with the client pointer and "data" pointer to respond to it.

Resource callbacks are most often used to implement custom web pages.

Note: Any custom web page that is added prior to calling the papplSystemRun function will replace the corresponding standard web page at the same path.

papplSystemAddResourceData

Add a static data resource.

void papplSystemAddResourceData(pappl_system_t *system, const char *path, const char *format, const void *data, size_t datalen);

Parameters

system System object
path Resource path
format MIME media type such as "image/png"
data Resource data
datalen Size of resource data

Discussion

This function adds a static resource at the specified path. The provided data is not copied to the resource and must remain stable for as long as the resource is added to the system.

Note: Any resource that is added prior to calling the papplSystemRun function will replace the corresponding standard resource at the same path.

papplSystemAddResourceDirectory

Add external files in a directory as resources.

void papplSystemAddResourceDirectory(pappl_system_t *system, const char *basepath, const char *directory);

Parameters

system System object
basepath Base resource path
directory Directory containing resource files

Discussion

This function adds static resources from the specified directory under the specified path. The directory is scanned and only those files present at the time of the call are available, and those files must remain stable for as long as the resources are added to the system..

Note: Any resource that is added prior to calling the papplSystemRun function will replace the corresponding standard resource at the same path.

papplSystemAddResourceFile

Add an external file as a resource.

void papplSystemAddResourceFile(pappl_system_t *system, const char *path, const char *format, const char *filename);

Parameters

system System object
path Resource path
format MIME media type such as "image/png"
filename Filename

Discussion

This function adds a static resource at the specified path. The provided file is not copied to the resource and must remain stable for as long as the resource is added to the system.

Note: Any resource that is added prior to calling the papplSystemRun function will replace the corresponding standard resource at the same path.

papplSystemAddResourceString

Add a static data resource as a C string.

void papplSystemAddResourceString(pappl_system_t *system, const char *path, const char *format, const char *data);

Parameters

system System object
path Resource path
format MIME media type such as "image/png"
data Resource data

Discussion

This function adds a static resource at the specified path. The provided data is not copied to the resource and must remain stable for as long as the resource is added to the system.

Note: Any resource that is added prior to calling the papplSystemRun function will replace the corresponding standard resource at the same path.

papplSystemAddStringsData

Add a static localization file resource.

void papplSystemAddStringsData(pappl_system_t *system, const char *path, const char *language, const char *data);

Parameters

system System object
path Resource path
language ISO language tag such as "en-US", "fr-CA", etc.
data Nul-terminated string containing strings file data

Discussion

This function adds a static localization resource at the specified path. Localization files use the NeXTStep strings ("text/strings") format defined in PWG Candidate Standard 5100.13-2013. The provided data is not copied to the resource and must remain stable for as long as the resource is added to the system.

Note: Any resource that is added prior to calling the papplSystemRun function will replace the corresponding standard resource at the same path.

papplSystemAddStringsFile

Add an external localization file resource.

void papplSystemAddStringsFile(pappl_system_t *system, const char *path, const char *language, const char *filename);

Parameters

system System object
path Resource path
language ISO language tag such as "en-US", "fr-CA", etc.
filename Filename

Discussion

This function adds a static localization resource at the specified path. Localization files use the NeXTStep strings ("text/strings") format defined in PWG Candidate Standard 5100.13-2013. The provided file is not copied to the resource and must remain stable for as long as the resource is added to the system.

Note: Any resource that is added prior to calling the papplSystemRun function will replace the corresponding standard resource at the same path.

papplSystemCleanJobs

Clean out old (completed) jobs.

void papplSystemCleanJobs(pappl_system_t *system);

Parameters

system System

Discussion

This function deletes all old (completed) jobs above the limit set by the papplPrinterSetMaxCompletedJobs function. The level may temporarily exceed this limit if the jobs were completed within the last 60 seconds.

Note: This function is normally called automatically from the papplSystemRun function.

papplSystemCreate

Create a system object.

pappl_system_t *papplSystemCreate(pappl_soptions_t options, const char *name, int port, const char *subtypes, const char *spooldir, const char *logfile, pappl_loglevel_t loglevel, const char *auth_service, bool tls_only);

Parameters

options Server options
name System name
port Port number or 0 for auto
subtypes DNS-SD sub-types or NULL for none
spooldir Spool directory or NULL for default
logfile Log file or NULL for default
loglevel Log level
auth_service PAM authentication service or NULL for none
tls_only Only support TLS connections?

Return Value

System object

Discussion

This function creates a new system object, which is responsible for managing all the printers, jobs, and resources used by the printer application.

The "options" argument specifies which options are enabled for the server:

  • PAPPL_SOPTIONS_NONE: No options.
  • PAPPL_SOPTIONS_DNSSD_HOST: When resolving DNS-SD service name collisions, use the DNS-SD hostname instead of a serial number or UUID.
  • PAPPL_SOPTIONS_WEB_LOG: Include the log file web page.
  • PAPPL_SOPTIONS_MULTI_QUEUE: Support multiple printers.
  • PAPPL_SOPTIONS_WEB_NETWORK: Include the network settings web page.
  • PAPPL_SOPTIONS_RAW_SOCKET: Accept jobs via raw sockets starting on port 9100.
  • PAPPL_SOPTIONS_WEB_REMOTE: Allow remote queue management.
  • PAPPL_SOPTIONS_WEB_SECURITY: Include the security settings web page.
  • PAPPL_SOPTIONS_WEB_INTERFACE: Include the standard printer and job monitoring web pages.
  • PAPPL_SOPTIONS_WEB_TLS: Include the TLS settings page.
  • PAPPL_SOPTIONS_USB_PRINTER: Accept jobs via USB for the default printer (embedded Linux only).

The "name" argument specifies a human-readable name for the system.

The "port" argument specifies the port number to bind to. A value of 0 will cause an available port number to be assigned when the first listener is added with the papplSystemAddListeners function.

The "subtypes" argument specifies one or more comma-delimited DNS-SD service sub-types such as "_print" and "_universal".

The "spooldir" argument specifies the location of job files. If NULL, a temporary directory is created.

The "logfile" argument specifies where to send log messages. If NULL, the log messages are written to a temporary file.

The "loglevel" argument specifies the initial logging level.

The "auth_service" argument specifies a PAM authentication service name. If NULL, no user authentication will be provided.

The "tls_only" argument controls whether the printer application will accept unencrypted connections. In general, this argument should always be false (allow unencrypted connections) since not all clients support encrypted printing.

papplSystemDelete

Delete a system object.

void papplSystemDelete(pappl_system_t *system);

Parameters

system System object

Discussion

Note: A system object cannot be deleted while the system is running.

papplSystemFindPrinter

Find a printer by resource, ID, or device URI.

pappl_printer_t *papplSystemFindPrinter(pappl_system_t *system, const char *resource, int printer_id, const char *device_uri);

Parameters

system System
resource Resource path or NULL
printer_id Printer ID or 0
device_uri Device URI or NULL

Return Value

Printer or NULL if none

Discussion

This function finds a printer contained in the system using its resource path, unique integer identifier, or device URI. If none of these is specified, the current default printer is returned.

papplSystemGetAdminGroup

Get the current administrative group, if any.

char *papplSystemGetAdminGroup(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Admin group or NULL if none

Discussion

This function copies the current administrative group, if any, to the specified buffer.

papplSystemGetAuthService

Get the PAM authorization service, if any.

const char *papplSystemGetAuthService(pappl_system_t *system);

Parameters

system System

Return Value

PAM authorization service or NULL if none

Discussion

This function returns the PAM authorization service being used by the system for authentication, if any.

papplSystemGetContact

Get the "system-contact" value.

pappl_contact_t *papplSystemGetContact(pappl_system_t *system, pappl_contact_t *contact);

Parameters

system System
contact Contact

Return Value

Contact

Discussion

This function copies the current system contact information to the specified buffer.

papplSystemGetDNSSDName

Get the current DNS-SD service name.

char *papplSystemGetDNSSDName(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Current DNS-SD service name or NULL for none

Discussion

This function copies the current DNS-SD service name of the system, if any, to the specified buffer.

papplSystemGetDefaultPrintGroup

Get the default print group, if any.

char *papplSystemGetDefaultPrintGroup(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Default print group or NULL if none

Discussion

This function copies the current default print group, if any, to the specified buffer.

papplSystemGetDefaultPrinterID

Get the current "default-printer-id" value.

int papplSystemGetDefaultPrinterID(pappl_system_t *system);

Parameters

system System

Return Value

"default-printer-id" value

Discussion

This function returns the positive integer identifier for the current default printer or 0 if there is no default printer.

papplSystemGetFooterHTML

Get the footer HTML for the web interface, if any.

const char *papplSystemGetFooterHTML(pappl_system_t *system);

Parameters

system System

Return Value

Footer HTML or NULL if none

Discussion

This function returns the HTML for the web page footer, if any. The footer HTML can be set using the papplSystemSetFooterHTML function.

papplSystemGetGeoLocation

Get the system geo-location string, if any.

char *papplSystemGetGeoLocation(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

"geo:" URI or NULL for none

Discussion

This function copies the current system geographic location as a "geo:" URI to the specified buffer.

papplSystemGetHostname

Get the system hostname.

char *papplSystemGetHostname(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Hostname

Discussion

This function copies the current system hostname to the specified buffer.

papplSystemGetLocation

Get the system location string, if any.

char *papplSystemGetLocation(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Location string or NULL for none

Discussion

This function copies the current human-readable location, if any, to the specified buffer.

papplSystemGetLogLevel

pappl_loglevel_t papplSystemGetLogLevel(pappl_system_t *system);

Parameters

system System

Return Value

Get the system log level.

This function returns the current system log level as an enumeration.

papplSystemGetMaxLogSize

Get the maximum log file size.

size_t papplSystemGetMaxLogSize(pappl_system_t *system);

Parameters

system System

Return Value

Maximum log file size or 0 for none

Discussion

This function gets the maximum log file size, which is only used when logging directly to a file. When the limit is reached, the current log file is renamed to "filename.O" and a new log file is created. Set the maximum size to 0 to disable log file rotation.

The default maximum log file size is 1MiB or 1048576 bytes.

papplSystemGetName

Get the system name.

char *papplSystemGetName(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Name string

Discussion

This function copies the current system name to the specified buffer.

papplSystemGetNextPrinterID

Get the next "printer-id" value.

int papplSystemGetNextPrinterID(pappl_system_t *system);

Parameters

system System

Return Value

Next "printer-id" value

Discussion

This function returns the positive integer identifier that will be used for the next printer that is created.

papplSystemGetOptions

Get the system options.

pappl_soptions_t papplSystemGetOptions(pappl_system_t *system);

Parameters

system System

Return Value

System options

Discussion

This function returns the system options as a bitfield.

papplSystemGetOrganization

Get the system organization string, if any.

char *papplSystemGetOrganization(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Organization string or NULL for none

Discussion

This function copies the current organization name, if any, to the specified buffer.

papplSystemGetOrganizationalUnit

Get the system organizational unit string, if any.

char *papplSystemGetOrganizationalUnit(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Organizational unit string or NULL for none

Discussion

This function copies the current organizational unit name, if any, to the specified buffer.

papplSystemGetPassword

Get the current web site access password.

char *papplSystemGetPassword(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Password hash

Discussion

This function copies the current web site password hash, if any, to the specified buffer.

Note: The access password is only used when the PAM authentication service is not set.

papplSystemGetPort

Get the port number for network connections to the system.

int papplSystemGetPort(pappl_system_t *system);

Parameters

system System

Return Value

Port number

Discussion

This function returns the port number that is used for network connections to the system.

papplSystemGetServerHeader

Get the Server: header for HTTP responses.

const char *papplSystemGetServerHeader(pappl_system_t *system);

Parameters

system System

Return Value

Server: header string or NULL for none

Discussion

This function returns the value of the HTTP "Server:" header that is used by the system.

papplSystemGetSessionKey

Get the current session key.

char *papplSystemGetSessionKey(pappl_system_t *system, char *buffer, size_t bufsize);

Parameters

system System
buffer String buffer
bufsize Size of string buffer

Return Value

Session key

Discussion

This function copies the current session key to the specified buffer. The session key is used for web interface forms to provide CSRF protection and is refreshed periodically.

papplSystemGetTLSOnly

Get the TLS-only state of the system.

bool papplSystemGetTLSOnly(pappl_system_t *system);

Parameters

system System

Return Value

true if the system is only accepting TLS encrypted connections, false otherwise

Discussion

This function returns whether the system will only accept encrypted connections.

papplSystemGetUUID

Get the "system-uuid" value.

const char *papplSystemGetUUID(pappl_system_t *system);

Parameters

system System

Return Value

"system-uuid" value

Discussion

This function returns the system's UUID value.

papplSystemGetVersions

Get the firmware names and versions.

int papplSystemGetVersions(pappl_system_t *system, int max_versions, pappl_version_t *versions);

Parameters

system System
max_versions Maximum number of versions to return
versions Versions array or NULL for don't care

Return Value

Number of firmware versions

Discussion

This function copies the system firmware information to the specified buffer. The return value is always the number of firmware versions that have been set using the papplSystemSetVersions function, regardless of the value of the "max_versions" argument.

papplSystemHashPassword

Generate a password hash using salt and password strings.

char *papplSystemHashPassword(pappl_system_t *system, const char *salt, const char *password, char *buffer, size_t bufsize);

Parameters

system System
salt Existing password hash or NULL to generate a new hash
password Plain-text password string
buffer String buffer
bufsize Size of string buffer

Return Value

Hashed password

Discussion

This function generates a password hash using the "salt" and "password" strings. The "salt" string should be NULL to generate a new password hash or the value of an existing password hash to verify that a given plaintext "password" string matches the password hash.

Note: Hashed access passwords are only used when the PAM authentication service is not set.

papplSystemIsRunning

Return whether the system is running.

bool papplSystemIsRunning(pappl_system_t *system);

Parameters

system System

Return Value

true if the system is running, false otherwise

Discussion

This function returns whether the system is running.

papplSystemIsShutdown

Return whether the system has been shutdown.

bool papplSystemIsShutdown(pappl_system_t *system);

Parameters

system System

Return Value

true if the system is shutdown, false otherwise

Discussion

This function returns whether the system is shutdown or scheduled to shutdown.

papplSystemIteratePrinters

Iterate all of the printers.

void papplSystemIteratePrinters(pappl_system_t *system, pappl_printer_cb_t cb, void *data);

Parameters

system System
cb Callback function
data Callback data

Discussion

This function iterates each of the printers managed by the system. The "cb" function is called once per printer with the "system" and "data" values.

papplSystemLoadState

Load the previous system state.

bool papplSystemLoadState(pappl_system_t *system, const char *filename);

Parameters

system System
filename File to load

Return Value

true on success, false on failure

Discussion

This function loads the previous system state from a file created by the papplSystemSaveState function. The system state contains all of the system object values, the list of printers, and the jobs for each printer.

When loading a printer definition, if the printer cannot be created (e.g., because the driver name is no longer valid) then that printer and all of its job history will be lost. In the case of a bad driver name, a printer application's driver callback can perform any necessary mapping of the driver name, including the use its auto-add callback to find a compatible new driver.

Note: This function must be called prior to papplSystemRun.

papplSystemMatchDriver

const char *papplSystemMatchDriver(pappl_system_t *system, const char *device_id);

Parameters

system System
device_id IEEE-1284 device ID string

Return Value

Match a driver to an IEEE-1284 device ID.

papplSystemRemoveLink

Remove a link from the navigation header.

void papplSystemRemoveLink(pappl_system_t *system, const char *label);

Parameters

system System
label Label string

Discussion

This function removes the named link for the system.

papplSystemRemoveResource

Remove a resource at the specified path.

void papplSystemRemoveResource(pappl_system_t *system, const char *path);

Parameters

system System object
path Resource path

Discussion

This function removes a resource at the specified path.

papplSystemRun

Run the printer application.

void papplSystemRun(pappl_system_t *system);

Parameters

system System

Discussion

This function runs the printer application, accepting new connections, handling requests, and processing jobs as needed. It returns once the system is shutdown, either through an IPP request or SIGTERM.

papplSystemSaveState

Save the current system state.

bool papplSystemSaveState(pappl_system_t *system, const char *filename);

Parameters

system System
filename File to save

Return Value

true on success, false on failure

Discussion

This function saves the current system state to a file. It is typically used with the papplSystemSetSaveCallback function to periodically save the state:

papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState,
    (void *)filename);
```
inter cannot be created (e.g., because the driver name is no longer valid) then that printer and all of its job history will be lost. In the case of a bad driver name, a printer application's driver callback can perform any necessary mapping of the driver name, including the use its auto-add callback to find a compatible new driver.

Note: This function must be called prior to papplSystemRun

papplSystemSetAdminGroup

Set the administrative group.

void papplSystemSetAdminGroup(pappl_system_t *system, const char *value);

Parameters

system System
value Admin group

Discussion

This function sets the group name used for administrative requests such as adding or deleting a printer.

Note: The administrative group is only used when the PAM authorization service is also set when the system is created.

papplSystemSetContact

Set the "system-contact" value.

void papplSystemSetContact(pappl_system_t *system, pappl_contact_t *contact);

Parameters

system System
contact Contact

Discussion

This function sets the system contact value.

papplSystemSetDNSSDName

Set the DNS-SD service name.

void papplSystemSetDNSSDName(pappl_system_t *system, const char *value);

Parameters

system System
value DNS-SD service name or NULL for none

Discussion

This function sets the DNS-SD service name of the system. If NULL, the DNS-SD registration is removed.

papplSystemSetDefaultPrintGroup

Set the default print group.

void papplSystemSetDefaultPrintGroup(pappl_system_t *system, const char *value);

Parameters

system System
value Default print group or NULL for none

Discussion

This function sets the default group name used for print requests.

Note: The default print group is only used when the PAM authorization service is also set when the system is created.

papplSystemSetDefaultPrinterID

Set the "default-printer-id" value.

void papplSystemSetDefaultPrinterID(pappl_system_t *system, int default_printer_id);

Parameters

system System
default_printer_id "default-printer-id" value

Discussion

This function sets the default printer using its unique positive integer identifier.

papplSystemSetFooterHTML

Set the footer HTML for the web interface.

void papplSystemSetFooterHTML(pappl_system_t *system, const char *html);

Parameters

system System
html Footer HTML or NULL for none

Discussion

This function sets the footer HTML for the web interface.

Note: The footer HTML can only be set prior to calling papplSystemRun.

papplSystemSetGeoLocation

Set the geographic location string.

void papplSystemSetGeoLocation(pappl_system_t *system, const char *value);

Parameters

system System
value "geo:" URI or NULL for none

Discussion

This function sets the geographic location of the system as a "geo:" URI. If NULL, the location is cleared.

papplSystemSetHostname

Set the system hostname.

void papplSystemSetHostname(pappl_system_t *system, const char *value);

Parameters

system System
value Hostname or NULL for default

Discussion

This function sets the system hostname. If NULL, the default hostname is used.

papplSystemSetLocation

Set the system location string, if any.

void papplSystemSetLocation(pappl_system_t *system, const char *value);

Parameters

system System
value Location or NULL for none

Discussion

This function sets the human-readable location of the system. If NULL, the location is cleared.

papplSystemSetLogLevel

Set the system log level

void papplSystemSetLogLevel(pappl_system_t *system, pappl_loglevel_t loglevel);

Parameters

system System
loglevel Log Level

Discussion

This function sets the log level as an enumeration.

papplSystemSetMIMECallback

Set the MIME typing callback for the system.

void papplSystemSetMIMECallback(pappl_system_t *system, pappl_mime_cb_t cb, void *data);

Parameters

system System
cb Callback function
data Callback data

Discussion

This function sets a custom MIME typing callback for the system. The MIME typing callback extends the built-in MIME typing support for other media types that are supported by the application, typically vendor print formats.

The callback function receives a buffer containing the initial bytes of the document data, the length of the buffer, and the callback data. It can then return NULL if the content is not recognized or a constant string containing the MIME media type, for example "application/vnd.hp-pcl" for HP PCL print data.

papplSystemSetMaxLogSize

Set the maximum log file size in bytes.

void papplSystemSetMaxLogSize(pappl_system_t *system, size_t maxsize);

Parameters

system System
maxsize Maximum log size in bytes or 0 for none

Discussion

This function sets the maximum log file size in bytes, which is only used when logging directly to a file. When the limit is reached, the current log file is renamed to "filename.O" and a new log file is created. Set the maximum size to 0 to disable log file rotation.

The default maximum log file size is 1MiB or 1048576 bytes.

papplSystemSetNextPrinterID

Set the next "printer-id" value.

void papplSystemSetNextPrinterID(pappl_system_t *system, int next_printer_id);

Parameters

system System
next_printer_id Next "printer-id" value

Discussion

This function sets the unique positive integer identifier that will be used for the next printer that is created. It is typically only called as part of restoring the state of a system.

Note: The next printer ID can only be set prior to calling papplSystemRun.

papplSystemSetOperationCallback

Set the IPP operation callback.

void papplSystemSetOperationCallback(pappl_system_t *system, pappl_ipp_op_cb_t cb, void *data);

Parameters

system System
cb Callback function
data Callback data

Discussion

This function sets a custom IPP operation handler for the system that is called for any IPP operations that are not handled by the built-in IPP services.

Note: The operation callback can only be set prior to calling papplSystemRun.

papplSystemSetOrganization

Set the system organization string, if any.

void papplSystemSetOrganization(pappl_system_t *system, const char *value);

Parameters

system System
value Organization or NULL for none

Discussion

This function sets the organization name for the system. If NULL, the name is cleared.

papplSystemSetOrganizationalUnit

Set the system organizational unit string, if any.

void papplSystemSetOrganizationalUnit(pappl_system_t *system, const char *value);

Parameters

system System
value Organizational unit or NULL for none

Discussion

This function sets the organizational unit name for the system. If NULL, the name is cleared.

papplSystemSetPassword

Set the access password hash string.

void papplSystemSetPassword(pappl_system_t *system, const char *hash);

Parameters

system System
hash Hash string

Discussion

This function sets the hash for the web access password. The hash string is generated using the papplSystemHashPassword function.

Note: The access password is only used when the PAM authentication service is not set.

papplSystemSetPrinterDrivers

Set the list of drivers and the driver callbacks.

void papplSystemSetPrinterDrivers(pappl_system_t *system, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_create_cb_t create_cb, pappl_pr_driver_cb_t driver_cb, void *data);

Parameters

system System
num_drivers Number of drivers
drivers Drivers
autoadd_cb Auto-add callback function or NULL if none
create_cb Printer creation callback function or NULL if none
driver_cb Driver initialization callback function
data Callback data

Discussion

This function sets the lists of printer drivers, the optional auto-add callback function, the optional creation callback, and the required driver initialization callback function.

The auto-add callback ("autoadd_cb") finds a compatible driver name for the specified printer. It is used when the client or user specifies the "auto" driver name, and for the "autoadd" sub-command for the papplMainloop API.

The creation callback ("create_cb") is called at the end of printer creation to make any common changes or additions to a new printer. It is typically used to add extra web pages, add per-printer static resources, and/or initialize the contact and location information.

The driver initialization callback ("driver_cb") is called to initialize the pappl_pr_driver_data_t structure, which provides all of the printer capabilities and callbacks for printing.

papplSystemSetSaveCallback

Set the save callback.

void papplSystemSetSaveCallback(pappl_system_t *system, pappl_save_cb_t cb, void *data);

Parameters

system System
cb Callback function
data Callback data

Discussion

This function sets a callback that is used to periodically save the current system state. Typically the callback function ("cb") is papplSystemSaveState and the callback data ("data") is the name of the state file:

papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState,
    (void *)filename);

Note: The save callback can only be set prior to calling papplSystemRun.

papplSystemSetUUID

Set the system UUID.

void papplSystemSetUUID(pappl_system_t *system, const char *value);

Parameters

system System
value UUID

Discussion

This function sets the system UUID value, overriding the default (generated) value. It is typically used when restoring the state of a previous incarnation of the system.

Note: The UUID can only be set prior to calling papplSystemRun.

papplSystemSetVersions

Set the firmware names and versions.

void papplSystemSetVersions(pappl_system_t *system, int num_versions, pappl_version_t *versions);

Parameters

system System
num_versions Number of versions
versions Firmware versions

Discussion

This function sets the names and versions of each firmware/software component of the printer application.

papplSystemShutdown

Shutdown the system.

void papplSystemShutdown(pappl_system_t *system);

Parameters

system System

Discussion

This function tells the system to perform an orderly shutdown of all printers and to terminate the main loop.

Data Types

pappl_client_t

Client connection object

typedef struct _pappl_client_s pappl_client_t;

pappl_color_mode_t

Bitfield for IPP "print-color-mode" values

typedef unsigned pappl_color_mode_t;

pappl_contact_t

Contact information

typedef struct pappl_contact_s pappl_contact_t;

pappl_content_t

Bitfield for IPP "print-content-optimize" values

typedef unsigned pappl_content_t;

pappl_default_cb_t

papplIterateDefaults callback

typedef void (*pappl_default_cb_t)(ipp_attribute_t *attr, void *data);

pappl_devclose_cb_t

Device close callback

typedef void (*pappl_devclose_cb_t)(pappl_device_t *device);

pappl_deverror_cb_t

Device error callback

typedef void (*pappl_deverror_cb_t)(const char *message, void *err_data);

pappl_device_cb_t

Device callback - return true to stop, false to continue

typedef bool (*pappl_device_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data);

pappl_device_t

Device connection object

typedef struct _pappl_device_s pappl_device_t;

pappl_devid_cb_t

Device ID callback

typedef char *(*pappl_devid_cb_t)(pappl_device_t *device, char *buffer, size_t bufsize);

pappl_devlist_cb_t

Device list callback

typedef bool (*pappl_devlist_cb_t)(pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data);

pappl_devmetrics_t

Device metrics

typedef struct pappl_devmetrics_s pappl_devmetrics_t;

pappl_devopen_cb_t

Device open callback

typedef bool (*pappl_devopen_cb_t)(pappl_device_t *device, const char *device_uri, const char *name);

pappl_devread_cb_t

Device read callback

typedef ssize_t (*pappl_devread_cb_t)(pappl_device_t *device, void *buffer, size_t bytes);

pappl_devstatus_cb_t

Device status callback

typedef pappl_preason_t (*pappl_devstatus_cb_t)(pappl_device_t *device);

pappl_devtype_t

Device type bitfield

typedef unsigned pappl_devtype_t;

pappl_devwrite_cb_t

Device write callback

typedef ssize_t (*pappl_devwrite_cb_t)(pappl_device_t *device, const void *buffer, size_t bytes);

pappl_dither_t[16][16]

16x16 dither array

typedef unsigned char pappl_dither_t[16][16];

pappl_duplex_t

Duplex printing support

typedef enum pappl_duplex_e pappl_duplex_t;

pappl_finishings_t

Bitfield for IPP "finishings" values

typedef unsigned pappl_finishings_t;

pappl_icon_t

Printer PNG icon structure

typedef struct pappl_icon_s pappl_icon_t;

pappl_identify_actions_t

Bitfield for IPP "identify-actions" values

typedef unsigned pappl_identify_actions_t;

pappl_ipp_op_cb_t

IPP operation callback function

typedef bool (*pappl_ipp_op_cb_t)(pappl_client_t *client, void *data);

pappl_job_cb_t

papplIterateXxxJobs callback function

typedef void (*pappl_job_cb_t)(pappl_job_t *job, void *data);

pappl_job_t

Job object

typedef struct _pappl_job_s pappl_job_t;

pappl_jreason_t

Bitfield for IPP "job-state-reasons" values

typedef unsigned int pappl_jreason_t;

pappl_kind_t

Bitfield for IPP "printer-kind" values

typedef unsigned pappl_kind_t;

pappl_label_mode_t

Bitfield for IPP "label-mode-xxx" values

typedef unsigned short pappl_label_mode_t;

pappl_loglevel_t

Log levels

typedef enum pappl_loglevel_e pappl_loglevel_t;

pappl_loptions_t

Bitfield for link options

typedef unsigned short pappl_loptions_t;

pappl_media_col_t

Media details structure

typedef struct pappl_media_col_s pappl_media_col_t;

pappl_media_tracking_t

Bitfield for IPP "media-tracking" values

typedef unsigned short pappl_media_tracking_t;

pappl_mime_cb_t

MIME typing callback function

typedef const char *(*pappl_mime_cb_t)(const unsigned char *header, size_t headersize, void *data);

pappl_mime_filter_cb_t

Filter callback function

typedef bool (*pappl_mime_filter_cb_t)(pappl_job_t *job, pappl_device_t *device, void *data);

pappl_ml_subcmd_cb_t

Sub-command callback

typedef int (*pappl_ml_subcmd_cb_t)(const char *base_name, int num_options, cups_option_t *options, int num_files, char **files, void *data);

pappl_ml_system_cb_t

System callback

typedef pappl_system_t *(*pappl_ml_system_cb_t)(int num_options, cups_option_t *options, void *data);

pappl_ml_usage_cb_t

Program usage callback

typedef void (*pappl_ml_usage_cb_t)(void *data);

pappl_pr_autoadd_cb_t

Auto-add callback

typedef const char *(*pappl_pr_autoadd_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data);

pappl_pr_create_cb_t

Printer creation callback

typedef void (*pappl_pr_create_cb_t)(pappl_printer_t *printer, void *data);

pappl_pr_delete_cb_t

Printer deletion callback

typedef void (*pappl_pr_delete_cb_t)(pappl_printer_t *printer, pappl_pr_driver_data_t *data);

pappl_pr_driver_cb_t

Driver callback function

typedef bool (*pappl_pr_driver_cb_t)(pappl_system_t *system, const char *driver_name, const char *device_uri, const char *device_id, pappl_pr_driver_data_t *driver_data, ipp_t **driver_attrs, void *data);

pappl_pr_driver_data_t

Print driver data

typedef struct pappl_pr_driver_data_s pappl_pr_driver_data_t;

pappl_pr_driver_t

Printer driver information

typedef struct pappl_pr_driver_s pappl_pr_driver_t;

pappl_pr_identify_cb_t

Identify-Printer callback

typedef void (*pappl_pr_identify_cb_t)(pappl_printer_t *printer, pappl_identify_actions_t actions, const char *message);

pappl_pr_options_t

Combined print job options

typedef struct pappl_pr_options_s pappl_pr_options_t;

pappl_pr_printfile_cb_t

Print a "raw" job callback

typedef bool (*pappl_pr_printfile_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device);

pappl_pr_rendjob_cb_t

End a raster job callback

typedef bool (*pappl_pr_rendjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device);

pappl_pr_rendpage_cb_t

End a raster page callback

typedef bool (*pappl_pr_rendpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page);

pappl_pr_rstartjob_cb_t

Start a raster job callback

typedef bool (*pappl_pr_rstartjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device);

pappl_pr_rstartpage_cb_t

Start a raster page callback

typedef bool (*pappl_pr_rstartpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page);

pappl_pr_rwriteline_cb_t

Write a line of raster graphics callback

typedef bool (*pappl_pr_rwriteline_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned y, const unsigned char *line);

pappl_pr_status_cb_t

Update printer status callback

typedef bool (*pappl_pr_status_cb_t)(pappl_printer_t *printer);

pappl_pr_testpage_cb_t

Print a test page callback

typedef const char *(*pappl_pr_testpage_cb_t)(pappl_printer_t *printer, char *buffer, size_t bufsize);

pappl_preason_t

Bitfield for IPP "printer-state-reasons" values

typedef unsigned int pappl_preason_t;

pappl_printer_cb_t

Printer iterator callback function

typedef void (*pappl_printer_cb_t)(pappl_printer_t *printer, void *data);

pappl_printer_t

Printer object

typedef struct _pappl_printer_s pappl_printer_t;

pappl_raster_type_t

Bitfield for IPP "pwg-raster-document-type-supported" values

typedef unsigned pappl_raster_type_t;

pappl_resource_cb_t

Dynamic resource callback function

typedef bool (*pappl_resource_cb_t)(pappl_client_t *client, void *data);

pappl_save_cb_t

Save callback function

typedef bool (*pappl_save_cb_t)(pappl_system_t *system, void *data);

pappl_scaling_t

Bitfield for IPP "print-scaling" values

typedef unsigned pappl_scaling_t;

pappl_sides_t

Bitfield for IPP "sides" values

typedef unsigned pappl_sides_t;

pappl_soptions_t

Bitfield for system options

typedef unsigned pappl_soptions_t;

pappl_supply_color_t

"printer-supply" color values

typedef enum pappl_supply_color_e pappl_supply_color_t;

pappl_supply_t

Supply data

typedef struct pappl_supply_s pappl_supply_t;

pappl_supply_type_t

IPP "printer-supply" type values

typedef enum pappl_supply_type_e pappl_supply_type_t;

pappl_system_t

System object

typedef struct _pappl_system_s pappl_system_t;

pappl_uoptions_t

USB gadget options bitfield

typedef unsigned pappl_uoptions_t;

pappl_version_t

Firmware version information

typedef struct pappl_version_s pappl_version_t;

Structures

pappl_contact_s

Contact information

struct pappl_contact_s {
    char email[256];
    char name[256];
    char telephone[256];
};

Members

email[256] Contact email address
name[256] Contact name
telephone[256] Contact phone number

pappl_devmetrics_s

Device metrics

struct pappl_devmetrics_s {
    size_t read_bytes;
    size_t read_msecs;
    size_t read_requests;
    size_t status_msecs;
    size_t status_requests;
    size_t write_bytes;
    size_t write_msecs;
    size_t write_requests;
};

Members

read_bytes Total number of bytes read
read_msecs Total number of milliseconds spent reading
read_requests Total number of read requests
status_msecs Total number of milliseconds spent getting status
status_requests Total number of status requests
write_bytes Total number of bytes written
write_msecs Total number of milliseconds spent writing
write_requests Total number of write requests

pappl_icon_s

Printer PNG icon structure

struct pappl_icon_s {
    const void *data;
    size_t datalen;
    char filename[256];
};

Members

data PNG icon data, if any
datalen Size of PNG icon data
filename[256] External filename, if any

pappl_media_col_s

Media details structure

struct pappl_media_col_s {
    int bottom_margin;
    int left_margin;
    int left_offset;
    int right_margin;
    int size_length;
    char size_name[64];
    int size_width;
    char source[64];
    int top_margin;
    int top_offset;
    pappl_media_tracking_t tracking;
    char type[64];
};

Members

bottom_margin Bottom margin in hundredths of millimeters
left_margin Left margin in hundredths of millimeters
left_offset Left offset in hundredths of millimeters
right_margin Right margin in hundredths of millimeters
size_length Height in hundredths of millimeters
size_name[64] PWG media size name
size_width Width in hundredths of millimeters
source[64] PWG media source name
top_margin Top margin in hundredths of millimeters
top_offset Top offset in hundredths of millimeters
tracking Media tracking
type[64] PWG media type name

pappl_pr_driver_data_s

Printer driver data

struct pappl_pr_driver_data_s {
    const char *bin[PAPPL_MAX_BIN];
    int bin_default;
    bool borderless;
    int bottom_top;
    pappl_color_mode_t color_default;
    pappl_color_mode_t color_supported;
    pappl_content_t content_default;
    int darkness_configured;
    int darkness_default;
    int darkness_supported;
    pappl_pr_delete_cb_t delete_cb;
    pappl_duplex_t duplex;
    void *extension;
    const char *features[PAPPL_MAX_VENDOR];
    pappl_finishings_t finishings;
    pappl_raster_type_t force_raster_type;
    const char *format;
    pappl_dither_t gdither;
    bool has_supplies;
    pappl_icon_t icons[3];
    pappl_pr_identify_cb_t identify_cb;
    pappl_identify_actions_t identify_default;
    pappl_identify_actions_t identify_supported;
    bool input_face_up;
    pappl_kind_t kind;
    int left_offset_supported[2];
    int left_right;
    char make_and_model[128];
    const char *media[PAPPL_MAX_MEDIA];
    pappl_media_col_t media_default;
    pappl_media_col_t media_ready[PAPPL_MAX_SOURCE];
    pappl_label_mode_t mode_configured;
    pappl_label_mode_t mode_supported;
    int num_bin;
    int num_features;
    int num_media;
    int num_resolution;
    int num_source;
    int num_type;
    int num_vendor;
    ipp_orient_t orient_default;
    bool output_face_up;
    pappl_dither_t pdither;
    int ppm;
    int ppm_color;
    pappl_pr_printfile_cb_t printfile_cb;
    ipp_quality_t quality_default;
    pappl_raster_type_t raster_types;
    pappl_pr_rendjob_cb_t rendjob_cb;
    pappl_pr_rendpage_cb_t rendpage_cb;
    pappl_pr_rstartjob_cb_t rstartjob_cb;
    pappl_pr_rstartpage_cb_t rstartpage_cb;
    pappl_pr_rwriteline_cb_t rwriteline_cb;
    pappl_scaling_t scaling_default;
    pappl_sides_t sides_default;
    pappl_sides_t sides_supported;
    const char *source[PAPPL_MAX_SOURCE];
    int speed_default;
    int speed_supported[2];
    pappl_pr_status_cb_t status_cb;
    int tear_offset_configured;
    int tear_offset_supported[2];
    pappl_pr_testpage_cb_t testpage_cb;
    int top_offset_supported[2];
    pappl_media_tracking_t tracking_supported;
    const char *type[PAPPL_MAX_TYPE];
    const char *vendor[PAPPL_MAX_VENDOR];
    int x_default;
    int x_resolution[PAPPL_MAX_RESOLUTION];
    int y_default;
    int y_resolution[PAPPL_MAX_RESOLUTION];
};

Members

bin[PAPPL_MAX_BIN] Output bins
bin_default Default output bin
borderless Borderless margins supported?
bottom_top Bottom and top margins in hundredths of millimeters
color_default "print-color-mode-default" value
color_supported "print-color-mode" values
content_default "print-content-default" value
darkness_configured printer-darkness-configured
darkness_default print-darkness-default
darkness_supported printer/print-darkness-supported (0 for none)
delete_cb Printer deletion callback
duplex Duplex printing modes supported
extension Extension data (managed by driver)
features[PAPPL_MAX_VENDOR] "ipp-features-supported" values
finishings "finishings-supported" values
force_raster_type Force a particular raster type?
format Printer-specific format
gdither 'auto', 'text', and 'graphic' dither array
has_supplies Printer has supplies to report
icons[3] "printer-icons" values
identify_cb Identify-Printer callback
identify_default "identify-actions-default" values
identify_supported "identify-actions-supported" values
input_face_up Does input media come in face-up?
kind "printer-kind" values
left_offset_supported[2] media-left-offset-supported (0,0 for none)
left_right Left and right margins in hundredths of millimeters
make_and_model[128] "printer-make-and-model" value
media[PAPPL_MAX_MEDIA] Supported media
media_default Default media
media_ready[PAPPL_MAX_SOURCE] Ready media
mode_configured label-mode-configured
mode_supported label-mode-supported
num_bin Number of output bins
num_features Number of "ipp-features-supported" values
num_media Number of supported media
num_resolution Number of printer resolutions
num_source Number of media sources (trays/rolls)
num_type Number of media types
num_vendor Number of vendor attributes
orient_default "orientation-requested-default" value
output_face_up Does output media come out face-up?
pdither 'photo' dither array
ppm "pages-per-minute" value
ppm_color "pages-per-minute-color" value, if any
printfile_cb Print (raw) file callback
quality_default "print-quality-default" value
raster_types "pwg-raster-document-type-supported" values
rendjob_cb End raster job callback
rendpage_cb End raster page callback
rstartjob_cb Start raster job callback
rstartpage_cb Start raster page callback
rwriteline_cb Write raster line callback
scaling_default "print-scaling-default" value
sides_default "sides-default" value
sides_supported "sides-supported" values
source[PAPPL_MAX_SOURCE] Media sources
speed_default print-speed-default
speed_supported[2] print-speed-supported (0,0 for none)
status_cb Status callback
tear_offset_configured label-tear-offset-configured
tear_offset_supported[2] label-tear-offset-supported (0,0 for none)
testpage_cb Test page callback
top_offset_supported[2] media-top-offset-supported (0,0 for none)
tracking_supported media-tracking-supported
type[PAPPL_MAX_TYPE] Media types
vendor[PAPPL_MAX_VENDOR] Vendor attribute names
x_default Default horizontal resolution
x_resolution[PAPPL_MAX_RESOLUTION] Horizontal printer resolutions
y_default Default vertical resolution
y_resolution[PAPPL_MAX_RESOLUTION] Vertical printer resolutions

pappl_pr_driver_s

Printer driver information

struct pappl_pr_driver_s {
    const char *description;
    const char *device_id;
    void *extension;
    const char *name;
};

Members

description Driver description (usually the make and model)
device_id IEEE-1284 device ID
extension Extension data pointer
name Driver name

pappl_pr_options_s

Combined print job options

struct pappl_pr_options_s {
    int copies;
    int darkness_configured;
    pappl_dither_t dither;
    pappl_finishings_t finishings;
    unsigned first_page;
    cups_page_header2_t header;
    unsigned last_page;
    pappl_media_col_t media;
    unsigned num_pages;
    int num_vendor;
    ipp_orient_t orientation_requested;
    char output_bin[64];
    pappl_color_mode_t print_color_mode;
    pappl_content_t print_content_optimize;
    int print_darkness;
    ipp_quality_t print_quality;
    pappl_scaling_t print_scaling;
    int print_speed;
    int printer_resolution[2];
    pappl_sides_t sides;
    cups_option_t *vendor;
};

Members

copies "copies" value
darkness_configured "printer-darkness-configured" value
dither Dither array, if any
finishings "finishings" value(s)
first_page First page in page-ranges, starting at 1
header Raster header
last_page Last page in page-ranges, starting at 1
media "media"/"media-col" value
num_pages Number of pages in job
num_vendor Number of vendor options
orientation_requested "orientation-requested" value
output_bin[64] "output-bin" value
print_color_mode "print-color-mode" value
print_content_optimize "print-content-optimize" value
print_darkness "print-darkness" value
print_quality "print-quality" value
print_scaling "print-scaling" value
print_speed "print-speed" value
printer_resolution[2] "printer-resolution" value in dots per inch
sides "sides" value
vendor Vendor options

pappl_supply_s

Supply data

struct pappl_supply_s {
    pappl_supply_color_t color;
    char description[256];
    bool is_consumed;
    int level;
    pappl_supply_type_t type;
};

Members

color Color, if any
description[256] Description
is_consumed Is this a supply that is consumed?
level Level (0-100, -1 = unknown)
type Type

pappl_version_s

Firmware version information

struct pappl_version_s {
    char name[64];
    char patches[64];
    char sversion[64];
    unsigned short version[4];
};

Members

name[64] "xxx-firmware-name" value
patches[64] "xxx-firmware-patches" value
sversion[64] "xxx-firmware-string-version" value
version[4] "xxx-firmware-version" value

Constants

pappl_color_mode_e

IPP "print-color-mode" bit values

Constants

PAPPL_COLOR_MODE_AUTO 'auto' - Automatic color/monochrome print mode
PAPPL_COLOR_MODE_AUTO_MONOCHROME 'auto-monochrome' - Automatic monochrome/process monochrome print mode
PAPPL_COLOR_MODE_BI_LEVEL 'bi-level' - B&W (threshold) print mode
PAPPL_COLOR_MODE_COLOR 'color' - Full color print mode
PAPPL_COLOR_MODE_MONOCHROME 'monochrome' - Grayscale print mode using 1 color
PAPPL_COLOR_MODE_PROCESS_MONOCHROME 'process-monochrome' - Grayscale print mode using multiple colors

pappl_content_e

IPP "print-content-optimize" bit values

Constants

PAPPL_CONTENT_AUTO 'auto': Automatically determine content
PAPPL_CONTENT_GRAPHIC 'graphic': Optimize for vector graphics
PAPPL_CONTENT_PHOTO 'photo': Optimize for photos/continuous tone images
PAPPL_CONTENT_TEXT 'text': Optimize for text
PAPPL_CONTENT_TEXT_AND_GRAPHIC 'text-and-graphic': Optimize for text and vector graphics

pappl_devtype_e

Device type bit values

Constants

PAPPL_DEVTYPE_ALL All printers
PAPPL_DEVTYPE_CUSTOM_LOCAL Local printer using a custom interface or protocol
PAPPL_DEVTYPE_CUSTOM_NETWORK Network printer using a custom interface or protocol
PAPPL_DEVTYPE_DNS_SD Network printers discovered via DNS-SD/mDNS
PAPPL_DEVTYPE_FILE Local file/directory
PAPPL_DEVTYPE_LOCAL All local printers
PAPPL_DEVTYPE_NETWORK All network printers
PAPPL_DEVTYPE_SNMP Network printers discovered via SNMP
PAPPL_DEVTYPE_SOCKET Network printers using raw socket
PAPPL_DEVTYPE_USB USB printers

pappl_duplex_e

Duplex printing support

Constants

PAPPL_DUPLEX_FLIPPED Duplex with flipped back sides
PAPPL_DUPLEX_MANUAL_TUMBLE Duplex with back side rotated 180 degrees for short-edge duplex
PAPPL_DUPLEX_NONE No duplex printing support
PAPPL_DUPLEX_NORMAL Duplex with normal back sides
PAPPL_DUPLEX_ROTATED Duplex with back side rotated 180 degrees for long-edge duplex

pappl_finishings_e

IPP "finishings" bit values

Constants

PAPPL_FINISHINGS_NONE 'none'
PAPPL_FINISHINGS_PUNCH 'punch'
PAPPL_FINISHINGS_STAPLE 'staple'
PAPPL_FINISHINGS_TRIM 'trim'

pappl_identify_actions_e

IPP "identify-actions" bit values

Constants

PAPPL_IDENTIFY_ACTIONS_DISPLAY 'display': Display a message
PAPPL_IDENTIFY_ACTIONS_FLASH 'flash': Flash the display or a light
PAPPL_IDENTIFY_ACTIONS_NONE No actions
PAPPL_IDENTIFY_ACTIONS_SOUND 'sound': Make a sound
PAPPL_IDENTIFY_ACTIONS_SPEAK 'speak': Speak a message

pappl_jreason_e

IPP "job-state-reasons" bit values

Constants

PAPPL_JREASON_ABORTED_BY_SYSTEM 'aborted-by-system'
PAPPL_JREASON_COMPRESSION_ERROR 'compression-error'
PAPPL_JREASON_DOCUMENT_FORMAT_ERROR 'document-format-error'
PAPPL_JREASON_DOCUMENT_PASSWORD_ERROR 'document-password-error'
PAPPL_JREASON_DOCUMENT_PERMISSION_ERROR 'document-permission-error'
PAPPL_JREASON_DOCUMENT_UNPRINTABLE_ERROR 'document-unprintable-error'
PAPPL_JREASON_ERRORS_DETECTED 'errors-detected'
PAPPL_JREASON_JOB_CANCELED_AT_DEVICE 'job-canceled-at-device'
PAPPL_JREASON_JOB_CANCELED_BY_USER 'job-canceled-by-user'
PAPPL_JREASON_JOB_COMPLETED_SUCCESSFULLY 'job-completed-successfully'
PAPPL_JREASON_JOB_COMPLETED_WITH_ERRORS 'job-completed-with-errors'
PAPPL_JREASON_JOB_COMPLETED_WITH_WARNINGS 'job-completed-with-warnings'
PAPPL_JREASON_JOB_DATA_INSUFFICIENT 'job-data-insufficient'
PAPPL_JREASON_JOB_INCOMING 'job-incoming'
PAPPL_JREASON_JOB_PRINTING 'job-printing'
PAPPL_JREASON_JOB_QUEUED 'job-queued'
PAPPL_JREASON_JOB_SPOOLING 'job-spooling'
PAPPL_JREASON_NONE 'none'
PAPPL_JREASON_PRINTER_STOPPED 'printer-stopped'
PAPPL_JREASON_PRINTER_STOPPED_PARTLY 'printer-stopped-partly'
PAPPL_JREASON_PROCESSING_TO_STOP_POINT 'processing-to-stop-point'
PAPPL_JREASON_QUEUED_IN_DEVICE 'queued-in-device'
PAPPL_JREASON_WARNINGS_DETECTED 'warnings-detected'

pappl_kind_e

IPP "printer-kind" bit values

Constants

PAPPL_KIND_DISC 'disc'
PAPPL_KIND_DOCUMENT 'document'
PAPPL_KIND_ENVELOPE 'envelope'
PAPPL_KIND_LABEL 'label'
PAPPL_KIND_LARGE_FORMAT 'large-format'
PAPPL_KIND_PHOTO 'photo'
PAPPL_KIND_POSTCARD 'postcard'
PAPPL_KIND_RECEIPT 'receipt'
PAPPL_KIND_ROLL 'roll'

pappl_label_mode_e

IPP "label-mode-xxx" bit values

Constants

PAPPL_LABEL_MODE_APPLICATOR 'applicator'
PAPPL_LABEL_MODE_CUTTER 'cutter'
PAPPL_LABEL_MODE_CUTTER_DELAYED 'cutter-delayed'
PAPPL_LABEL_MODE_KIOSK 'kiosk'
PAPPL_LABEL_MODE_PEEL_OFF 'peel-off'
PAPPL_LABEL_MODE_PEEL_OFF_PREPEEL 'peel-off-prepeel'
PAPPL_LABEL_MODE_REWIND 'rewind'
PAPPL_LABEL_MODE_RFID 'rfid'
PAPPL_LABEL_MODE_TEAR_OFF 'tear-off'

pappl_loglevel_e

Log levels

Constants

PAPPL_LOGLEVEL_DEBUG Debug message
PAPPL_LOGLEVEL_ERROR Error message
PAPPL_LOGLEVEL_FATAL Fatal message
PAPPL_LOGLEVEL_INFO Informational message
PAPPL_LOGLEVEL_UNSPEC Not specified
PAPPL_LOGLEVEL_WARN Warning message

pappl_loptions_e

Link option bits

Constants

PAPPL_LOPTIONS_CONFIGURATION Link shown in configuration section
PAPPL_LOPTIONS_HTTPS_REQUIRED Link requires HTTPS
PAPPL_LOPTIONS_JOB Link shown in job(s) section
PAPPL_LOPTIONS_LOGGING Link shown in logging section
PAPPL_LOPTIONS_NAVIGATION Link shown in navigation bar
PAPPL_LOPTIONS_NETWORK Link shown in network section
PAPPL_LOPTIONS_OTHER Link shown in other section
PAPPL_LOPTIONS_PRINTER Link shown in printer(s) section
PAPPL_LOPTIONS_SECURITY Link shown in security section
PAPPL_LOPTIONS_STATUS Link shown in status section
PAPPL_LOPTIONS_TLS Link shown in TLS section

pappl_media_tracking_e

IPP "media-tracking" bit values

Constants

PAPPL_MEDIA_TRACKING_CONTINUOUS 'continuous'
PAPPL_MEDIA_TRACKING_GAP 'gap'
PAPPL_MEDIA_TRACKING_MARK 'mark'
PAPPL_MEDIA_TRACKING_WEB 'web'

pappl_preason_e

IPP "printer-state-reasons" bit values

Constants

PAPPL_PREASON_COVER_OPEN 'cover-open'
PAPPL_PREASON_DEVICE_STATUS Supported papplDeviceGetStatus bits
PAPPL_PREASON_INPUT_TRAY_MISSING 'input-tray-missing'
PAPPL_PREASON_MARKER_SUPPLY_EMPTY 'marker-supply-empty'
PAPPL_PREASON_MARKER_SUPPLY_LOW 'marker-supply-low'
PAPPL_PREASON_MARKER_WASTE_ALMOST_FULL 'marker-waste-almost-full'
PAPPL_PREASON_MARKER_WASTE_FULL 'marker-waste-full'
PAPPL_PREASON_MEDIA_EMPTY 'media-empty'
PAPPL_PREASON_MEDIA_JAM 'media-jam'
PAPPL_PREASON_MEDIA_LOW 'media-low'
PAPPL_PREASON_MEDIA_NEEDED 'media-needed'
PAPPL_PREASON_NONE 'none'
PAPPL_PREASON_OFFLINE 'offline'
PAPPL_PREASON_OTHER 'other'
PAPPL_PREASON_SPOOL_AREA_FULL 'spool-area-full'
PAPPL_PREASON_TONER_EMPTY 'toner-empty'
PAPPL_PREASON_TONER_LOW 'toner-low'

pappl_raster_type_e

IPP "pwg-raster-document-type-supported" bit values

Constants

PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_16 16-bit per component AdobeRGB
PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_8 8-bit per component AdobeRGB
PAPPL_PWG_RASTER_TYPE_BLACK_1 1-bit (device) black
PAPPL_PWG_RASTER_TYPE_BLACK_16 16-bit (device) black
PAPPL_PWG_RASTER_TYPE_BLACK_8 8-bit (device) black
PAPPL_PWG_RASTER_TYPE_CMYK_16 16-bit per component (device) CMYK
PAPPL_PWG_RASTER_TYPE_CMYK_8 8-bit per component (device) CMYK
PAPPL_PWG_RASTER_TYPE_NONE Do not force a particular raster type
PAPPL_PWG_RASTER_TYPE_RGB_16 16-bit per component (device) RGB)
PAPPL_PWG_RASTER_TYPE_RGB_8 8-bit per component (device) RGB
PAPPL_PWG_RASTER_TYPE_SGRAY_16 16-bit grayscale with 2.2 gamma
PAPPL_PWG_RASTER_TYPE_SGRAY_8 8-bit grayscale with 2.2 gamma
PAPPL_PWG_RASTER_TYPE_SRGB_16 16-bit per component sRGB
PAPPL_PWG_RASTER_TYPE_SRGB_8 8-bit per component sRGB

pappl_scaling_e

IPP "print-scaling" bit values

Constants

PAPPL_SCALING_AUTO 'auto': Scale to fit (non-borderless) or fill (borderless) if larger, otherwise center
PAPPL_SCALING_AUTO_FIT 'auto-fit': Scale to fit if larger, otherwise center
PAPPL_SCALING_FILL 'fill': Scale to fill the media
PAPPL_SCALING_FIT 'fit': Scale to fit within margins
PAPPL_SCALING_NONE 'none': No scaling (center/crop)

pappl_sides_e

IPP "sides" bit values

Constants

PAPPL_SIDES_ONE_SIDED 'one-sided'
PAPPL_SIDES_TWO_SIDED_LONG_EDGE 'two-sided-long-edge'
PAPPL_SIDES_TWO_SIDED_SHORT_EDGE 'two-sided-short-edge'

pappl_soptions_e

System option bits

Constants

PAPPL_SOPTIONS_DNSSD_HOST Use hostname in DNS-SD service names instead of serial number/UUID
PAPPL_SOPTIONS_MULTI_QUEUE Support multiple printers
PAPPL_SOPTIONS_NONE No options
PAPPL_SOPTIONS_RAW_SOCKET Accept jobs via raw sockets
PAPPL_SOPTIONS_USB_PRINTER Accept jobs via USB for default printer (embedded Linux only)
PAPPL_SOPTIONS_WEB_INTERFACE Enable the standard web pages
PAPPL_SOPTIONS_WEB_LOG Enable the log file page
PAPPL_SOPTIONS_WEB_NETWORK Enable the network settings page
PAPPL_SOPTIONS_WEB_REMOTE Allow remote queue management (vs. localhost only)
PAPPL_SOPTIONS_WEB_SECURITY Enable the user/password settings page
PAPPL_SOPTIONS_WEB_TLS Enable the TLS settings page

pappl_supply_color_e

"printer-supply" color values

Constants

PAPPL_SUPPLY_COLOR_BLACK Black ink/toner (photo or matte)
PAPPL_SUPPLY_COLOR_CYAN Cyan ink/toner
PAPPL_SUPPLY_COLOR_GRAY Gray ink (sometimes marketed as light gray)
PAPPL_SUPPLY_COLOR_GREEN Green ink
PAPPL_SUPPLY_COLOR_LIGHT_CYAN Light cyan ink
PAPPL_SUPPLY_COLOR_LIGHT_GRAY Light gray ink (sometimes marketed as light light gray)
PAPPL_SUPPLY_COLOR_LIGHT_MAGENTA Light magenta ink
PAPPL_SUPPLY_COLOR_MAGENTA Magenta ink/toner
PAPPL_SUPPLY_COLOR_NO_COLOR No color (waste tank, etc.)
PAPPL_SUPPLY_COLOR_ORANGE Orange ink
PAPPL_SUPPLY_COLOR_VIOLET Violet ink
PAPPL_SUPPLY_COLOR_YELLOW Yellow ink/toner

pappl_supply_type_e

IPP "printer-supply" type values

Constants

PAPPL_SUPPLY_TYPE_BANDING_SUPPLY Banding finisher supplies
PAPPL_SUPPLY_TYPE_BINDING_SUPPLY Binding finisher supplies
PAPPL_SUPPLY_TYPE_CLEANER_UNIT Cleaning unit
PAPPL_SUPPLY_TYPE_CORONA_WIRE Corona wire (laser printers)
PAPPL_SUPPLY_TYPE_COVERS Cover finisher supplies
PAPPL_SUPPLY_TYPE_DEVELOPER Developer supply
PAPPL_SUPPLY_TYPE_FUSER Fuser (laser printers)
PAPPL_SUPPLY_TYPE_FUSER_CLEANING_PAD Fuser cleaning pad (laser printers)
PAPPL_SUPPLY_TYPE_FUSER_OIL Fuser oil supply (laser printers)
PAPPL_SUPPLY_TYPE_FUSER_OILER Fuser oiler (laser printers)
PAPPL_SUPPLY_TYPE_FUSER_OIL_WICK Fuser oil wick (laser printers)
PAPPL_SUPPLY_TYPE_INK Ink supply
PAPPL_SUPPLY_TYPE_INK_CARTRIDGE Ink cartridge
PAPPL_SUPPLY_TYPE_INK_RIBBON Ink ribbon supply
PAPPL_SUPPLY_TYPE_INSERTS Insert finisher supplies
PAPPL_SUPPLY_TYPE_OPC Optical photoconductor (laser printers)
PAPPL_SUPPLY_TYPE_PAPER_WRAP Wrap finisher supplies
PAPPL_SUPPLY_TYPE_RIBBON_WAX Wax ribbon supply
PAPPL_SUPPLY_TYPE_SOLID_WAX Solid wax supply
PAPPL_SUPPLY_TYPE_STAPLES Staple finisher supplies
PAPPL_SUPPLY_TYPE_STITCHING_WIRE Staple/stitch finisher supplies
PAPPL_SUPPLY_TYPE_TONER Toner supply
PAPPL_SUPPLY_TYPE_TONER_CARTRIDGE Toner cartridge
PAPPL_SUPPLY_TYPE_TRANSFER_UNIT Transfer unit (laser printers)
PAPPL_SUPPLY_TYPE_WASTE_INK Waste ink
PAPPL_SUPPLY_TYPE_WASTE_TONER Waste toner
PAPPL_SUPPLY_TYPE_WASTE_WATER Waste water
PAPPL_SUPPLY_TYPE_WASTE_WAX Waste wax
PAPPL_SUPPLY_TYPE_WATER Water supply

pappl_uoptions_e

USB gadget options

Constants

PAPPL_UOPTIONS_ETHERNET Include USB ethernet gadget
PAPPL_UOPTIONS_NONE No options (just USB printer)
PAPPL_UOPTIONS_SERIAL Include USB serial gadget
PAPPL_UOPTIONS_STORAGE Include USB mass storage gadget
PAPPL_UOPTIONS_STORAGE_READONLY USB mass storage gadget is read-only
PAPPL_UOPTIONS_STORAGE_REMOVABLE USB mass storage gadget is removable
pappl-1.0.3/install-sh000077500000000000000000000127041403603036100146330ustar00rootroot00000000000000#!/bin/sh # # Install a program, script, or datafile. # # Copyright 2008-2012 by Apple Inc. # # This script is not compatible with BSD (or any other) install program, as it # allows owner and group changes to fail with a warning and makes sure that the # destination directory permissions are as specified - BSD install and the # original X11 install script did not change permissions of existing # directories. It also does not support the transform options since CUPS does # not use them... # # Original script from X11R5 (mit/util/scripts/install.sh) # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # Force umask to 022... umask 022 # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" gzipprog="${GZIPPROG-gzip}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" gzipcp() { # gzipcp from to $gzipprog -9 <"$1" >"$2" } while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue ;; -d) dir_arg=true shift continue ;; -m) chmodcmd="$chmodprog $2" shift shift continue ;; -o) chowncmd="$chownprog $2" shift shift continue ;; -g) chgrpcmd="$chgrpprog $2" shift shift continue ;; -s) stripcmd="$stripprog" shift continue ;; -z) instcmd="gzipcp" shift continue ;; *) if [ x"$src" = x ]; then src="$1" else dst="$1" fi shift continue ;; esac done if [ x"$src" = x ]; then echo "install-sh: No input file specified" exit 1 fi if [ x"$dir_arg" != x ]; then dst="$src" src="" if [ -d "$dst" ]; then instcmd=: else instcmd=$mkdirprog fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ ! -f "$src" -a ! -d "$src" ]; then echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ]; then echo "install: No destination specified" exit 1 fi # If destination is a directory, append the input filename. if [ -d "$dst" ]; then dst="$dst/`basename $src`" fi fi ## this sed command emulates the dirname command dstdir="`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`" # Make sure that the destination directory exists. # This part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ]; then $doit $mkdirprog "${pathcomp}"; fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ]; then # Make a directory... $doit $instcmd $dst || exit 1 # Allow chown/chgrp to fail, but log a warning if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst || echo "warning: Unable to change owner of $dst!"; fi if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst || echo "warning: Unable to change group of $dst!"; fi if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst || exit 1; fi else # Install a file... dstfile="`basename $dst`" # Check the destination file - for libraries just use the "-x" option # to strip... case "$dstfile" in *.a | *.dylib | *.sl | *.sl.* | *.so | *.so.*) stripopt="-x" ;; *) stripopt="" ;; esac # Make a temp file name in the proper directory. dsttmp="$dstdir/#inst.$$#" # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp || exit 1 # Update permissions and strip as needed, then move to the final name. # If the chmod, strip, rm, or mv commands fail, remove the installed # file... if [ x"$stripcmd" != x ]; then $doit $stripcmd $stripopt "$dsttmp" || echo "warning: Unable to strip $dst!"; fi if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp" || echo "warning: Unable to change owner of $dst!"; fi if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp" || echo "warning: Unable to change group of $dst!"; fi trap "rm -f ${dsttmp}" 0 && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; fi && $doit $rmcmd -f "$dstdir/$dstfile" && $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" fi exit 0 pappl-1.0.3/man/000077500000000000000000000000001403603036100133765ustar00rootroot00000000000000pappl-1.0.3/man/Makefile000066400000000000000000000020021403603036100150300ustar00rootroot00000000000000# # Man page makefile for the Printer Application Framework # # Copyright © 2020 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # include ../Makedefs MAN1 = \ pappl.1 \ pappl-makeresheader.1 MAN3 = \ pappl-client.3 \ pappl-device.3 \ pappl-job.3 \ pappl-log.3 \ pappl-mainloop.3 \ pappl-printer.3 \ pappl-resource.3 \ pappl-system.3 # Make everything all: # Clean everything clean: # Clean all non-distribution files distclean: clean # Update dependencies depend: # Install everything install: echo Installing man pages in $(BUILDROOT)/$(mandir)/man1... $(INSTALL) -d -m 755 $(BUILDROOT)/$(mandir)/man1 for file in $(MAN1); do \ $(INSTALL) -c -m 644 $$file $(BUILDROOT)/$(mandir)/man1; \ done echo Installing man pages in $(BUILDROOT)/$(mandir)/man3... $(INSTALL) -d -m 755 $(BUILDROOT)/$(mandir)/man3 for file in $(MAN3); do \ $(INSTALL) -c -m 644 $$file $(BUILDROOT)/$(mandir)/man3; \ done # Test everything test: pappl-1.0.3/man/pappl-client-body.man000066400000000000000000000131561403603036100174240ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_client_s .B pappl_client_t; .PP .I char * .br .BI papplClientGetCSRFToken "(pappl_client_t *client, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplClientGetCookie "(pappl_client_t *client, const char *name, char *buffer, size_t bufsize);" .PP .I int .br .BI papplClientGetForm "(pappl_client_t *client, cups_option_t **form);" .PP .I const char * .br .BI papplClientGetHostName "(pappl_client_t *client);" .PP .I int .br .BI papplClientGetHostPort "(pappl_client_t *client);" .PP .I http_t * .br .BI papplClientGetHTTP "(pappl_client_t *client);" .PP .I pappl_job_t * .br .BI papplClientGetJob "(pappl_client_t *client);" .PP .I http_state_t .br .BI papplClientGetMethod "(pappl_client_t *client);" .PP .I ipp_op_t .br .BI papplClientGetOperation "(pappl_client_t *client);" .PP .I const char * .br .BI papplClientGetOptions "(pappl_client_t *client);" .PP .I pappl_printer_t * .br .BI papplClientGetPrinter "(pappl_client_t *client);" .PP .I ipp_t * .br .BI papplClientGetRequest "(pappl_client_t *client);" .PP .I ipp_t * .br .BI papplClientGetResponse "(pappl_client_t *client);" .PP .I pappl_system_t * .br .BI papplClientGetSystem "(pappl_client_t *client);" .PP .I const char * .br .BI papplClientGetURI "(pappl_client_t *client);" .PP .I const char * .br .BI papplClientGetUsername "(pappl_client_t *client);" .PP .I bool .br .BI papplClientHTMLAuthorize "(pappl_client_t *client);" .PP .I void .br .BI papplClientHTMLEscape "(pappl_client_t *client, const char *s, size_t slen);" .PP .I void .br .BI papplClientHTMLFooter "(pappl_client_t *client);" .PP .I void .br .BI papplClientHTMLHeader "(pappl_client_t *client, const char *title, int refresh);" .PP .I void .br .BI papplClientHTMLPrintf "(pappl_client_t *client, const char *format, ...);" .PP .I void .br .BI papplClientHTMLPuts "(pappl_client_t *client, const char *s);" .PP .I void .br .BI papplClientHTMLStartForm "(pappl_client_t *client, const char *action, bool multipart);" .PP .I http_status_t .br .BI papplClientIsAuthorized "(pappl_client_t *client);" .PP .I bool .br .BI papplClientRespond "(pappl_client_t *client, http_status_t code, const char *content_coding, const char *type, time_t last_modified, size_t length);" .PP .I ipp_t * .br .BI papplClientRespondIPP "(pappl_client_t *client, ipp_status_t status, const char *message, ...);" .PP .I bool .br .BI papplClientRespondRedirect "(pappl_client_t *client, http_status_t code, const char *path);" .PP .I void .br .BI papplClientSetCookie "(pappl_client_t *client, const char * name, const char *value, int expires);" .PP .I bool .br .BI papplClientValidateForm "(pappl_client_t *client, int num_form, cups_option_t *form);" .SH DESCRIPTION The .B PAPPL client functions provide access to client connections. Client connections and the life cycle of the .B pappl_client_t objects are managed automatically by the system object for the printer application. .PP The .B papplClientGet functions get the current values for various client-supplied request data. .SH RESPONDING TO CLIENT REQUESTS The .B papplClientRespond function starts a HTTP response to a client request. The .B papplClientHTMLHeader and .B papplClientHTMLFooter functions send standard HTML headers and footers for the printer application's configured web interface while the .B papplClientHTMLEscape, papplClientHTMLPrintf, papplClientHTMLPuts, and .B papplClientHTMLStartForm functions send HTML messages or strings. Use the .B papplClientGetHTTP and (CUPS) .B httpWrite2 functions to send arbitrary data in a client response. Cookies can be included in web browser requests using the .B papplClientSetCookie function. .PP The .B papplClientRespondIPP function starts an IPP response. Use the various CUPS .B ippAdd functions to add attributes to the response message. .PP The .B papplClientRespondRedirect function sends a redirection response to the client. .SH HTML FORMS .B PAPPL provides the .B papplClientGetCSRFToken, papplClientGetForm, papplClientHTMLStartForm and .B papplClientValidateForm functions to securely manage HTML forms. .PP The .B papplClientHTMLStartForm function starts a HTML form and inserts a hidden variable containing a CSRF token that was generated by .B PAPPL from a secure session key that is periodically updated. Upon receipt of a follow-up form submission request, the .B papplClientGetForm and .B papplClientValidateForm functions can be used to securely read the form data (including any file attachments) and validate the hidden CSRF token. .SH AUTHENTICATION AND AUTHORIZATION .B PAPPL supports both user-based authentication using PAM modules and a simple cookie-based password authentication mechanism that is used to limit administrative access through the web interface. .PP The .B papplHTMLAuthorize function authorizes access to the web interface and handles displaying an authentication form on the client's web browser. The return value indicates whether the client is authorized to access the web page. .PP The .B papplIsAuthorized function can be used to determine whether the current client is authorized to perform administrative operations and is normally only used for IPP clients. Local users are always authorized while remote users must provide credentials (typically a username and password) for access. This function will return an HTTP status code that can be provided to the .B httpClientSendResponse function. The value .I HTTP_STATUS_CONTINUE indicates that authorization is granted and the request should continue. The .B papplGetUsername function can be used to obtain the authenticated user identity. pappl-1.0.3/man/pappl-client.3000066400000000000000000000514621403603036100160620ustar00rootroot00000000000000.TH pappl-client 3 "pappl client functions" "2021-02-15" "pappl client functions" .SH NAME pappl-client \- pappl client functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_client_s .B pappl_client_t; .PP .I char * .br .BI papplClientGetCSRFToken "(pappl_client_t *client, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplClientGetCookie "(pappl_client_t *client, const char *name, char *buffer, size_t bufsize);" .PP .I int .br .BI papplClientGetForm "(pappl_client_t *client, cups_option_t **form);" .PP .I const char * .br .BI papplClientGetHostName "(pappl_client_t *client);" .PP .I int .br .BI papplClientGetHostPort "(pappl_client_t *client);" .PP .I http_t * .br .BI papplClientGetHTTP "(pappl_client_t *client);" .PP .I pappl_job_t * .br .BI papplClientGetJob "(pappl_client_t *client);" .PP .I http_state_t .br .BI papplClientGetMethod "(pappl_client_t *client);" .PP .I ipp_op_t .br .BI papplClientGetOperation "(pappl_client_t *client);" .PP .I const char * .br .BI papplClientGetOptions "(pappl_client_t *client);" .PP .I pappl_printer_t * .br .BI papplClientGetPrinter "(pappl_client_t *client);" .PP .I ipp_t * .br .BI papplClientGetRequest "(pappl_client_t *client);" .PP .I ipp_t * .br .BI papplClientGetResponse "(pappl_client_t *client);" .PP .I pappl_system_t * .br .BI papplClientGetSystem "(pappl_client_t *client);" .PP .I const char * .br .BI papplClientGetURI "(pappl_client_t *client);" .PP .I const char * .br .BI papplClientGetUsername "(pappl_client_t *client);" .PP .I bool .br .BI papplClientHTMLAuthorize "(pappl_client_t *client);" .PP .I void .br .BI papplClientHTMLEscape "(pappl_client_t *client, const char *s, size_t slen);" .PP .I void .br .BI papplClientHTMLFooter "(pappl_client_t *client);" .PP .I void .br .BI papplClientHTMLHeader "(pappl_client_t *client, const char *title, int refresh);" .PP .I void .br .BI papplClientHTMLPrintf "(pappl_client_t *client, const char *format, ...);" .PP .I void .br .BI papplClientHTMLPuts "(pappl_client_t *client, const char *s);" .PP .I void .br .BI papplClientHTMLStartForm "(pappl_client_t *client, const char *action, bool multipart);" .PP .I http_status_t .br .BI papplClientIsAuthorized "(pappl_client_t *client);" .PP .I bool .br .BI papplClientRespond "(pappl_client_t *client, http_status_t code, const char *content_coding, const char *type, time_t last_modified, size_t length);" .PP .I ipp_t * .br .BI papplClientRespondIPP "(pappl_client_t *client, ipp_status_t status, const char *message, ...);" .PP .I bool .br .BI papplClientRespondRedirect "(pappl_client_t *client, http_status_t code, const char *path);" .PP .I void .br .BI papplClientSetCookie "(pappl_client_t *client, const char * name, const char *value, int expires);" .PP .I bool .br .BI papplClientValidateForm "(pappl_client_t *client, int num_form, cups_option_t *form);" .SH DESCRIPTION The .B PAPPL client functions provide access to client connections. Client connections and the life cycle of the .B pappl_client_t objects are managed automatically by the system object for the printer application. .PP The .B papplClientGet functions get the current values for various client-supplied request data. .SH RESPONDING TO CLIENT REQUESTS The .B papplClientRespond function starts a HTTP response to a client request. The .B papplClientHTMLHeader and .B papplClientHTMLFooter functions send standard HTML headers and footers for the printer application's configured web interface while the .B papplClientHTMLEscape, papplClientHTMLPrintf, papplClientHTMLPuts, and .B papplClientHTMLStartForm functions send HTML messages or strings. Use the .B papplClientGetHTTP and (CUPS) .B httpWrite2 functions to send arbitrary data in a client response. Cookies can be included in web browser requests using the .B papplClientSetCookie function. .PP The .B papplClientRespondIPP function starts an IPP response. Use the various CUPS .B ippAdd functions to add attributes to the response message. .PP The .B papplClientRespondRedirect function sends a redirection response to the client. .SH HTML FORMS .B PAPPL provides the .B papplClientGetCSRFToken, papplClientGetForm, papplClientHTMLStartForm and .B papplClientValidateForm functions to securely manage HTML forms. .PP The .B papplClientHTMLStartForm function starts a HTML form and inserts a hidden variable containing a CSRF token that was generated by .B PAPPL from a secure session key that is periodically updated. Upon receipt of a follow-up form submission request, the .B papplClientGetForm and .B papplClientValidateForm functions can be used to securely read the form data (including any file attachments) and validate the hidden CSRF token. .SH AUTHENTICATION AND AUTHORIZATION .B PAPPL supports both user-based authentication using PAM modules and a simple cookie-based password authentication mechanism that is used to limit administrative access through the web interface. .PP The .B papplHTMLAuthorize function authorizes access to the web interface and handles displaying an authentication form on the client's web browser. The return value indicates whether the client is authorized to access the web page. .PP The .B papplIsAuthorized function can be used to determine whether the current client is authorized to perform administrative operations and is normally only used for IPP clients. Local users are always authorized while remote users must provide credentials (typically a username and password) for access. This function will return an HTTP status code that can be provided to the .B httpClientSendResponse function. The value .I HTTP_STATUS_CONTINUE indicates that authorization is granted and the request should continue. The .B papplGetUsername function can be used to obtain the authenticated user identity. .SH FUNCTIONS .SS papplClientGetCSRFToken Get a unique Cross-Site Request Forgery token string. .PP .nf char * papplClientGetCSRFToken ( pappl_client_t *client, char *buffer, size_t bufsize ); .fi .PP This function generates and returns a unique Cross-Site Request Forgery token string to be used as the value of a hidden variable in all HTML forms sent in the response and then compared when validating the form data in the subsequent request. .PP The value is based on the current system session key and client address in order to make replay attacks infeasible. .PP .IP 5 Note: The \fIpapplClientHTMLStartForm\fR function automatically adds the .IP 5 hidden CSRF variable, and the \fIpapplClientIsValidForm\fR function .IP 5 validates the value. .SS papplClientGetCookie Get a cookie from the client. .PP .nf char * papplClientGetCookie ( pappl_client_t *client, const char *name, char *buffer, size_t bufsize ); .fi .PP This function gets a HTTP "cookie" value from the client request. \fBNULL\fR is returned if no cookie has been set by a prior request, or if the user has disabled or removed the cookie. .PP Use the \fIpapplClientSetCookie\fR function to set a cookie in a response to a request. .PP .IP 5 Note: Cookies set with \fIpapplClientSetCookie\fR will not be available to .IP 5 this function until the following request. .SS papplClientGetForm Get form data from the web client. .PP .nf int papplClientGetForm ( pappl_client_t *client, cups_option_t **form ); .fi .PP For HTTP GET requests, the form data is collected from the request URI. For HTTP POST requests, the form data is read from the client. .PP The returned form values must be freed using the \fBcupsFreeOptions\fR function. .PP .IP 5 Note: Because the form data is read from the client connection, this .IP 5 function can only be called once per request. .SS papplClientGetHTTP Get the HTTP connection associated with a client object. .PP .nf http_t * papplClientGetHTTP ( pappl_client_t *client ); .fi .PP This function returns the HTTP connection associated with the client and is used when sending response data directly to the client using the CUPS \fBhttpXxx\fR functions. .SS papplClientGetHostName Get the hostname from the client-supplied Host: field. .PP .nf const char * papplClientGetHostName ( pappl_client_t *client ); .fi .PP This function returns the hostname that was used in the request and should be used in any URLs or URIs that you generate. .SS papplClientGetHostPort Get the port from the client-supplied Host: field. .PP .nf int papplClientGetHostPort ( pappl_client_t *client ); .fi .PP This function returns the port number that was used in the request and should be used in any URLs or URIs that you generate. .SS papplClientGetJob Get the target job for an IPP request. .PP .nf pappl_job_t * papplClientGetJob ( pappl_client_t *client ); .fi .PP This function returns the job associated with the current IPP request. \fBNULL\fR is returned if the request does not target a job. .SS papplClientGetMethod Get the HTTP request method. .PP .nf http_state_t papplClientGetMethod ( pappl_client_t *client ); .fi .PP This function returns the HTTP request method that was used, for example \fBHTTP_STATE_GET\fR for a GET request or \fBHTTP_STATE_POST\fR for a POST request. .SS papplClientGetOperation Get the IPP operation code. .PP .nf ipp_op_t papplClientGetOperation ( pappl_client_t *client ); .fi .PP This function returns the IPP operation code associated with the current IPP request. .SS papplClientGetOptions Get the options from the request URI. .PP .nf const char * papplClientGetOptions ( pappl_client_t *client ); .fi .PP This function returns any options that were passed in the HTTP request URI. The options are the characters after the "?" character, for example a request URI of "/mypage?name=value" will have an options string of "name=value". .PP \fBNULL\fR is returned if the request URI did not contain any options. .PP .IP 5 Note: HTTP GET form variables are normally accessed using the .IP 5 \fIpapplClientGetForm\fR function. This function should only be used when .IP 5 getting non-form data. .SS papplClientGetPrinter Get the target printer for an IPP request. .PP .nf pappl_printer_t * papplClientGetPrinter ( pappl_client_t *client ); .fi .PP This function returns the printer associated with the current IPP request. \fBNULL\fR is returned if the request does not target a printer. .SS papplClientGetRequest Get the IPP request message. .PP .nf ipp_t * papplClientGetRequest ( pappl_client_t *client ); .fi .PP This function returns the attributes in the current IPP request, for use with the CUPS \fBippFindAttribute\fR, \fBippFindNextAttribute\fR, \fBippFirstAttribute\fR, and \fBippNextAttribute\fR functions. .SS papplClientGetResponse Get the IPP response message. .PP .nf ipp_t * papplClientGetResponse ( pappl_client_t *client ); .fi .PP This function returns the attributes in the current IPP response, for use with the CUPS \fBippAddXxx\fR and \fBippSetXxx\fR functions. Use the \fIpapplClientRespondIPP\fR function to set the status code and message, if any. .SS papplClientGetSystem Get the containing system for the client. .PP .nf pappl_system_t * papplClientGetSystem ( pappl_client_t *client ); .fi .PP This function returns the system object that contains the client. .SS papplClientGetURI Get the HTTP request URI. .PP .nf const char * papplClientGetURI ( pappl_client_t *client ); .fi .PP This function returns the URI that was sent in the current HTTP request. .PP .IP 5 Note: Any options in the URI are removed and can be accessed separately .IP 5 using the \fIpapplClientGetOptions\fR function. .SS papplClientGetUsername Get the authenticated username, if any. .PP .nf const char * papplClientGetUsername ( pappl_client_t *client ); .fi .PP This function returns the current authenticated username, if any. .SS papplClientHTMLAuthorize Handle authorization for the web interface. .PP .nf bool papplClientHTMLAuthorize ( pappl_client_t *client ); .fi .PP The web interface supports both authentication against user accounts and authentication using a single administrative access password. This function handles the details of authentication for the web interface based on the system authentication service configuration (the "auth_service" argument to \fIpapplSystemCreate\fR). .PP .IP 5 Note: IPP operation callbacks needing to perform authorization should use .IP 5 the \fIpapplClientIsAuthorized\fR function instead. .SS papplClientHTMLEscape Send a string to a web browser client. .PP .nf void papplClientHTMLEscape ( pappl_client_t *client, const char *s, size_t slen ); .fi .PP This function sends the specified string to the web browser client and escapes special characters as HTML entities as needed, for example "&" is sent as \fB&\fR. .SS papplClientHTMLFooter Show the web interface footer. .PP .nf void papplClientHTMLFooter ( pappl_client_t *client ); .fi .PP This function sends the standard web interface footer followed by a trailing 0-length chunk to finish the current HTTP response. Use the \fIpapplSystemSetFooterHTML\fR function to add any custom HTML needed in the footer. .SS papplClientHTMLHeader Show the web interface header and title. .PP .nf void papplClientHTMLHeader ( pappl_client_t *client, const char *title, int refresh ); .fi .PP This function sends the standard web interface header and title. If the "refresh" argument is greater than zero, the page will automatically reload after that many seconds. .PP Use the \fIpapplSystemAddLink\fR function to add system-wide navigation links to the header. Similarly, use \fIpapplPrinterAddLink\fR to add printer-specific links, which will appear in the web interface printer if the system is not configured to support multiple printers (the \fBPAPPL_SOPTIONS_MULTI_QUEUE\fR option to \fIpapplSystemCreate\fR). .SS papplClientHTMLPrinterFooter Show the web interface footer for printers. .PP .nf void papplClientHTMLPrinterFooter ( pappl_client_t *client ); .fi .PP This function sends the standard web interface footer for a printer followed by a trailing 0-length chunk to finish the current HTTP response. Use the \fIpapplSystemSetFooterHTML\fR function to add any custom HTML needed in the footer. .SS papplClientHTMLPrinterHeader Show the web interface header and title for printers. .PP .nf void papplClientHTMLPrinterHeader ( pappl_client_t *client, pappl_printer_t *printer, const char *title, int refresh, const char *label, const char *path_or_url ); .fi .PP This function sends the standard web interface header and title for a printer. If the "refresh" argument is greater than zero, the page will automatically reload after that many seconds. .PP If "label" and "path_or_url" are non-\fBNULL\fR strings, an additional navigation link is included with the title header - this is typically used for an action button ("Change"). .PP Use the \fIpapplSystemAddLink\fR function to add system-wide navigation links to the header. Similarly, use \fIpapplPrinterAddLink\fR to add printer-specific links, which will appear in the web interface printer if the system is not configured to support multiple printers (the \fBPAPPL_SOPTIONS_MULTI_QUEUE\fR option to \fIpapplSystemCreate\fR). .SS papplClientHTMLPrintf Send formatted text to the web browser client, escaping as needed. .PP .nf void papplClientHTMLPrintf ( pappl_client_t *client, const char *format, ... ); .fi .PP This function sends formatted text to the web browser client using \fBprintf\fR-style formatting codes. The format string itself is not escaped to allow for embedded HTML, however strings inserted using the '%c' or \fB%s\fR codes are escaped properly for HTML - "&" is sent as \fB&\fR, etc. .SS papplClientHTMLPuts Send a HTML string to the web browser client. .PP .nf void papplClientHTMLPuts ( pappl_client_t *client, const char *s ); .fi .PP This function sends a HTML string to the client without performing any escaping of special characters. .SS papplClientHTMLStartForm Start a HTML form. .PP .nf void papplClientHTMLStartForm ( pappl_client_t *client, const char *action, bool multipart ); .fi .PP This function starts a HTML form with the specified "action" path and includes the CSRF token as a hidden variable. If the "multipart" argument is \fBtrue\fR, the form is annotated to support file attachments up to 2MiB in size. .SS papplClientIsAuthorized Determine whether a client is authorized for administrative requests. .PP .nf http_status_t papplClientIsAuthorized ( pappl_client_t *client ); .fi .PP This function determines whether a client is authorized to submit an administrative request. .PP The return value is \fBHTTP_STATUS_CONTINUE\fR if access is authorized, \fBHTTP_STATUS_FORBIDDEN\fR if access is not allowed, \fBHTTP_STATUS_UNAUTHORIZED\fR if authorization is required, or \fBHTTP_STATUS_UPGRADE_REQUIRED\fR if the connection needs to be encrypted. All of these values can be passed to the \fIpapplClientRespond\fR function. .SS papplClientIsValidForm Validate HTML form variables. .PP .nf bool papplClientIsValidForm ( pappl_client_t *client, int num_form, cups_option_t *form ); .fi .PP This function validates the contents of a HTML form using the CSRF token included as a hidden variable. When sending a HTML form you should use the \fIpapplClientStartForm\fR function to start the HTML form and insert the CSRF token for later validation. .PP .IP 5 Note: Callers are expected to validate all other form variables. .SS papplClientRespond Send a regular HTTP response. .PP .nf bool papplClientRespond ( pappl_client_t *client, http_status_t code, const char *content_encoding, const char *type, time_t last_modified, size_t length ); .fi .PP This function sends all of the required HTTP fields and includes standard messages for errors. The following values for "code" are explicitly supported: .PP .IP \(bu 5 \fBHTTP_STATUS_OK\fR: The request is successful. .IP \(bu 5 \fBHTTP_STATUS_BAD_REQUEST\fR: The client submitted a bad request. .IP \(bu 5 \fBHTTP_STATUS_CONTINUE\fR: An authentication challenge is not needed. .IP \(bu 5 \fBHTTP_STATUS_FORBIDDEN\fR: Authenticated but not allowed. .IP \(bu 5 \fBHTTP_STATUS_METHOD_NOT_ALLOWED\fR: The HTTP method is not supported for the given URI. .IP \(bu 5 \fBHTTP_STATUS_UNAUTHORIZED\fR: Not authenticated. .IP \(bu 5 \fBHTTP_STATUS_UPGRADE_REQUIRED\fR: Redirects the client to a secure page. .PP Use the \fIpapplClientRespondRedirect\fR when you need to redirect the client to another page. .SS papplClientRespondIPP Send an IPP response. .PP .nf ipp_t * papplClientRespondIPP ( pappl_client_t *client, ipp_status_t status, const char *message, ... ); .fi .PP This function sets the return status for an IPP request and returns the current IPP response message. The "status" and "message" arguments replace any existing status-code and "status-message" attribute value that may be already present in the response. .PP .IP 5 Note: You should call this function prior to adding any response .IP 5 attributes. .SS papplClientRespondIPPUnsupported Respond with an unsupported IPP attribute. .PP .nf void papplClientRespondIPPUnsupported ( pappl_client_t *client, ipp_attribute_t *attr ); .fi .PP This function returns a 'client-error-attributes-or-values-not-supported' status code and adds the specified attribute to the unsupported attributes group in the response. .SS papplClientRespondRedirect Respond with a redirect to another page. .PP .nf bool papplClientRespondRedirect ( pappl_client_t *client, http_status_t code, const char *path ); .fi .PP This function sends a HTTP response that redirects the client to another page or URL. The most common "code" value to return is \fBHTTP_STATUS_FOUND\fR. .SS papplClientSetCookie Set a cookie for the web browser client. .PP .nf void papplClientSetCookie ( pappl_client_t *client, const char *name, const char *value, int expires ); .fi .PP This function sets the value of a cookie for the client by updating the \fBSet-Cookie\fR header in the HTTP response that will be sent. The "name" and "value" strings must contain only valid characters for a cookie and its value as documented in RFC 6265, which basically means letters, numbers, "@", "-", ".", and "_". .PP The "expires" argument specifies how long the cookie will remain active in seconds, for example \fB3600\fR seconds is one hour and \fB86400\fR seconds is one day. If the value is zero or less, a "session" cookie is created instead which will expire as soon as the web browser is closed. .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-device-body.man000066400000000000000000000056171403603036100174100ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_device_s .B pappl_device_t; .PP .I void .br .BI papplDeviceAddScheme "(const char *scheme, pappl_dtype_t dtype, pappl_devlist_cb_t list_cb, pappl_devopen_cb_t open_cb, pappl_devclose_cb_t close_cb, pappl_devread_cb_t read_cb, pappl_devwrite_cb_t write_cb, pappl_devstatus_cb_t status_cb);" .PP .I void .br .BI papplDeviceClose "(pappl_device_t *device);" .PP .I void .br .BI papplDeviceError "(pappl_device_t *device, const char *message, ...);" .PP .I void .br .BI papplDeviceFlush "(pappl_device_t *device);" .PP .I void * .br .BI papplDeviceGetData "(pappl_device_t *device);" .PP .I pappl_dmetrics_t * .br .BI papplDeviceGetMetrics "(pappl_device_t *device, pappl_dmetrics_t *metrics);" .PP .I pappl_preason_t .br .BI papplDeviceGetStatus "(pappl_device_t *device);" .PP .I bool .br .BI papplDeviceList "(pappl_dtype_t types, pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data);" .PP .I pappl_device_t * .br .BI papplDeviceOpen "(const char *device_uri, const char *name, pappl_deverror_cb_t err_cb, void *err_data);" .PP .I int .br .BI papplDeviceParse1284ID "(const char *device_id, cups_option_t **pairs);" .PP .I ssize_t .br .BI papplDevicePrintf "(pappl_device_t *device, const char *format, ...);" .PP .I ssize_t .br .BI papplDevicePuts "(pappl_device_t *device, const char *s);" .PP .I ssize_t .br .BI papplDeviceRead "(pappl_device_t *device, void *buffer, size_t bytes);" .PP .I void .br .BI papplDeviceSetData "(pappl_device_t *device, void *data);" .PP .I ssize_t .br .BI papplDeviceWrite "(pappl_device_t *device, const void *buffer, size_t bytes);" .SH DESCRIPTION The .B PAPPL device functions provide access to output device connections and to list available output devices. Output devices are accessed using Uniform Resource Identifier (URI) strings such as "file:///path/to/file-or-directory", "socket://11.22.33.44", and "usb://make/model?serial=number". .PP The .B papplDeviceList function lists available output devices, providing each available output device to the supplied callback function. The list only contains devices whose URI scheme supports discovery, at present USB printers and network printers that advertise themselves using DNS-SD/mDNS and/or SNMPv1. .PP The .B papplDeviceOpen function opens a connection to an output device using its URI. The .B papplDeviceClose function closes the connection. .PP The .B papplDevicePrintf, papplDevicePuts, and .B papplDeviceWrite functions send data to the device, while the .B papplDeviceRead function reads data from the device. .PP The .B papplDeviceGetMetrics function gets statistical information about all communications with the device while it has been open, while the .B papplDeviceGetStatus function gets the hardware status of a device and maps it to the .B pappl_preason_t bitfield. pappl-1.0.3/man/pappl-device.3000066400000000000000000000357471403603036100160530ustar00rootroot00000000000000.TH pappl-device 3 "pappl device functions" "2021-02-15" "pappl device functions" .SH NAME pappl-device \- pappl device functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_device_s .B pappl_device_t; .PP .I void .br .BI papplDeviceAddScheme "(const char *scheme, pappl_dtype_t dtype, pappl_devlist_cb_t list_cb, pappl_devopen_cb_t open_cb, pappl_devclose_cb_t close_cb, pappl_devread_cb_t read_cb, pappl_devwrite_cb_t write_cb, pappl_devstatus_cb_t status_cb);" .PP .I void .br .BI papplDeviceClose "(pappl_device_t *device);" .PP .I void .br .BI papplDeviceError "(pappl_device_t *device, const char *message, ...);" .PP .I void .br .BI papplDeviceFlush "(pappl_device_t *device);" .PP .I void * .br .BI papplDeviceGetData "(pappl_device_t *device);" .PP .I pappl_dmetrics_t * .br .BI papplDeviceGetMetrics "(pappl_device_t *device, pappl_dmetrics_t *metrics);" .PP .I pappl_preason_t .br .BI papplDeviceGetStatus "(pappl_device_t *device);" .PP .I bool .br .BI papplDeviceList "(pappl_dtype_t types, pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data);" .PP .I pappl_device_t * .br .BI papplDeviceOpen "(const char *device_uri, const char *name, pappl_deverror_cb_t err_cb, void *err_data);" .PP .I int .br .BI papplDeviceParse1284ID "(const char *device_id, cups_option_t **pairs);" .PP .I ssize_t .br .BI papplDevicePrintf "(pappl_device_t *device, const char *format, ...);" .PP .I ssize_t .br .BI papplDevicePuts "(pappl_device_t *device, const char *s);" .PP .I ssize_t .br .BI papplDeviceRead "(pappl_device_t *device, void *buffer, size_t bytes);" .PP .I void .br .BI papplDeviceSetData "(pappl_device_t *device, void *data);" .PP .I ssize_t .br .BI papplDeviceWrite "(pappl_device_t *device, const void *buffer, size_t bytes);" .SH DESCRIPTION The .B PAPPL device functions provide access to output device connections and to list available output devices. Output devices are accessed using Uniform Resource Identifier (URI) strings such as "file:///path/to/file-or-directory", "socket://11.22.33.44", and "usb://make/model?serial=number". .PP The .B papplDeviceList function lists available output devices, providing each available output device to the supplied callback function. The list only contains devices whose URI scheme supports discovery, at present USB printers and network printers that advertise themselves using DNS-SD/mDNS and/or SNMPv1. .PP The .B papplDeviceOpen function opens a connection to an output device using its URI. The .B papplDeviceClose function closes the connection. .PP The .B papplDevicePrintf, papplDevicePuts, and .B papplDeviceWrite functions send data to the device, while the .B papplDeviceRead function reads data from the device. .PP The .B papplDeviceGetMetrics function gets statistical information about all communications with the device while it has been open, while the .B papplDeviceGetStatus function gets the hardware status of a device and maps it to the .B pappl_preason_t bitfield. .SH ENUMERATIONS .SS pappl_devtype_e Device type bit values .TP 5 PAPPL_DEVTYPE_ALL .br All printers .TP 5 PAPPL_DEVTYPE_CUSTOM_LOCAL .br Local printer using a custom interface or protocol .TP 5 PAPPL_DEVTYPE_CUSTOM_NETWORK .br Network printer using a custom interface or protocol .TP 5 PAPPL_DEVTYPE_DNS_SD .br Network printers discovered via DNS-SD/mDNS .TP 5 PAPPL_DEVTYPE_FILE .br Local file/directory .TP 5 PAPPL_DEVTYPE_LOCAL .br All local printers .TP 5 PAPPL_DEVTYPE_NETWORK .br All network printers .TP 5 PAPPL_DEVTYPE_SNMP .br Network printers discovered via SNMP .TP 5 PAPPL_DEVTYPE_SOCKET .br Network printers using raw socket .TP 5 PAPPL_DEVTYPE_USB .br USB printers .SH FUNCTIONS .SS papplDeviceAddScheme Add a device URI scheme. .PP .nf void papplDeviceAddScheme ( const char *scheme, pappl_devtype_t dtype, pappl_devlist_cb_t list_cb, pappl_devopen_cb_t open_cb, pappl_devclose_cb_t close_cb, pappl_devread_cb_t read_cb, pappl_devwrite_cb_t write_cb, pappl_devstatus_cb_t status_cb, pappl_devid_cb_t id_cb ); .fi .PP This function registers a device URI scheme with PAPPL, so that devices using the named scheme can receive print data, report status information, and so forth. PAPPL includes support for the following URI schemes: .PP .IP \(bu 5 \fBdnssd\fR: Network printers discovered using DNS-SD. .IP \(bu 5 \fBfile\fR: Character device files, plain files, and directories. .IP \(bu 5 \fBsnmp\fR: Network printers discovered using SNMPv1. .IP \(bu 5 \fBsocket\fR: Network printers using a hostname or numeric IP address. .IP \(bu 5 \fBusb\fR: Class 1 (unidirectional) or 2 (bidirectional) USB printers. .PP The "scheme" parameter specifies the URI scheme and must consist of lowercase letters, digits, "-", "_", and/or ".", for example "x-foo" or "com.example.bar". .PP The "dtype" parameter specifies the device type and should be \fBPAPPL_DTYPE_CUSTOM_LOCAL\fR for locally connected printers and \fBPAPPL_DTYPE_CUSTOM_NETWORK\fR for network printers. .PP Each of the callbacks corresponds to one of the \fBpapplDevice\fR functions: .PP .IP \(bu 5 "list_cb": Implements discovery of devices (optional) .IP \(bu 5 "open_cb": Opens communication with a device and allocates any device- specific data as needed .IP \(bu 5 "close_cb": Closes communication with a device and frees any device- specific data as needed .IP \(bu 5 "read_cb": Reads data from a device .IP \(bu 5 "write_cb": Write data to a device .IP \(bu 5 "status_cb": Gets basic printer state information from a device (optional) .IP \(bu 5 "id_cb": Gets the current IEEE-1284 device ID from a device (optional) .PP The "open_cb" callback typically calls \fIpapplDeviceSetData\fR to store a pointer to contextual information for the connection while the "close_cb", "id_cb", "read_cb", "write_cb", and "status_cb" callbacks typically call \fIpapplDeviceGetData\fR to retrieve it. .SS papplDeviceClose Close a device connection. .PP .nf void papplDeviceClose ( pappl_device_t *device ); .fi .PP This function flushes any pending write data and closes the connection to a device. .SS papplDeviceError Report an error on a device. .PP .nf void papplDeviceError ( pappl_device_t *device, const char *message, ... ); .fi .PP This function reports an error on a device using the client-supplied callback function. It is normally called from any custom device URI scheme callbacks you implement. .SS papplDeviceFlush Flush any buffered data to the device. .PP .nf void papplDeviceFlush ( pappl_device_t *device ); .fi .PP This function flushes any pending write data sent using the \fIpapplDevicePrintf\fR, \fIpapplDevicePuts\fR, or \fIpapplDeviceWrite\fR functions to the device. .SS papplDeviceGetData Get device-specific data. .PP .nf void * papplDeviceGetData ( pappl_device_t *device ); .fi .PP This function returns any device-specific data that has been set by the device open callback. It is normally only called from any custom device URI scheme callbacks you implement. .SS papplDeviceGetID Get the IEEE-1284 device ID. .PP .nf char * papplDeviceGetID ( pappl_device_t *device, char *buffer, size_t bufsize ); .fi .PP This function queries the IEEE-1284 device ID from the device and copies it to the provided buffer. The buffer must be at least 64 bytes and should be at least 1024 bytes in length. .PP .IP 5 \fINote:\fR This function can block for up to several seconds depending on .IP 5 the type of connection. .SS papplDeviceGetMetrics Get the device metrics. .PP .nf pappl_devmetrics_t * papplDeviceGetMetrics ( pappl_device_t *device, pappl_devmetrics_t *metrics ); .fi .PP This function returns a copy of the device metrics data, which includes the number, length (in bytes), and duration (in milliseconds) of read, status, and write requests for the current session. This information is normally used for performance measurement and optimization during development of a printer application. It can also be useful diagnostic information. .SS papplDeviceGetStatus Get the printer status bits. .PP .nf pappl_preason_t papplDeviceGetStatus ( pappl_device_t *device ); .fi .PP This function returns the current printer status bits, as applicable to the current device. .PP The status bits for USB devices come from the original Centronics parallel printer "standard" which was later formally standardized in IEEE 1284-1984 and the USB Device Class Definition for Printing Devices. Some vendor extensions are also supported. .PP The status bits for network devices come from the hrPrinterDetectedErrorState property that is defined in the SNMP Printer MIB v2 (RFC 3805). .PP This function returns a \fIpappl_preason_t\fR bitfield which can be passed to the \fIpapplPrinterSetReasons\fR function. Use the \fIPAPPL_PREASON_DEVICE_STATUS\fR value as the value of the "remove" argument. .PP .IP 5 Note: This function can block for several seconds while getting the status .IP 5 information. .SS papplDeviceIsSupported Determine whether a given URI is supported. .PP .nf bool papplDeviceIsSupported ( const char *uri ); .fi .PP This function determines whether a given URI or URI scheme is supported as a device. .SS papplDeviceList List available devices. .PP .nf bool papplDeviceList ( pappl_devtype_t types, pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data ); .fi .PP This function lists the available devices, calling the "cb" function once per device that is discovered/listed. The callback function receives the device URI, IEEE-1284 device ID (if any), and "data" pointer, and returns \fBtrue\fR to stop listing devices and \fBfalse\fR to continue. .PP The "types" argument determines which devices are listed, for example \fBPAPPL_DEVTYPE_ALL\fR will list all types of devices while \fBPAPPL_DEVTYPE_USB\fR only lists USB printers. .PP Any errors are reported using the supplied "err_cb" function. If you specify \fBNULL\fR for this argument, errors are sent to \fBstderr\fR. .PP .IP 5 Note: This function will block (not return) until each of the device URI .IP 5 schemes has reported all of the devices \fIor\fR the supplied callback function .IP 5 returns \fBtrue\fR. .SS papplDeviceOpen Open a connection to a device. .PP .nf pappl_device_t * papplDeviceOpen ( const char *device_uri, const char *name, pappl_deverror_cb_t err_cb, void *err_data ); .fi .PP This function opens a connection to the specified device URI. The "name" argument provides textual context for the connection and is usually the name (title) of the print job. .PP Any errors are reported using the supplied "err_cb" function. If you specify \fBNULL\fR for this argument, errors are sent to \fBstderr\fR. .SS papplDeviceParseID Parse an IEEE-1284 device ID string. .PP .nf int papplDeviceParseID ( const char *device_id, cups_option_t **pairs ); .fi .PP This function parses an IEEE-1284 device ID string and returns an array of key/value pairs as a \fBcups_option_t\fR array. The returned array must be freed using the \fBcupsFreeOptions\fR function. .SS papplDevicePrintf Write a formatted string. .PP .nf ssize_t papplDevicePrintf ( pappl_device_t *device, const char *format, ... ); .fi .PP This function buffers a formatted string that will be sent to the device. The "format" argument accepts all \fBprintf\fR format specifiers and behaves identically to that function. .PP Call the \fIpapplDeviceFlush\fR function to ensure that the formatted string is immediately sent to the device. .SS papplDevicePuts Write a literal string. .PP .nf ssize_t papplDevicePuts ( pappl_device_t *device, const char *s ); .fi .PP This function buffers a literal string that will be sent to the device. Call the \fIpapplDeviceFlush\fR function to ensure that the literal string is immediately sent to the device. .SS papplDeviceRead Read from a device. .PP .nf ssize_t papplDeviceRead ( pappl_device_t *device, void *buffer, size_t bytes ); .fi .PP This function reads data from the device. Depending on the device, this function may block indefinitely. .SS papplDeviceSetData Set device-specific data. .PP .nf void papplDeviceSetData ( pappl_device_t *device, void *data ); .fi .PP This function sets any device-specific data needed to communicate with the device. It is normally only called from the open callback that was registered for the device URI scheme. .SS papplDeviceWrite Write to a device. .PP .nf ssize_t papplDeviceWrite ( pappl_device_t *device, const void *buffer, size_t bytes ); .fi .PP This function buffers data that will be sent to the device. Call the \fIpapplDeviceFlush\fR function to ensure that the data is immediately sent to the device. .SH STRUCTURES .SS pappl_devmetrics_s Device metrics .PP .nf struct pappl_devmetrics_s { size_t read_bytes; size_t read_msecs; size_t read_requests; size_t status_msecs; size_t status_requests; size_t write_bytes; size_t write_msecs; size_t write_requests; }; .fi .SH TYPES .SS pappl_devclose_cb_t Device close callback .PP .nf typedef void (*pappl_devclose_cb_t)(pappl_device_t *device); .fi .SS pappl_deverror_cb_t Device error callback .PP .nf typedef void (*pappl_deverror_cb_t)(const char *message, void *err_data); .fi .SS pappl_device_cb_t Device callback - return \fBtrue\fR to stop, \fBfalse\fR to continue .PP .nf typedef bool (*pappl_device_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data); .fi .SS pappl_devid_cb_t Device ID callback .PP .nf typedef char * (*pappl_devid_cb_t)(pappl_device_t *device, char *buffer, size_t bufsize); .fi .SS pappl_devlist_cb_t Device list callback .PP .nf typedef bool (*pappl_devlist_cb_t)(pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data); .fi .SS pappl_devmetrics_t Device metrics .PP .nf typedef struct pappl_devmetrics_s pappl_devmetrics_t; .fi .SS pappl_devopen_cb_t Device open callback .PP .nf typedef bool (*pappl_devopen_cb_t)(pappl_device_t *device, const char *device_uri, const char *name); .fi .SS pappl_devread_cb_t Device read callback .PP .nf typedef ssize_t (*pappl_devread_cb_t)(pappl_device_t *device, void *buffer, size_t bytes); .fi .SS pappl_devstatus_cb_t Device status callback .PP .nf typedef pappl_preason_t (*pappl_devstatus_cb_t)(pappl_device_t *device); .fi .SS pappl_devtype_t Device type bitfield .PP .nf typedef unsigned pappl_devtype_t; .fi .SS pappl_devwrite_cb_t Device write callback .PP .nf typedef ssize_t (*pappl_devwrite_cb_t)(pappl_device_t *device, const void *buffer, size_t bytes); .fi .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-footer.man000066400000000000000000000011651403603036100165060ustar00rootroot00000000000000.SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-job-body.man000066400000000000000000000062731403603036100167220ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_job_s .B pappl_job_t; .PP .I void .br .BI papplJobCancel "(pappl_job_t *job);" .PP .I bool .br .BI papplJobFilterImage "(pappl_job_t *job, pappl_device_t *device, pappl_joptions_t *options, const unsigned char *pixels, unsigned width, unsigned height, unsigned depth, bool smoothing);" .PP .I ipp_attribute_t * .br .BI papplJobGetAttribute "(pappl_job_t *job, const char *name);" .PP .I void * .br .BI papplJobGetData "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetFilename "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetFormat "(pappl_job_t *job);" .PP .I int .br .BI papplJobGetID "(pappl_job_t *job);" .PP .I int .br .BI papplJobGetImpressions "(pappl_job_t *job);" .PP .I int .br .BI papplJobGetImpressionsCompleted "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetMessage "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetName "(pappl_job_t *job);" .PP .I pappl_joptions_t * .br .BI papplJobGetOptions "(pappl_job_t *job, pappl_joptions_t *options, unsigned num_pages, bool color);" .PP .I pappl_printer_t * .br .BI papplJobGetPrinter "(pappl_job_t *job);" .PP .I pappl_jreason_t .br .BI papplJobGetReasons "(pappl_job_t *job);" .PP .I ipp_jstate_t .br .BI papplJobGetState "(pappl_job_t *job);" .PP .I time_t .br .BI papplJobGetTimeCompleted "(pappl_job_t *job);" .PP .I time_t .br .BI papplJobGetTimeCreated "(pappl_job_t *job);" .PP .I time_t .br .BI papplJobGetTimeProcessed "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetUsername "(pappl_job_t *job);" .PP .I bool .br .BI papplJobIsCanceled "(pappl_job_t *job);" .PP .I int .br .BI papplJobOpenFile "(pappl_job_t *job, char *fname, size_t fnamesize, const char *directory, const char *ext, const char *mode);" .PP .I void .br .BI papplJobSetData "(pappl_job_t *job, void *data);" .PP .I void .br .BI papplJobSetImpressions "(pappl_job_t *job, int impressions);" .PP .I void .br .BI papplJobSetImpressionsCompleted "(pappl_job_t *job, int add);" .PP .I void .br .BI papplJobSetMessage "(pappl_job_t *job, const char *message, ...);" .PP .I void .br .BI papplJobSetReasons "(pappl_job_t *job, pappl_jreason_t add, pappl_jreason_t remove);" .SH DESCRIPTION The .B PAPPL job functions provide access to the job object. Jobs and the life cycle of the .B pappl_job_t objects are managed automatically by the system object for the printer application. .PP The .B papplJobGet functions get the current values associated with a job while the .B papplJobSet functions set the current values associated with a job. .PP The .B papplJobCancel function cancels processing of a job, while the .B papplJobIsCanceled function returns whether a job is in the canceled state (\fIIPP_JSTATE_CANCELED\fR) or is in the process of being canceled (\fIIPP_JSTATE_PROCESSING\fR and \fIPAPPL_JREASON_PROCESSING_TO_STOP_POINT\fR). .PP The .B papplJobOpenFile function opens a file associated with the job. The file descriptor must be closed by the caller using the .BR close (2) system call. .PP The .B papplJobFilterImage function converts raw image data to raster data suitable for the printer. pappl-1.0.3/man/pappl-job.3000066400000000000000000000352421403603036100153540ustar00rootroot00000000000000.TH pappl-job 3 "pappl job functions" "2021-02-15" "pappl job functions" .SH NAME pappl-job \- pappl job functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_job_s .B pappl_job_t; .PP .I void .br .BI papplJobCancel "(pappl_job_t *job);" .PP .I bool .br .BI papplJobFilterImage "(pappl_job_t *job, pappl_device_t *device, pappl_joptions_t *options, const unsigned char *pixels, unsigned width, unsigned height, unsigned depth, bool smoothing);" .PP .I ipp_attribute_t * .br .BI papplJobGetAttribute "(pappl_job_t *job, const char *name);" .PP .I void * .br .BI papplJobGetData "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetFilename "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetFormat "(pappl_job_t *job);" .PP .I int .br .BI papplJobGetID "(pappl_job_t *job);" .PP .I int .br .BI papplJobGetImpressions "(pappl_job_t *job);" .PP .I int .br .BI papplJobGetImpressionsCompleted "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetMessage "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetName "(pappl_job_t *job);" .PP .I pappl_joptions_t * .br .BI papplJobGetOptions "(pappl_job_t *job, pappl_joptions_t *options, unsigned num_pages, bool color);" .PP .I pappl_printer_t * .br .BI papplJobGetPrinter "(pappl_job_t *job);" .PP .I pappl_jreason_t .br .BI papplJobGetReasons "(pappl_job_t *job);" .PP .I ipp_jstate_t .br .BI papplJobGetState "(pappl_job_t *job);" .PP .I time_t .br .BI papplJobGetTimeCompleted "(pappl_job_t *job);" .PP .I time_t .br .BI papplJobGetTimeCreated "(pappl_job_t *job);" .PP .I time_t .br .BI papplJobGetTimeProcessed "(pappl_job_t *job);" .PP .I const char * .br .BI papplJobGetUsername "(pappl_job_t *job);" .PP .I bool .br .BI papplJobIsCanceled "(pappl_job_t *job);" .PP .I int .br .BI papplJobOpenFile "(pappl_job_t *job, char *fname, size_t fnamesize, const char *directory, const char *ext, const char *mode);" .PP .I void .br .BI papplJobSetData "(pappl_job_t *job, void *data);" .PP .I void .br .BI papplJobSetImpressions "(pappl_job_t *job, int impressions);" .PP .I void .br .BI papplJobSetImpressionsCompleted "(pappl_job_t *job, int add);" .PP .I void .br .BI papplJobSetMessage "(pappl_job_t *job, const char *message, ...);" .PP .I void .br .BI papplJobSetReasons "(pappl_job_t *job, pappl_jreason_t add, pappl_jreason_t remove);" .SH DESCRIPTION The .B PAPPL job functions provide access to the job object. Jobs and the life cycle of the .B pappl_job_t objects are managed automatically by the system object for the printer application. .PP The .B papplJobGet functions get the current values associated with a job while the .B papplJobSet functions set the current values associated with a job. .PP The .B papplJobCancel function cancels processing of a job, while the .B papplJobIsCanceled function returns whether a job is in the canceled state (\fIIPP_JSTATE_CANCELED\fR) or is in the process of being canceled (\fIIPP_JSTATE_PROCESSING\fR and \fIPAPPL_JREASON_PROCESSING_TO_STOP_POINT\fR). .PP The .B papplJobOpenFile function opens a file associated with the job. The file descriptor must be closed by the caller using the .BR close (2) system call. .PP The .B papplJobFilterImage function converts raw image data to raster data suitable for the printer. .SH ENUMERATIONS .SS pappl_jreason_e IPP "job-state-reasons" bit values .TP 5 PAPPL_JREASON_ABORTED_BY_SYSTEM .br \'aborted-by-system' .TP 5 PAPPL_JREASON_COMPRESSION_ERROR .br \'compression-error' .TP 5 PAPPL_JREASON_DOCUMENT_FORMAT_ERROR .br \'document-format-error' .TP 5 PAPPL_JREASON_DOCUMENT_PASSWORD_ERROR .br \'document-password-error' .TP 5 PAPPL_JREASON_DOCUMENT_PERMISSION_ERROR .br \'document-permission-error' .TP 5 PAPPL_JREASON_DOCUMENT_UNPRINTABLE_ERROR .br \'document-unprintable-error' .TP 5 PAPPL_JREASON_ERRORS_DETECTED .br \'errors-detected' .TP 5 PAPPL_JREASON_JOB_CANCELED_AT_DEVICE .br \'job-canceled-at-device' .TP 5 PAPPL_JREASON_JOB_CANCELED_BY_USER .br \'job-canceled-by-user' .TP 5 PAPPL_JREASON_JOB_COMPLETED_SUCCESSFULLY .br \'job-completed-successfully' .TP 5 PAPPL_JREASON_JOB_COMPLETED_WITH_ERRORS .br \'job-completed-with-errors' .TP 5 PAPPL_JREASON_JOB_COMPLETED_WITH_WARNINGS .br \'job-completed-with-warnings' .TP 5 PAPPL_JREASON_JOB_DATA_INSUFFICIENT .br \'job-data-insufficient' .TP 5 PAPPL_JREASON_JOB_INCOMING .br \'job-incoming' .TP 5 PAPPL_JREASON_JOB_PRINTING .br \'job-printing' .TP 5 PAPPL_JREASON_JOB_QUEUED .br \'job-queued' .TP 5 PAPPL_JREASON_JOB_SPOOLING .br \'job-spooling' .TP 5 PAPPL_JREASON_NONE .br \'none' .TP 5 PAPPL_JREASON_PRINTER_STOPPED .br \'printer-stopped' .TP 5 PAPPL_JREASON_PRINTER_STOPPED_PARTLY .br \'printer-stopped-partly' .TP 5 PAPPL_JREASON_PROCESSING_TO_STOP_POINT .br \'processing-to-stop-point' .TP 5 PAPPL_JREASON_QUEUED_IN_DEVICE .br \'queued-in-device' .TP 5 PAPPL_JREASON_WARNINGS_DETECTED .br \'warnings-detected' .SH FUNCTIONS .SS papplJobCancel Cancel a job. .PP .nf void papplJobCancel ( pappl_job_t *job ); .fi .PP This function cancels the specified job. If the job is currently being printed, it will be stopped at a convenient time (usually the end of a page) so that the printer will be left in a known state. .SS papplJobCreatePrintOptions Create the printer options for a job. .PP .nf pappl_pr_options_t * papplJobCreatePrintOptions ( pappl_job_t *job, unsigned num_pages, bool color ); .fi .PP This function allocates a printer options structure and computes the print options for a job based upon the Job Template attributes submitted in the print request and the default values set in the printer driver data. .PP The "num_pages" and "color" arguments specify the number of pages and whether the document contains non-grayscale colors - this information typically comes from parsing the job file. .SS papplJobDeletePrintOptions Delete a job options structure. .PP .nf void papplJobDeletePrintOptions ( pappl_pr_options_t *options ); .fi .PP This function frees the memory used for a job options structure. .SS papplJobFilterImage Filter an image in memory. .PP .nf bool papplJobFilterImage ( pappl_job_t *job, pappl_device_t *device, pappl_pr_options_t *options, const unsigned char *pixels, int width, int height, int depth, int ppi, bool smoothing ); .fi .PP This function will print a grayscale or sRGB image using the printer's raster driver interface, scaling and positioning the image as necessary based on the job options, and printing as many copies as requested. .PP The image data is an array of grayscale ("depth" = \fB1\fR) or sRGB ("depth" = \fB3\fR) pixels starting at the top-left corner of the image. .PP The image resolution ("ppi") is expressed in pixels per inch and is used for some "print-scaling" modes. Pass \fB0\fR if the image has no explicit resolution information. .SS papplJobGetAttribute Get an attribute from a job. .PP .nf ipp_attribute_t * papplJobGetAttribute ( pappl_job_t *job, const char *name ); .fi .PP This function gets the named IPP attribute from a job. The returned attribute can be examined using the \fBippGetXxx\fR functions. .SS papplJobGetData Get per-job driver data. .PP .nf void * papplJobGetData ( pappl_job_t *job ); .fi .PP This function returns the driver data associated with the job. It is normally only called from drivers to maintain state for the processing of the job, for example to store bitmap compression information. .SS papplJobGetFilename Get the job's filename. .PP .nf const char * papplJobGetFilename ( pappl_job_t *job ); .fi .PP This function returns the filename for the job's document data. .SS papplJobGetFormat Get the MIME media type for the job's file. .PP .nf const char * papplJobGetFormat ( pappl_job_t *job ); .fi .PP This function returns the MIME media type for the job's document data. .SS papplJobGetID Get the job ID value. .PP .nf int papplJobGetID ( pappl_job_t *job ); .fi .PP This function returns the job's unique integer identifier. .SS papplJobGetImpressions Get the number of impressions (sides) in the job. .PP .nf int papplJobGetImpressions ( pappl_job_t *job ); .fi .PP This function returns the number of impressions in the job's document data. An impression is one side of an output page. .SS papplJobGetImpressionsCompleted Get the number of completed impressions (sides) in the job. .PP .nf int papplJobGetImpressionsCompleted ( pappl_job_t *job ); .fi .PP This function returns the number of impressions that have been printed. An impression is one side of an output page. .SS papplJobGetMessage Get the current job message string, if any. .PP .nf const char * papplJobGetMessage ( pappl_job_t *job ); .fi .PP This function returns the current job message string, if any. .SS papplJobGetName Get the job name/title. .PP .nf const char * papplJobGetName ( pappl_job_t *job ); .fi .PP This function returns the name or title of the job. .SS papplJobGetPrinter Get the printer for the job. .PP .nf pappl_printer_t * papplJobGetPrinter ( pappl_job_t *job ); .fi .PP This function returns the printer containing the job. .SS papplJobGetReasons Get the current job state reasons. .PP .nf pappl_jreason_t papplJobGetReasons ( pappl_job_t *job ); .fi .PP This function returns the current job state reasons bitfield. .SS papplJobGetState Get the current job state. .PP .nf ipp_jstate_t papplJobGetState ( pappl_job_t *job ); .fi .PP This function returns the current job processing state, which is represented as an enumeration: .PP .IP \(bu 5 \fBIPP_JSTATE_ABORTED\fR: Job has been aborted by the system due to an error. .IP \(bu 5 \fBIPP_JSTATE_CANCELED\fR: Job has been canceled by a user. .IP \(bu 5 \fBIPP_JSTATE_COMPLETED\fR: Job has finished printing. .IP \(bu 5 \fBIPP_JSTATE_HELD\fR: Job is being held for some reason, typically because the document data is being received. .IP \(bu 5 \fBIPP_JSTATE_PENDING\fR: Job is queued and waiting to be printed. .IP \(bu 5 \fBIPP_JSTATE_PROCESSING\fR: Job is being printed. .IP \(bu 5 \fBIPP_JSTATE_STOPPED\fR: Job is paused, typically when the printer is not ready. .SS papplJobGetTimeCompleted Get the job completion time, if any. .PP .nf time_t papplJobGetTimeCompleted ( pappl_job_t *job ); .fi .PP This function returns the date and time when the job reached the completed, canceled, or aborted states. \fB0\fR is returned if the job is not yet in one of those states. .SS papplJobGetTimeCreated Get the job creation time. .PP .nf time_t papplJobGetTimeCreated ( pappl_job_t *job ); .fi .PP This function returns the date and time when the job was created. .SS papplJobGetTimeProcessed Get the job processing time. .PP .nf time_t papplJobGetTimeProcessed ( pappl_job_t *job ); .fi .PP This function returns the date and time when the job started processing (printing). .SS papplJobGetUsername Get the name of the user that submitted the job. .PP .nf const char * papplJobGetUsername ( pappl_job_t *job ); .fi .PP This function returns the name of the user that submitted the job. .SS papplJobIsCanceled Return whether the job is canceled. .PP .nf bool papplJobIsCanceled ( pappl_job_t *job ); .fi .PP This function returns \fBtrue\fR if the job has been canceled or aborted. .SS papplJobOpenFile Create or open a file for the document in a job. .PP .nf int papplJobOpenFile ( pappl_job_t *job, char *fname, size_t fnamesize, const char *directory, const char *ext, const char *mode ); .fi .PP This function creates or opens a file for a job. The "fname" and "fnamesize" arguments specify the location and size of a buffer to store the job filename, which incorporates the "directory", printer ID, job ID, job name (title), and "ext" values. The job name is "sanitized" to only contain alphanumeric characters. .PP The "mode" argument is "r" to read an existing job file or "w" to write a new job file. New files are created with restricted permissions for security purposes. .SS papplJobSetData Set the per-job driver data pointer. .PP .nf void papplJobSetData ( pappl_job_t *job, void *data ); .fi .PP This function sets the driver data for the specified job. It is normally only called from drivers to maintain state for the processing of the job, for example to store bitmap compression information. .SS papplJobSetImpressions Set the number of impressions (sides) in a job. .PP .nf void papplJobSetImpressions ( pappl_job_t *job, int impressions ); .fi .PP This function sets the number of impressions in a job. An impression is one side of an output page. .SS papplJobSetImpressionsCompleted Add completed impressions (sides) to the job. .PP .nf void papplJobSetImpressionsCompleted ( pappl_job_t *job, int add ); .fi .PP This function updates the number of completed impressions in a job. An impression is one side of an output page. .SS papplJobSetMessage Set the job message string. .PP .nf void papplJobSetMessage ( pappl_job_t *job, const char *message, ... ); .fi .PP This function sets the job message string using a \fBprintf\fR-style format string. .PP .IP 5 Note: The maximum length of the job message string is 1023 bytes. .SS papplJobSetReasons Set the job state reasons bit values. .PP .nf void papplJobSetReasons ( pappl_job_t *job, pappl_jreason_t add, pappl_jreason_t remove ); .fi .PP This function updates the job state reasons bitfield. The "remove" bits are cleared first, then the "add" bits are set. .SS papplPrinterFindJob Find a job. .PP .nf pappl_job_t * papplPrinterFindJob ( pappl_printer_t *printer, int job_id ); .fi .PP This function finds a job submitted to a printer using its integer ID value. .SS papplSystemCleanJobs Clean out old (completed) jobs. .PP .nf void papplSystemCleanJobs ( pappl_system_t *system ); .fi .PP This function deletes all old (completed) jobs above the limit set by the \fIpapplPrinterSetMaxCompletedJobs\fR function. The level may temporarily exceed this limit if the jobs were completed within the last 60 seconds. .PP .IP 5 Note: This function is normally called automatically from the .IP 5 \fIpapplSystemRun\fR function. .SH TYPES .SS pappl_jreason_t Bitfield for IPP "job-state-reasons" values .PP .nf typedef unsigned int pappl_jreason_t; .fi .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-log-body.man000066400000000000000000000022131403603036100167170ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I void .br .BI papplLog "(pappl_system_t *system, pappl_loglevel_t level, const char *message, ...);" .PP .I void .br .BI papplLogClient "(pappl_client_t *client, pappl_loglevel_t level, const char *message, ...);" .PP .I void .br .BI papplLogJob "(pappl_job_t *job, pappl_loglevel_t level, const char *message, ...);" .PP .I void .br .BI papplLogPrinter "(pappl_printer_t *printer, pappl_loglevel_t level, const char *message, ...);" .SH DESCRIPTION The .B PAPPL logging functions record messages to the configured log file. .PP .B papplLog records messages applying to the system as a whole. .PP .B papplLogClient, .B papplLogJob, and .B papplLogPrinter record messages specific to a client connection, print job, or printer respectively. .PP The .I level argument specifies a log level from debugging (\fIPAPPL_LOGLEVEL_DEBUG\fR) to fatal (\fIPAPPL_LOGLEVEL_FATAL\fR) and is used to determine whether the message is recorded to the log. .PP The .I message argument specifies the message using a .BR printf (3) format string. pappl-1.0.3/man/pappl-log.3000066400000000000000000000130501403603036100153540ustar00rootroot00000000000000.TH pappl-log 3 "pappl logging functions" "2021-02-15" "pappl logging functions" .SH NAME pappl-log \- pappl logging functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I void .br .BI papplLog "(pappl_system_t *system, pappl_loglevel_t level, const char *message, ...);" .PP .I void .br .BI papplLogClient "(pappl_client_t *client, pappl_loglevel_t level, const char *message, ...);" .PP .I void .br .BI papplLogJob "(pappl_job_t *job, pappl_loglevel_t level, const char *message, ...);" .PP .I void .br .BI papplLogPrinter "(pappl_printer_t *printer, pappl_loglevel_t level, const char *message, ...);" .SH DESCRIPTION The .B PAPPL logging functions record messages to the configured log file. .PP .B papplLog records messages applying to the system as a whole. .PP .B papplLogClient, .B papplLogJob, and .B papplLogPrinter record messages specific to a client connection, print job, or printer respectively. .PP The .I level argument specifies a log level from debugging (\fIPAPPL_LOGLEVEL_DEBUG\fR) to fatal (\fIPAPPL_LOGLEVEL_FATAL\fR) and is used to determine whether the message is recorded to the log. .PP The .I message argument specifies the message using a .BR printf (3) format string. .SH ENUMERATIONS .SS pappl_loglevel_e Log levels .TP 5 PAPPL_LOGLEVEL_DEBUG .br Debug message .TP 5 PAPPL_LOGLEVEL_ERROR .br Error message .TP 5 PAPPL_LOGLEVEL_FATAL .br Fatal message .TP 5 PAPPL_LOGLEVEL_INFO .br Informational message .TP 5 PAPPL_LOGLEVEL_UNSPEC .br Not specified .TP 5 PAPPL_LOGLEVEL_WARN .br Warning message .SH FUNCTIONS .SS papplLog Log a message for the system. .PP .nf void papplLog ( pappl_system_t *system, pappl_loglevel_t level, const char *message, ... ); .fi .PP This function sends a message to the system's log file. The "level" argument specifies the urgency of the message: .PP .IP \(bu 5 \fBPAPPL_LOGLEVEL_DEBUG\fR: A debugging message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_ERROR\fR: An error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_FATAL\fR: A fatal error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_INFO\fR: An informational message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_WARN\fR: A warning message. .PP The "message" argument specifies a \fBprintf\fR-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters. .SS papplLogClient Log a message for a client. .PP .nf void papplLogClient ( pappl_client_t *client, pappl_loglevel_t level, const char *message, ... ); .fi .PP This function sends a client message to the system's log file. The "level" argument specifies the urgency of the message: .PP .IP \(bu 5 \fBPAPPL_LOGLEVEL_DEBUG\fR: A debugging message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_ERROR\fR: An error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_FATAL\fR: A fatal error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_INFO\fR: An informational message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_WARN\fR: A warning message. .PP The "message" argument specifies a \fBprintf\fR-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters. .SS papplLogDevice Log a device error for the system... .PP .nf void papplLogDevice ( const char *message, void *data ); .fi .PP This function sends a device error message to the system's log file. .SS papplLogJob Log a message for a job. .PP .nf void papplLogJob ( pappl_job_t *job, pappl_loglevel_t level, const char *message, ... ); .fi .PP This function sends a job message to the system's log file. The "level" argument specifies the urgency of the message: .PP .IP \(bu 5 \fBPAPPL_LOGLEVEL_DEBUG\fR: A debugging message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_ERROR\fR: An error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_FATAL\fR: A fatal error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_INFO\fR: An informational message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_WARN\fR: A warning message. .PP The "message" argument specifies a \fBprintf\fR-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters. .SS papplLogPrinter Log a message for a printer. .PP .nf void papplLogPrinter ( pappl_printer_t *printer, pappl_loglevel_t level, const char *message, ... ); .fi .PP This function sends a printer message to the system's log file. The "level" argument specifies the urgency of the message: .PP .IP \(bu 5 \fBPAPPL_LOGLEVEL_DEBUG\fR: A debugging message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_ERROR\fR: An error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_FATAL\fR: A fatal error message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_INFO\fR: An informational message. .IP \(bu 5 \fBPAPPL_LOGLEVEL_WARN\fR: A warning message. .PP The "message" argument specifies a \fBprintf\fR-style format string. Values logged using the "%c" and "%s" format specifiers are sanitized to not contain control characters. .SH TYPES .SS pappl_loglevel_t Log levels .PP .nf typedef enum pappl_loglevel_e pappl_loglevel_t; .fi .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-mainloop-body.man000066400000000000000000000011441403603036100177560ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I int .br .BI papplMainloop "(int argc, char *argv[], const char *version, const char *footer_html, int num_drivers, pappl_driver_t *drivers, pappl_driver_cb_t driver_cb, pappl_ml_autoadd_cb_t autoadd_cb, const char *subcmd_name, pappl_ml_subcmd_cb_t subcmd_cb, pappl_ml_system_cb_t system_cb, pappl_ml_usage_cb_t usage_cb, void *data);" .SH DESCRIPTION The .PP PAPPL mainloop function starts the printer application and supports standard command-line arguments and behaviors. pappl-1.0.3/man/pappl-mainloop.3000066400000000000000000000072301403603036100164140ustar00rootroot00000000000000.TH pappl-mainloop 3 "pappl main loop functions" "2021-02-15" "pappl main loop functions" .SH NAME pappl-mainloop \- pappl main loop functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I int .br .BI papplMainloop "(int argc, char *argv[], const char *version, const char *footer_html, int num_drivers, pappl_driver_t *drivers, pappl_driver_cb_t driver_cb, pappl_ml_autoadd_cb_t autoadd_cb, const char *subcmd_name, pappl_ml_subcmd_cb_t subcmd_cb, pappl_ml_system_cb_t system_cb, pappl_ml_usage_cb_t usage_cb, void *data);" .SH DESCRIPTION The .PP PAPPL mainloop function starts the printer application and supports standard command-line arguments and behaviors. .SH FUNCTIONS .SS papplMainloop Run a standard main loop for printer applications. .PP .nf int papplMainloop ( int argc, char *argv[], const char *version, const char *footer_html, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_driver_cb_t driver_cb, const char *subcmd_name, pappl_ml_subcmd_cb_t subcmd_cb, pappl_ml_system_cb_t system_cb, pappl_ml_usage_cb_t usage_cb, void *data ); .fi .PP This function runs a standard main loop for a printer application. The "argc" and "argv" arguments are those provided to the \fBmain\fR function. .PP The "version" argument specifies a numeric version number for the printer application that conforms to semantic versioning guidelines with up to four numbers, for example "1.2.3.4". .PP The "footer_html" argument specifies HTML text to use in the footer of the web interface. If \fBNULL\fR, the footer is omitted. .PP The "num_drivers", "drivers", and "driver_cb" arguments specify a list of drivers and the driver callback for printers. Specify \fB0\fR and \fBNULL\fR if the drivers are configured in the system callback. The "autoadd_cb" argument specifies a callback for automatically adding new printers with the "autoadd" sub-command and for auto-detecting the driver when adding manually. .PP The "usage_cb" argument specifies a callback that displays a usage/help summary. If \fBNULL\fR, a generic summary is shown as needed. .PP The "subcmd_name" and "subcmd_cb" arguments specify the name and a callback for a custom sub-command. If \fBNULL\fR, no custom sub-commands will be supported. .PP The "system_cb" argument specifies a function that will create a new \fBpappl_system_t\fR object. If \fBNULL\fR, a default system object is created. .PP The "data" argument provides application-specific data for each of the callbacks. .SH TYPES .SS pappl_ml_subcmd_cb_t Sub-command callback .PP .nf typedef int (*pappl_ml_subcmd_cb_t)(const char *base_name, int num_options, cups_option_t *options, int num_files, char **files, void *data); .fi .SS pappl_ml_system_cb_t System callback .PP .nf typedef pappl_system_t * (*pappl_ml_system_cb_t)(int num_options, cups_option_t *options, void *data); .fi .SS pappl_ml_usage_cb_t Program usage callback .PP .nf typedef void (*pappl_ml_usage_cb_t)(void *data); .fi .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-makeresheader.1000066400000000000000000000025321403603036100173740ustar00rootroot00000000000000.\" .\" pappl-makeresheader man page .\" .\" Copyright © 2019-2020 by Michael R Sweet .\" .\" Licensed under Apache License v2.0. See the file "LICENSE" for more .\" information. .\" .TH pappl-makeresheader 1 "pappl-makeresheader" "2020-10-25" "Michael R Sweet" .SH NAME pappl-makeresheader \- resource header utility .SH SYNOPSIS .B pappl-makeresheader .I FILES > .I FILENAME.h .SH DESCRIPTION .B pappl-makeresheader creates a C header file suitable for inclusion in a printer application. The header file creates a C unsigned char array or string constant that can be used with the .B papplSystemAddResourceData or .B papplSystemAddResourceString functions, or as data for a printer icon in the .B pappl_icon_t structure. The C variable names used for each file are generated by replacing all spaces, dots, and dashes with the underscore. For example, a file named "my printer-icon.png" would have the C variable name "my_printer_icon_png". .SH SEE ALSO .BR pappl (1), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-printer-body.man000066400000000000000000000150451403603036100176300ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_printer_s .B pappl_printer_t; .PP .I void .br .BI papplPrinterCancelAllJobs "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterCloseDevice "(pappl_printer_t *printer);" .PP .I pappl_printer_t * .br .BI papplPrinterCreate "(pappl_system_t *system, int printer_id, const char *printer_name, const char *driver_name, const char *device_id, const char *device_uri);" .PP .I void .br .BI papplPrinterDelete "(pappl_printer_t *printer);" .PP .I pappl_job_t * .br .BI papplPrinterFindJob "(pappl_printer_t *printer, int job_id);" .PP .I pappl_contact_t * .br .BI papplPrinterGetContact "(pappl_printer_t *printer, pappl_contact_t *contact);" .PP .I const char * .br .BI papplPrinterGetDeviceID "(pappl_printer_t *printer);" .PP .I const char * .br .BI papplPrinterGetDeviceURI "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetDNSSDName "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I ipp_t * .br .BI papplPrinterGetDriverAttributes "(pappl_printer_t *printer);" .PP .I pappl_driver_data_t * .br .BI papplPrinterGetDriverData "(pappl_printer_t *printer, pappl_driver_data_t *data);" .PP .I const char * .br .BI papplPrinterGetDriverName "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetGeoLocation "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I int .br .BI papplPrinterGetID "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetImpressionsCompleted "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetLocation "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I int .br .BI papplPrinterGetMaxActiveJobs "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetMaxCompletedJobs "(pappl_printer_t *printer);" .PP .I const char * .br .BI papplPrinterGetName "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNextJobID "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNumberOfActiveJobs "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNumberOfCompletedJobs "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNumberOfJobs "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetOrganization "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplPrinterGetOrganizationalUnit "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplPrinterGetPrintGroup "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I pappl_preason_t .br .BI papplPrinterGetReasons "(pappl_printer_t *printer);" .PP .I ipp_pstate_t .br .BI papplPrinterGetState "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetSupplies "(pappl_printer_t *printer, int max_supplies, pappl_supply_t *supplies);" .PP .I pappl_system_t * .br .BI papplPrinterGetSystem "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterIterateActiveJobs "(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit);" .PP .I void .br .BI papplPrinterIterateAllJobs "(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit);" .PP .I void .br .BI papplPrinterIterateCompletedJobs "(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit);" .PP .I pappl_device_t * .br .BI papplPrinterOpenDevice "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterPause "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterResume "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterSetContact "(pappl_printer_t *printer, pappl_contact_t *contact);" .PP .I void .br .BI papplPrinterSetDNSSDName "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetDriverData "(pappl_printer_t *printer, pappl_driver_data_t *data, ipp_t *attrs);" .PP .I void .br .BI papplPrinterSetDriverDefaults "(pappl_printer_t *printer, pappl_driver_data_t *data);" .PP .I void .br .BI papplPrinterSetGeoLocation "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetImpressionsCompleted "(pappl_printer_t *printer, int add);" .PP .I void .br .BI papplPrinterSetLocation "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetMaxActiveJobs "(pappl_printer_t *printer, int max_active_jobs);" .PP .I void .br .BI papplPrinterSetMaxCompletedJobs "(pappl_printer_t *printer, int max_completed_jobs);" .PP .I void .br .BI papplPrinterSetNextJobID "(pappl_printer_t *printer, int next_job_id);" .PP .I void .br .BI papplPrinterSetOrganization "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetOrganizationalUnit "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetPrintGroup "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetReadyMedia "(pappl_printer_t *printer, int num_ready, pappl_media_col_t *ready);" .PP .I void .br .BI papplPrinterSetReasons "(pappl_printer_t *printer, pappl_preason_t add, pappl_preason_t remove);" .PP .I void .br .BI papplPrinterSetSupplies "(pappl_printer_t *printer, int num_supplies, pappl_supply_t *supplies);" .PP .I void .br .BI papplPrinterSetUSB "(pappl_printer_t *printer, unsigned vendor_id, unsigned product_id, pappl_uoptions_t options);" .SH DESCRIPTION The .B PAPPL printer functions provide access to the printer object. Printers are created and deleted by the printer application while the life cycle of the .B pappl_printer_t objects are managed automatically by the system object for the printer application. .PP The .B papplPrinterCreate function creates a new printer while the .B papplPrinterDelete function deletes a printer. .PP The .B papplPrinterPause function stops a printer while the .B papplPrinterResume function starts a printer. .PP The .B papplPrinterGet functions get the current values associated with a printer while the .B papplPrinterSet functions set the current values associated with a printer. .PP The .B papplPrinterCancelAddJobs, papplPrinterFindJob, .B papplPrinterGetNumberOfActiveJobs, papplPrinterGetNumberOfCompletedJobs, .B papplPrinterGetNumberOfJobs, papplPrinterIterateActiveJobs, .B papplPrinterIterateAllJobs, and .B papplPrinterIterateCompletedJobs functions provide access to the jobs queued on a printer. .PP The .B papplPrinterOpenDevice function provides access to the device associated with a printer when it is not processing a job. The device must be later closed using the .B papplPrinterCloseDevice function so that the printer can resume job processing. pappl-1.0.3/man/pappl-printer.3000066400000000000000000001231211403603036100162570ustar00rootroot00000000000000.TH pappl-printer 3 "pappl printer functions" "2021-02-15" "pappl printer functions" .SH NAME pappl-printer \- pappl printer functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_printer_s .B pappl_printer_t; .PP .I void .br .BI papplPrinterCancelAllJobs "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterCloseDevice "(pappl_printer_t *printer);" .PP .I pappl_printer_t * .br .BI papplPrinterCreate "(pappl_system_t *system, int printer_id, const char *printer_name, const char *driver_name, const char *device_id, const char *device_uri);" .PP .I void .br .BI papplPrinterDelete "(pappl_printer_t *printer);" .PP .I pappl_job_t * .br .BI papplPrinterFindJob "(pappl_printer_t *printer, int job_id);" .PP .I pappl_contact_t * .br .BI papplPrinterGetContact "(pappl_printer_t *printer, pappl_contact_t *contact);" .PP .I const char * .br .BI papplPrinterGetDeviceID "(pappl_printer_t *printer);" .PP .I const char * .br .BI papplPrinterGetDeviceURI "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetDNSSDName "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I ipp_t * .br .BI papplPrinterGetDriverAttributes "(pappl_printer_t *printer);" .PP .I pappl_driver_data_t * .br .BI papplPrinterGetDriverData "(pappl_printer_t *printer, pappl_driver_data_t *data);" .PP .I const char * .br .BI papplPrinterGetDriverName "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetGeoLocation "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I int .br .BI papplPrinterGetID "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetImpressionsCompleted "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetLocation "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I int .br .BI papplPrinterGetMaxActiveJobs "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetMaxCompletedJobs "(pappl_printer_t *printer);" .PP .I const char * .br .BI papplPrinterGetName "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNextJobID "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNumberOfActiveJobs "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNumberOfCompletedJobs "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetNumberOfJobs "(pappl_printer_t *printer);" .PP .I char * .br .BI papplPrinterGetOrganization "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplPrinterGetOrganizationalUnit "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplPrinterGetPrintGroup "(pappl_printer_t *printer, char *buffer, size_t bufsize);" .PP .I pappl_preason_t .br .BI papplPrinterGetReasons "(pappl_printer_t *printer);" .PP .I ipp_pstate_t .br .BI papplPrinterGetState "(pappl_printer_t *printer);" .PP .I int .br .BI papplPrinterGetSupplies "(pappl_printer_t *printer, int max_supplies, pappl_supply_t *supplies);" .PP .I pappl_system_t * .br .BI papplPrinterGetSystem "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterIterateActiveJobs "(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit);" .PP .I void .br .BI papplPrinterIterateAllJobs "(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit);" .PP .I void .br .BI papplPrinterIterateCompletedJobs "(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit);" .PP .I pappl_device_t * .br .BI papplPrinterOpenDevice "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterPause "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterResume "(pappl_printer_t *printer);" .PP .I void .br .BI papplPrinterSetContact "(pappl_printer_t *printer, pappl_contact_t *contact);" .PP .I void .br .BI papplPrinterSetDNSSDName "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetDriverData "(pappl_printer_t *printer, pappl_driver_data_t *data, ipp_t *attrs);" .PP .I void .br .BI papplPrinterSetDriverDefaults "(pappl_printer_t *printer, pappl_driver_data_t *data);" .PP .I void .br .BI papplPrinterSetGeoLocation "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetImpressionsCompleted "(pappl_printer_t *printer, int add);" .PP .I void .br .BI papplPrinterSetLocation "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetMaxActiveJobs "(pappl_printer_t *printer, int max_active_jobs);" .PP .I void .br .BI papplPrinterSetMaxCompletedJobs "(pappl_printer_t *printer, int max_completed_jobs);" .PP .I void .br .BI papplPrinterSetNextJobID "(pappl_printer_t *printer, int next_job_id);" .PP .I void .br .BI papplPrinterSetOrganization "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetOrganizationalUnit "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetPrintGroup "(pappl_printer_t *printer, const char *value);" .PP .I void .br .BI papplPrinterSetReadyMedia "(pappl_printer_t *printer, int num_ready, pappl_media_col_t *ready);" .PP .I void .br .BI papplPrinterSetReasons "(pappl_printer_t *printer, pappl_preason_t add, pappl_preason_t remove);" .PP .I void .br .BI papplPrinterSetSupplies "(pappl_printer_t *printer, int num_supplies, pappl_supply_t *supplies);" .PP .I void .br .BI papplPrinterSetUSB "(pappl_printer_t *printer, unsigned vendor_id, unsigned product_id, pappl_uoptions_t options);" .SH DESCRIPTION The .B PAPPL printer functions provide access to the printer object. Printers are created and deleted by the printer application while the life cycle of the .B pappl_printer_t objects are managed automatically by the system object for the printer application. .PP The .B papplPrinterCreate function creates a new printer while the .B papplPrinterDelete function deletes a printer. .PP The .B papplPrinterPause function stops a printer while the .B papplPrinterResume function starts a printer. .PP The .B papplPrinterGet functions get the current values associated with a printer while the .B papplPrinterSet functions set the current values associated with a printer. .PP The .B papplPrinterCancelAddJobs, papplPrinterFindJob, .B papplPrinterGetNumberOfActiveJobs, papplPrinterGetNumberOfCompletedJobs, .B papplPrinterGetNumberOfJobs, papplPrinterIterateActiveJobs, .B papplPrinterIterateAllJobs, and .B papplPrinterIterateCompletedJobs functions provide access to the jobs queued on a printer. .PP The .B papplPrinterOpenDevice function provides access to the device associated with a printer when it is not processing a job. The device must be later closed using the .B papplPrinterCloseDevice function so that the printer can resume job processing. .SH ENUMERATIONS .SS pappl_color_mode_e IPP "print-color-mode" bit values .TP 5 PAPPL_COLOR_MODE_AUTO .br \'auto' - Automatic color/monochrome print mode .TP 5 PAPPL_COLOR_MODE_AUTO_MONOCHROME .br \'auto-monochrome' - Automatic monochrome/process monochrome print mode .TP 5 PAPPL_COLOR_MODE_BI_LEVEL .br \'bi-level' - B&W (threshold) print mode .TP 5 PAPPL_COLOR_MODE_COLOR .br \'color' - Full color print mode .TP 5 PAPPL_COLOR_MODE_MONOCHROME .br \'monochrome' - Grayscale print mode using 1 color .TP 5 PAPPL_COLOR_MODE_PROCESS_MONOCHROME .br \'process-monochrome' - Grayscale print mode using multiple colors .SS pappl_content_e IPP "print-content-optimize" bit values .TP 5 PAPPL_CONTENT_AUTO .br \'auto': Automatically determine content .TP 5 PAPPL_CONTENT_GRAPHIC .br \'graphic': Optimize for vector graphics .TP 5 PAPPL_CONTENT_PHOTO .br \'photo': Optimize for photos/continuous tone images .TP 5 PAPPL_CONTENT_TEXT .br \'text': Optimize for text .TP 5 PAPPL_CONTENT_TEXT_AND_GRAPHIC .br \'text-and-graphic': Optimize for text and vector graphics .SS pappl_duplex_e Duplex printing support .TP 5 PAPPL_DUPLEX_FLIPPED .br Duplex with flipped back sides .TP 5 PAPPL_DUPLEX_MANUAL_TUMBLE .br Duplex with back side rotated 180 degrees for short-edge duplex .TP 5 PAPPL_DUPLEX_NONE .br No duplex printing support .TP 5 PAPPL_DUPLEX_NORMAL .br Duplex with normal back sides .TP 5 PAPPL_DUPLEX_ROTATED .br Duplex with back side rotated 180 degrees for long-edge duplex .SS pappl_finishings_e IPP "finishings" bit values .TP 5 PAPPL_FINISHINGS_NONE .br \'none' .TP 5 PAPPL_FINISHINGS_PUNCH .br \'punch' .TP 5 PAPPL_FINISHINGS_STAPLE .br \'staple' .TP 5 PAPPL_FINISHINGS_TRIM .br \'trim' .SS pappl_identify_actions_e IPP "identify-actions" bit values .TP 5 PAPPL_IDENTIFY_ACTIONS_DISPLAY .br \'display': Display a message .TP 5 PAPPL_IDENTIFY_ACTIONS_FLASH .br \'flash': Flash the display or a light .TP 5 PAPPL_IDENTIFY_ACTIONS_NONE .br No actions .TP 5 PAPPL_IDENTIFY_ACTIONS_SOUND .br \'sound': Make a sound .TP 5 PAPPL_IDENTIFY_ACTIONS_SPEAK .br \'speak': Speak a message .SS pappl_kind_e IPP "printer-kind" bit values .TP 5 PAPPL_KIND_DISC .br \'disc' .TP 5 PAPPL_KIND_DOCUMENT .br \'document' .TP 5 PAPPL_KIND_ENVELOPE .br \'envelope' .TP 5 PAPPL_KIND_LABEL .br \'label' .TP 5 PAPPL_KIND_LARGE_FORMAT .br \'large-format' .TP 5 PAPPL_KIND_PHOTO .br \'photo' .TP 5 PAPPL_KIND_POSTCARD .br \'postcard' .TP 5 PAPPL_KIND_RECEIPT .br \'receipt' .TP 5 PAPPL_KIND_ROLL .br \'roll' .SS pappl_label_mode_e IPP "label-mode-xxx" bit values .TP 5 PAPPL_LABEL_MODE_APPLICATOR .br \'applicator' .TP 5 PAPPL_LABEL_MODE_CUTTER .br \'cutter' .TP 5 PAPPL_LABEL_MODE_CUTTER_DELAYED .br \'cutter-delayed' .TP 5 PAPPL_LABEL_MODE_KIOSK .br \'kiosk' .TP 5 PAPPL_LABEL_MODE_PEEL_OFF .br \'peel-off' .TP 5 PAPPL_LABEL_MODE_PEEL_OFF_PREPEEL .br \'peel-off-prepeel' .TP 5 PAPPL_LABEL_MODE_REWIND .br \'rewind' .TP 5 PAPPL_LABEL_MODE_RFID .br \'rfid' .TP 5 PAPPL_LABEL_MODE_TEAR_OFF .br \'tear-off' .SS pappl_media_tracking_e IPP "media-tracking" bit values .TP 5 PAPPL_MEDIA_TRACKING_CONTINUOUS .br \'continuous' .TP 5 PAPPL_MEDIA_TRACKING_GAP .br \'gap' .TP 5 PAPPL_MEDIA_TRACKING_MARK .br \'mark' .TP 5 PAPPL_MEDIA_TRACKING_WEB .br \'web' .SS pappl_preason_e IPP "printer-state-reasons" bit values .TP 5 PAPPL_PREASON_COVER_OPEN .br \'cover-open' .TP 5 PAPPL_PREASON_DEVICE_STATUS .br Supported \fIpapplDeviceGetStatus\fR bits .TP 5 PAPPL_PREASON_INPUT_TRAY_MISSING .br \'input-tray-missing' .TP 5 PAPPL_PREASON_MARKER_SUPPLY_EMPTY .br \'marker-supply-empty' .TP 5 PAPPL_PREASON_MARKER_SUPPLY_LOW .br \'marker-supply-low' .TP 5 PAPPL_PREASON_MARKER_WASTE_ALMOST_FULL .br \'marker-waste-almost-full' .TP 5 PAPPL_PREASON_MARKER_WASTE_FULL .br \'marker-waste-full' .TP 5 PAPPL_PREASON_MEDIA_EMPTY .br \'media-empty' .TP 5 PAPPL_PREASON_MEDIA_JAM .br \'media-jam' .TP 5 PAPPL_PREASON_MEDIA_LOW .br \'media-low' .TP 5 PAPPL_PREASON_MEDIA_NEEDED .br \'media-needed' .TP 5 PAPPL_PREASON_NONE .br \'none' .TP 5 PAPPL_PREASON_OFFLINE .br \'offline' .TP 5 PAPPL_PREASON_OTHER .br \'other' .TP 5 PAPPL_PREASON_SPOOL_AREA_FULL .br \'spool-area-full' .TP 5 PAPPL_PREASON_TONER_EMPTY .br \'toner-empty' .TP 5 PAPPL_PREASON_TONER_LOW .br \'toner-low' .SS pappl_raster_type_e IPP "pwg-raster-document-type-supported" bit values .TP 5 PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_16 .br 16-bit per component AdobeRGB .TP 5 PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_8 .br 8-bit per component AdobeRGB .TP 5 PAPPL_PWG_RASTER_TYPE_BLACK_1 .br 1-bit (device) black .TP 5 PAPPL_PWG_RASTER_TYPE_BLACK_16 .br 16-bit (device) black .TP 5 PAPPL_PWG_RASTER_TYPE_BLACK_8 .br 8-bit (device) black .TP 5 PAPPL_PWG_RASTER_TYPE_CMYK_16 .br 16-bit per component (device) CMYK .TP 5 PAPPL_PWG_RASTER_TYPE_CMYK_8 .br 8-bit per component (device) CMYK .TP 5 PAPPL_PWG_RASTER_TYPE_NONE .br Do not force a particular raster type .TP 5 PAPPL_PWG_RASTER_TYPE_RGB_16 .br 16-bit per component (device) RGB) .TP 5 PAPPL_PWG_RASTER_TYPE_RGB_8 .br 8-bit per component (device) RGB .TP 5 PAPPL_PWG_RASTER_TYPE_SGRAY_16 .br 16-bit grayscale with 2.2 gamma .TP 5 PAPPL_PWG_RASTER_TYPE_SGRAY_8 .br 8-bit grayscale with 2.2 gamma .TP 5 PAPPL_PWG_RASTER_TYPE_SRGB_16 .br 16-bit per component sRGB .TP 5 PAPPL_PWG_RASTER_TYPE_SRGB_8 .br 8-bit per component sRGB .SS pappl_scaling_e IPP "print-scaling" bit values .TP 5 PAPPL_SCALING_AUTO .br \'auto': Scale to fit (non-borderless) or fill (borderless) if larger, otherwise center .TP 5 PAPPL_SCALING_AUTO_FIT .br \'auto-fit': Scale to fit if larger, otherwise center .TP 5 PAPPL_SCALING_FILL .br \'fill': Scale to fill the media .TP 5 PAPPL_SCALING_FIT .br \'fit': Scale to fit within margins .TP 5 PAPPL_SCALING_NONE .br \'none': No scaling (center/crop) .SS pappl_sides_e IPP "sides" bit values .TP 5 PAPPL_SIDES_ONE_SIDED .br \'one-sided' .TP 5 PAPPL_SIDES_TWO_SIDED_LONG_EDGE .br \'two-sided-long-edge' .TP 5 PAPPL_SIDES_TWO_SIDED_SHORT_EDGE .br \'two-sided-short-edge' .SS pappl_supply_color_e "printer-supply" color values .TP 5 PAPPL_SUPPLY_COLOR_BLACK .br Black ink/toner (photo or matte) .TP 5 PAPPL_SUPPLY_COLOR_CYAN .br Cyan ink/toner .TP 5 PAPPL_SUPPLY_COLOR_GRAY .br Gray ink (sometimes marketed as light gray) .TP 5 PAPPL_SUPPLY_COLOR_GREEN .br Green ink .TP 5 PAPPL_SUPPLY_COLOR_LIGHT_CYAN .br Light cyan ink .TP 5 PAPPL_SUPPLY_COLOR_LIGHT_GRAY .br Light gray ink (sometimes marketed as light light gray) .TP 5 PAPPL_SUPPLY_COLOR_LIGHT_MAGENTA .br Light magenta ink .TP 5 PAPPL_SUPPLY_COLOR_MAGENTA .br Magenta ink/toner .TP 5 PAPPL_SUPPLY_COLOR_NO_COLOR .br No color (waste tank, etc.) .TP 5 PAPPL_SUPPLY_COLOR_ORANGE .br Orange ink .TP 5 PAPPL_SUPPLY_COLOR_VIOLET .br Violet ink .TP 5 PAPPL_SUPPLY_COLOR_YELLOW .br Yellow ink/toner .SS pappl_supply_type_e IPP "printer-supply" type values .TP 5 PAPPL_SUPPLY_TYPE_BANDING_SUPPLY .br Banding finisher supplies .TP 5 PAPPL_SUPPLY_TYPE_BINDING_SUPPLY .br Binding finisher supplies .TP 5 PAPPL_SUPPLY_TYPE_CLEANER_UNIT .br Cleaning unit .TP 5 PAPPL_SUPPLY_TYPE_CORONA_WIRE .br Corona wire (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_COVERS .br Cover finisher supplies .TP 5 PAPPL_SUPPLY_TYPE_DEVELOPER .br Developer supply .TP 5 PAPPL_SUPPLY_TYPE_FUSER .br Fuser (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_FUSER_CLEANING_PAD .br Fuser cleaning pad (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_FUSER_OIL .br Fuser oil supply (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_FUSER_OILER .br Fuser oiler (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_FUSER_OIL_WICK .br Fuser oil wick (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_INK .br Ink supply .TP 5 PAPPL_SUPPLY_TYPE_INK_CARTRIDGE .br Ink cartridge .TP 5 PAPPL_SUPPLY_TYPE_INK_RIBBON .br Ink ribbon supply .TP 5 PAPPL_SUPPLY_TYPE_INSERTS .br Insert finisher supplies .TP 5 PAPPL_SUPPLY_TYPE_OPC .br Optical photoconductor (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_PAPER_WRAP .br Wrap finisher supplies .TP 5 PAPPL_SUPPLY_TYPE_RIBBON_WAX .br Wax ribbon supply .TP 5 PAPPL_SUPPLY_TYPE_SOLID_WAX .br Solid wax supply .TP 5 PAPPL_SUPPLY_TYPE_STAPLES .br Staple finisher supplies .TP 5 PAPPL_SUPPLY_TYPE_STITCHING_WIRE .br Staple/stitch finisher supplies .TP 5 PAPPL_SUPPLY_TYPE_TONER .br Toner supply .TP 5 PAPPL_SUPPLY_TYPE_TONER_CARTRIDGE .br Toner cartridge .TP 5 PAPPL_SUPPLY_TYPE_TRANSFER_UNIT .br Transfer unit (laser printers) .TP 5 PAPPL_SUPPLY_TYPE_WASTE_INK .br Waste ink .TP 5 PAPPL_SUPPLY_TYPE_WASTE_TONER .br Waste toner .TP 5 PAPPL_SUPPLY_TYPE_WASTE_WATER .br Waste water .TP 5 PAPPL_SUPPLY_TYPE_WASTE_WAX .br Waste wax .TP 5 PAPPL_SUPPLY_TYPE_WATER .br Water supply .SS pappl_uoptions_e USB gadget options .TP 5 PAPPL_UOPTIONS_ETHERNET .br Include USB ethernet gadget .TP 5 PAPPL_UOPTIONS_NONE .br No options (just USB printer) .TP 5 PAPPL_UOPTIONS_SERIAL .br Include USB serial gadget .TP 5 PAPPL_UOPTIONS_STORAGE .br Include USB mass storage gadget .TP 5 PAPPL_UOPTIONS_STORAGE_READONLY .br USB mass storage gadget is read-only .TP 5 PAPPL_UOPTIONS_STORAGE_REMOVABLE .br USB mass storage gadget is removable .SH FUNCTIONS .SS papplPrinterCancelAllJobs Cancel all jobs on the printer. .PP .nf void papplPrinterCancelAllJobs ( pappl_printer_t *printer ); .fi .PP This function cancels all jobs on the printer. If any job is currently being printed, it will be stopped at a convenient time (usually the end of a page) so that the printer will be left in a known state. .SS papplPrinterCloseDevice Close the device associated with the printer. .PP .nf void papplPrinterCloseDevice ( pappl_printer_t *printer ); .fi .PP This function closes the device for a printer. The device must have been previously opened using the \fIpapplPrinterOpenDevice\fR function. .SS papplPrinterCreate Create a new printer. .PP .nf pappl_printer_t * papplPrinterCreate ( pappl_system_t *system, int printer_id, const char *printer_name, const char *driver_name, const char *device_id, const char *device_uri ); .fi .PP This function creates a new printer (service) on the specified system. The "type" argument specifies the type of service to create and must currently be the value \fBPAPPL_SERVICE_TYPE_PRINT\fR. .PP The "printer_id" argument specifies a positive integer identifier that is unique to the system. If you specify a value of \fB0\fR a new identifier will be assigned. .PP The "driver_name" argument specifies a named driver for the printer, from the list of drivers registered with the \fIpapplSystemSetPrinterDrivers\fR function. .PP The "device_id" and "device_uri" arguments specify the IEEE-1284 device ID and device URI strings for the printer. .PP On error, this function sets the \fBerrno\fR variable to one of the following values: .PP .IP \(bu 5 \fBEEXIST\fR: A printer with the specified name already exists. .IP \(bu 5 \fBEINVAL\fR: Bad values for the arguments were specified. .IP \(bu 5 \fBEIO\fR: The driver callback failed. .IP \(bu 5 \fBENOENT\fR: No driver callback has been set. .IP \(bu 5 \fBENOMEM\fR: Ran out of memory. .SS papplPrinterDelete Delete a printer. .PP .nf void papplPrinterDelete ( pappl_printer_t *printer ); .fi .PP This function deletes a printer from a system, freeing all memory and canceling all jobs as needed. .SS papplPrinterGetContact Get the "printer-contact" value. .PP .nf pappl_contact_t * papplPrinterGetContact ( pappl_printer_t *printer, pappl_contact_t *contact ); .fi .PP This function copies the current printer contact information to the buffer pointed to by the "contact" argument. .SS papplPrinterGetDNSSDName Get the current DNS-SD service name. .PP .nf char * papplPrinterGetDNSSDName ( pappl_printer_t *printer, char *buffer, size_t bufsize ); .fi .PP This function copies the current DNS-SD service name to the buffer pointed to by the "buffer" argument. .SS papplPrinterGetDeviceID Get the IEEE-1284 device ID of the printer. .PP .nf const char * papplPrinterGetDeviceID ( pappl_printer_t *printer ); .fi .PP This function returns the IEEE-1284 device ID of the printer. .SS papplPrinterGetDeviceURI Get the URI of the device associated with the printer. .PP .nf const char * papplPrinterGetDeviceURI ( pappl_printer_t *printer ); .fi .PP This function returns the device URI for the printer. .SS papplPrinterGetDriverAttributes Get a copy of the current driver attributes. .PP .nf ipp_t * papplPrinterGetDriverAttributes ( pappl_printer_t *printer ); .fi .PP This function returns a copy the current driver attributes. Use the \fBippDelete\fR function to free the memory used for the attributes when you are done. .SS papplPrinterGetDriverData Get the current print driver data. .PP .nf pappl_pr_driver_data_t * papplPrinterGetDriverData ( pappl_printer_t *printer, pappl_pr_driver_data_t *data ); .fi .PP This function copies the current print driver data, defaults, and ready (loaded) media information into the specified buffer. .SS papplPrinterGetDriverName Get the driver name for a printer. .PP .nf const char * papplPrinterGetDriverName ( pappl_printer_t *printer ); .fi .PP This function returns the driver name for the printer. .SS papplPrinterGetGeoLocation Get the current geo-location as a "geo:" URI. .PP .nf char * papplPrinterGetGeoLocation ( pappl_printer_t *printer, char *buffer, size_t bufsize ); .fi .PP This function copies the currently configured geographic location as a "geo:" URI to the buffer pointed to by the "buffer" argument. .SS papplPrinterGetID Get the printer ID. .PP .nf int papplPrinterGetID ( pappl_printer_t *printer ); .fi .PP This function returns the printer's unique positive integer identifier. .SS papplPrinterGetImpressionsCompleted Get the number of impressions (sides) that have been printed. .PP .nf int papplPrinterGetImpressionsCompleted ( pappl_printer_t *printer ); .fi .PP This function returns the number of impressions that have been printed. An impression is one side of an output page. .SS papplPrinterGetLocation Get the location string. .PP .nf char * papplPrinterGetLocation ( pappl_printer_t *printer, char *buffer, size_t bufsize ); .fi .PP This function copies the printer's human-readable location to the buffer pointed to by the "buffer" argument. .SS papplPrinterGetMaxActiveJobs Get the maximum number of active (queued) jobs allowed by the printer. .PP .nf int papplPrinterGetMaxActiveJobs ( pappl_printer_t *printer ); .fi .PP This function returns the maximum number of active jobs that the printer supports, as configured by the \fIpapplPrinterSetMaxActiveJobs\fR function. .SS papplPrinterGetMaxCompletedJobs Get the maximum number of jobs retained for history by the printer. .PP .nf int papplPrinterGetMaxCompletedJobs ( pappl_printer_t *printer ); .fi .PP This function returns the maximum number of jobs that are retained in the job history as configured by the \fIpapplPrinterSetMaxCompletedJobs\fR function. .SS papplPrinterGetName Get the printer name. .PP .nf const char * papplPrinterGetName ( pappl_printer_t *printer ); .fi .PP This function returns the printer's human-readable name. .SS papplPrinterGetNextJobID Get the next job ID. .PP .nf int papplPrinterGetNextJobID ( pappl_printer_t *printer ); .fi .PP This function returns the positive integer identifier that will be used for the next job that is created. .SS papplPrinterGetNumberOfActiveJobs Get the number of active print jobs. .PP .nf int papplPrinterGetNumberOfActiveJobs ( pappl_printer_t *printer ); .fi .PP This function returns the number of print jobs that are either printing or waiting to be printed. .SS papplPrinterGetNumberOfCompletedJobs Get the number of completed print jobs. .PP .nf int papplPrinterGetNumberOfCompletedJobs ( pappl_printer_t *printer ); .fi .PP This function returns the number of print jobs that have been aborted, canceled, or completed. .SS papplPrinterGetNumberOfJobs Get the total number of print jobs. .PP .nf int papplPrinterGetNumberOfJobs ( pappl_printer_t *printer ); .fi .PP This function returns the number of print jobs that are printing, waiting to be printed, have been aborted, have been canceled, or have completed. .SS papplPrinterGetOrganization Get the organization name. .PP .nf char * papplPrinterGetOrganization ( pappl_printer_t *printer, char *buffer, size_t bufsize ); .fi .PP This function copies the printer's organization name to the buffer pointed to by the "buffer" argument. .SS papplPrinterGetOrganizationalUnit Get the organizational unit name. .PP .nf char * papplPrinterGetOrganizationalUnit ( pappl_printer_t *printer, char *buffer, size_t bufsize ); .fi .PP This function copies the printer's organizational unit name to the buffer pointed to by the "buffer" argument. .SS papplPrinterGetPath Get the URL path for a printer web page. .PP .nf char * papplPrinterGetPath ( pappl_printer_t *printer, const char *subpath, char *buffer, size_t bufsize ); .fi .PP This function generates and returns the URL path for the printer's web page. The "subpath" argument specifies an optional sub-path for a specific printer web page. .SS papplPrinterGetPrintGroup Get the print authorization group, if any. .PP .nf char * papplPrinterGetPrintGroup ( pappl_printer_t *printer, char *buffer, size_t bufsize ); .fi .PP This function copies the printer's authorization group name to the buffer pointed to by the "buffer" argument. .SS papplPrinterGetReasons Get the current "printer-state-reasons" bit values. .PP .nf pappl_preason_t papplPrinterGetReasons ( pappl_printer_t *printer ); .fi .PP This function returns the current printer state reasons bitfield, which can be updated by the printer driver and/or by the \fIpapplPrinterSetReasons\fR function. .SS papplPrinterGetState Get the current "printer-state" value. .PP .nf ipp_pstate_t papplPrinterGetState ( pappl_printer_t *printer ); .fi .PP This function returns the current printer state as an enumeration: .PP .IP \(bu 5 \fBIPP_PSTATE_IDLE\fR: The printer is idle and has no jobs to process. .IP \(bu 5 \fBIPP_PSTATE_PROCESSING\fR: The printer is processing a job and/or producing output. .IP \(bu 5 \fBIPP_PSTATE_STOPPED\fR: The printer is stopped for maintenance. .SS papplPrinterGetSupplies Get the current "printer-supplies" values. .PP .nf int papplPrinterGetSupplies ( pappl_printer_t *printer, int max_supplies, pappl_supply_t *supplies ); .fi .PP This function copies the current printer supply values to the specified array. The "max_supplies" and "supplies" arguments can be \fB0\fR and \fBNULL\fR to query the number of supplies used. .PP The return value is the actual number of supplies used by the printer, regardless of the size of the array. .SS papplPrinterGetSystem Get the system associated with the printer. .PP .nf pappl_system_t * papplPrinterGetSystem ( pappl_printer_t *printer ); .fi .PP This function returns a pointer to the system object that contains the printer. .SS papplPrinterIterateActiveJobs Iterate over the active jobs. .PP .nf void papplPrinterIterateActiveJobs ( pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int job_index, int limit ); .fi .PP This function iterates over jobs that are either printing or waiting to be printed. The specified callback "cb" will be called once per job with the data pointer "data". .PP The "job_index" argument specifies the first job in the list to iterate, where \fB1\fR is the first job, etc. The "limit" argument specifies the maximum number of jobs to iterate - use \fB0\fR to iterate an unlimited number of jobs. .SS papplPrinterIterateAllJobs Iterate over all the jobs. .PP .nf void papplPrinterIterateAllJobs ( pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int job_index, int limit ); .fi .PP This function iterates over all jobs. The specified callback "cb" will be called once per job with the data pointer "data". .PP The "job_index" argument specifies the first job in the list to iterate, where \fB1\fR is the first job, etc. The "limit" argument specifies the maximum number of jobs to iterate - use \fB0\fR to iterate an unlimited number of jobs. .SS papplPrinterIterateCompletedJobs Iterate over the completed jobs. .PP .nf void papplPrinterIterateCompletedJobs ( pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int job_index, int limit ); .fi .PP This function iterates over jobs that are aborted, canceled, or completed. The specified callback "cb" will be called once per job with the data pointer "data". .PP The "job_index" argument specifies the first job in the list to iterate, where \fB1\fR is the first job, etc. The "limit" argument specifies the maximum number of jobs to iterate - use \fB0\fR to iterate an unlimited number of jobs. .SS papplPrinterOpenDevice Open the device associated with a printer. .PP .nf pappl_device_t * papplPrinterOpenDevice ( pappl_printer_t *printer ); .fi .PP This function opens the printer's device. \fBNULL\fR is returned if the device is already in use, for example while a job is being printed. .PP The returned device must be closed using the \fIpapplPrinterCloseDevice\fR function. .SS papplPrinterPause Pause (stop) a printer. .PP .nf void papplPrinterPause ( pappl_printer_t *printer ); .fi .PP This function pauses a printer. If the printer is currently processing (printing) a job, it will be completed before the printer is stopped. .SS papplPrinterResume Resume (start) a printer. .PP .nf void papplPrinterResume ( pappl_printer_t *printer ); .fi .PP This function resumes a printer and starts processing any pending jobs. .SS papplPrinterSetContact Set the "printer-contact" value. .PP .nf void papplPrinterSetContact ( pappl_printer_t *printer, pappl_contact_t *contact ); .fi .PP This function sets the printer's contact information. .SS papplPrinterSetDNSSDName Set the DNS-SD service name. .PP .nf void papplPrinterSetDNSSDName ( pappl_printer_t *printer, const char *value ); .fi .PP This function sets the printer's DNS-SD service name. If \fBNULL\fR, the printer will stop advertising the printer. .SS papplPrinterSetDriverData Set the driver data. .PP .nf bool papplPrinterSetDriverData ( pappl_printer_t *printer, pappl_pr_driver_data_t *data, ipp_t *attrs ); .fi .PP This function validates and sets the driver data, including all defaults and ready (loaded) media. .PP .IP 5 Note: This function regenerates all of the driver-specific capability .IP 5 attributes like "media-col-database", "sides-supported", and so forth. .IP 5 Use the \fIpapplPrinterSetDriverDefaults\fR or .IP 5 \fIpapplPrinterSetReadyMedia\fR functions to efficiently change the .IP 5 "xxx-default" or "xxx-ready" values, respectively. .SS papplPrinterSetDriverDefaults Set the default print option values. .PP .nf bool papplPrinterSetDriverDefaults ( pappl_printer_t *printer, pappl_pr_driver_data_t *data, int num_vendor, cups_option_t *vendor ); .fi .PP This function validates and sets the printer's default print options. .PP .IP 5 Note: Unlike \fIpapplPrinterSetPrintDriverData\fR, this function only .IP 5 changes the "xxx_default" members of the driver data and is considered .IP 5 lightweight. .SS papplPrinterSetGeoLocation Set the geo-location value as a "geo:" URI. .PP .nf void papplPrinterSetGeoLocation ( pappl_printer_t *printer, const char *value ); .fi .PP This function sets the printer's geographic location as a "geo:" URI. If \fBNULL\fR, the location is cleared to the 'unknown' value. .SS papplPrinterSetImpressionsCompleted Add impressions (sides) to the total count of printed impressions. .PP .nf void papplPrinterSetImpressionsCompleted ( pappl_printer_t *printer, int add ); .fi .PP This function adds to the printer's impressions counter. An impression is one side of an output page. .SS papplPrinterSetLocation Set the location string. .PP .nf void papplPrinterSetLocation ( pappl_printer_t *printer, const char *value ); .fi .PP This function sets the printer's human-readable location string. If \fBNULL\fR, the location is cleared. .SS papplPrinterSetMaxActiveJobs Set the maximum number of active jobs for the printer. .PP .nf void papplPrinterSetMaxActiveJobs ( pappl_printer_t *printer, int max_active_jobs ); .fi .PP This function sets the maximum number of jobs that can be spooled on the printer at one time. .PP .IP 5 Note: This limit does not apply to streaming raster formats such as PWG .IP 5 Raster since they are not spooled. .SS papplPrinterSetMaxCompletedJobs Set the maximum number of completed jobs for the printer. .PP .nf void papplPrinterSetMaxCompletedJobs ( pappl_printer_t *printer, int max_completed_jobs ); .fi .PP This function sets the maximum number of aborted, canceled, or completed jobs that are retained in the job history. .SS papplPrinterSetNextJobID Set the next "job-id" value. .PP .nf void papplPrinterSetNextJobID ( pappl_printer_t *printer, int next_job_id ); .fi .PP This function sets the next unique positive integer identifier that will be used for a job. .PP .IP 5 Note: This function is normally only called once to restore the previous .IP 5 state of the printer. .SS papplPrinterSetOrganization Set the organization name. .PP .nf void papplPrinterSetOrganization ( pappl_printer_t *printer, const char *value ); .fi .PP This function sets the printer's organization name. If \fBNULL\fR the value is cleared. .SS papplPrinterSetOrganizationalUnit Set the organizational unit name. .PP .nf void papplPrinterSetOrganizationalUnit ( pappl_printer_t *printer, const char *value ); .fi .PP This function sets the printer's organizational unit name. If \fBNULL\fR the value is cleared. .SS papplPrinterSetPrintGroup Set the print authorization group, if any. .PP .nf void papplPrinterSetPrintGroup ( pappl_printer_t *printer, const char *value ); .fi .PP This function sets the printer's authorization group. If \fBNULL\fR, the group is cleared. .PP .IP 5 Note: The authorization group is only used if the system is created with a .IP 5 named authorization service. .SS papplPrinterSetReadyMedia Set the ready (loaded) media. .PP .nf bool papplPrinterSetReadyMedia ( pappl_printer_t *printer, int num_ready, pappl_media_col_t *ready ); .fi .PP This function validates and sets the printer's ready (loaded) media. .SS papplPrinterSetReasons Add or remove values from "printer-state-reasons". .PP .nf void papplPrinterSetReasons ( pappl_printer_t *printer, pappl_preason_t add, pappl_preason_t remove ); .fi .PP This function updates the printer state reasons bitfield by clearing any bit values in the "remove" argument and setting any bit values in the "add" argument. .SS papplPrinterSetSupplies Set/update the supplies for a printer. .PP .nf void papplPrinterSetSupplies ( pappl_printer_t *printer, int num_supplies, pappl_supply_t *supplies ); .fi .PP This function updates the supply information for the printer. .SS papplPrinterSetUSB Set the USB vendor and product IDs for a printer. .PP .nf void papplPrinterSetUSB ( pappl_printer_t *printer, unsigned vendor_id, unsigned product_id, pappl_uoptions_t options, const char *storagefile ); .fi .PP This function sets the USB vendor and product IDs for a printer as well as specifying USB gadget options when the printer is registered with the USB device controller. .PP .IP 5 Note: USB gadget functionality is currently only available when running .IP 5 on Linux with compatible hardware such as the Raspberry Pi. .SH STRUCTURES .SS pappl_icon_s Printer PNG icon structure .PP .nf struct pappl_icon_s { const void *data; size_t datalen; char filename[256]; }; .fi .SS pappl_media_col_s Media details structure .PP .nf struct pappl_media_col_s { int bottom_margin; int left_margin; int left_offset; int right_margin; int size_length; char size_name[64]; int size_width; char source[64]; int top_margin; int top_offset; pappl_media_tracking_t tracking; char type[64]; }; .fi .SS pappl_pr_driver_data_s Printer driver data .PP .nf struct pappl_pr_driver_data_s { const char *bin[PAPPL_MAX_BIN]; int bin_default; bool borderless; int bottom_top; pappl_color_mode_t color_default; pappl_color_mode_t color_supported; pappl_content_t content_default; int darkness_configured; int darkness_default; int darkness_supported; pappl_pr_delete_cb_t delete_cb; pappl_duplex_t duplex; void *extension; const char *features[PAPPL_MAX_VENDOR]; pappl_finishings_t finishings; pappl_raster_type_t force_raster_type; const char *format; pappl_dither_t gdither; bool has_supplies; pappl_icon_t icons[3]; pappl_pr_identify_cb_t identify_cb; pappl_identify_actions_t identify_default; pappl_identify_actions_t identify_supported; bool input_face_up; pappl_kind_t kind; int left_offset_supported[2]; int left_right; char make_and_model[128]; const char *media[PAPPL_MAX_MEDIA]; pappl_media_col_t media_default; pappl_media_col_t media_ready[PAPPL_MAX_SOURCE]; pappl_label_mode_t mode_configured; pappl_label_mode_t mode_supported; int num_bin; int num_features; int num_media; int num_resolution; int num_source; int num_type; int num_vendor; ipp_orient_t orient_default; bool output_face_up; pappl_dither_t pdither; int ppm; int ppm_color; pappl_pr_printfile_cb_t printfile_cb; ipp_quality_t quality_default; pappl_raster_type_t raster_types; pappl_pr_rendjob_cb_t rendjob_cb; pappl_pr_rendpage_cb_t rendpage_cb; pappl_pr_rstartjob_cb_t rstartjob_cb; pappl_pr_rstartpage_cb_t rstartpage_cb; pappl_pr_rwriteline_cb_t rwriteline_cb; pappl_scaling_t scaling_default; pappl_sides_t sides_default; pappl_sides_t sides_supported; const char *source[PAPPL_MAX_SOURCE]; int speed_default; int speed_supported[2]; pappl_pr_status_cb_t status_cb; int tear_offset_configured; int tear_offset_supported[2]; pappl_pr_testpage_cb_t testpage_cb; int top_offset_supported[2]; pappl_media_tracking_t tracking_supported; const char *type[PAPPL_MAX_TYPE]; const char *vendor[PAPPL_MAX_VENDOR]; int x_default; int x_resolution[PAPPL_MAX_RESOLUTION]; int y_default; int y_resolution[PAPPL_MAX_RESOLUTION]; }; .fi .SS pappl_pr_options_s Combined print job options .PP .nf struct pappl_pr_options_s { int copies; int darkness_configured; pappl_dither_t dither; pappl_finishings_t finishings; unsigned first_page; cups_page_header2_t header; unsigned last_page; pappl_media_col_t media; unsigned num_pages; int num_vendor; ipp_orient_t orientation_requested; char output_bin[64]; pappl_color_mode_t print_color_mode; pappl_content_t print_content_optimize; int print_darkness; ipp_quality_t print_quality; pappl_scaling_t print_scaling; int print_speed; int printer_resolution[2]; pappl_sides_t sides; cups_option_t *vendor; }; .fi .SS pappl_supply_s Supply data .PP .nf struct pappl_supply_s { pappl_supply_color_t color; char description[256]; bool is_consumed; int level; pappl_supply_type_t type; }; .fi .SH TYPES .SS pappl_color_mode_t Bitfield for IPP "print-color-mode" values .PP .nf typedef unsigned pappl_color_mode_t; .fi .SS pappl_content_t Bitfield for IPP "print-content-optimize" values .PP .nf typedef unsigned pappl_content_t; .fi .SS pappl_default_cb_t papplIterateDefaults callback .PP .nf typedef void (*pappl_default_cb_t)(ipp_attribute_t *attr, void *data); .fi .SS pappl_duplex_t Duplex printing support .PP .nf typedef enum pappl_duplex_e pappl_duplex_t; .fi .SS pappl_finishings_t Bitfield for IPP "finishings" values .PP .nf typedef unsigned pappl_finishings_t; .fi .SS pappl_icon_t Printer PNG icon structure .PP .nf typedef struct pappl_icon_s pappl_icon_t; .fi .SS pappl_identify_actions_t Bitfield for IPP "identify-actions" values .PP .nf typedef unsigned pappl_identify_actions_t; .fi .SS pappl_job_cb_t papplIterateXxxJobs callback function .PP .nf typedef void (*pappl_job_cb_t)(pappl_job_t *job, void *data); .fi .SS pappl_kind_t Bitfield for IPP "printer-kind" values .PP .nf typedef unsigned pappl_kind_t; .fi .SS pappl_label_mode_t Bitfield for IPP "label-mode-xxx" values .PP .nf typedef unsigned short pappl_label_mode_t; .fi .SS pappl_media_col_t Media details structure .PP .nf typedef struct pappl_media_col_s pappl_media_col_t; .fi .SS pappl_media_tracking_t Bitfield for IPP "media-tracking" values .PP .nf typedef unsigned short pappl_media_tracking_t; .fi .SS pappl_pr_delete_cb_t Printer deletion callback .PP .nf typedef void (*pappl_pr_delete_cb_t)(pappl_printer_t *printer, pappl_pr_driver_data_t *data); .fi .SS pappl_pr_identify_cb_t Identify-Printer callback .PP .nf typedef void (*pappl_pr_identify_cb_t)(pappl_printer_t *printer, pappl_identify_actions_t actions, const char *message); .fi .SS pappl_pr_printfile_cb_t Print a "raw" job callback .PP .nf typedef bool (*pappl_pr_printfile_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); .fi .SS pappl_pr_rendjob_cb_t End a raster job callback .PP .nf typedef bool (*pappl_pr_rendjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); .fi .SS pappl_pr_rendpage_cb_t End a raster page callback .PP .nf typedef bool (*pappl_pr_rendpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); .fi .SS pappl_pr_rstartjob_cb_t Start a raster job callback .PP .nf typedef bool (*pappl_pr_rstartjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); .fi .SS pappl_pr_rstartpage_cb_t Start a raster page callback .PP .nf typedef bool (*pappl_pr_rstartpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); .fi .SS pappl_pr_rwriteline_cb_t Write a line of raster graphics callback .PP .nf typedef bool (*pappl_pr_rwriteline_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned y, const unsigned char *line); .fi .SS pappl_pr_status_cb_t Update printer status callback .PP .nf typedef bool (*pappl_pr_status_cb_t)(pappl_printer_t *printer); .fi .SS pappl_pr_testpage_cb_t Print a test page callback .PP .nf typedef const char * (*pappl_pr_testpage_cb_t)(pappl_printer_t *printer, char *buffer, size_t bufsize); .fi .SS pappl_raster_type_t Bitfield for IPP "pwg-raster-document-type-supported" values .PP .nf typedef unsigned pappl_raster_type_t; .fi .SS pappl_scaling_t Bitfield for IPP "print-scaling" values .PP .nf typedef unsigned pappl_scaling_t; .fi .SS pappl_sides_t Bitfield for IPP "sides" values .PP .nf typedef unsigned pappl_sides_t; .fi .SS pappl_supply_color_t "printer-supply" color values .PP .nf typedef enum pappl_supply_color_e pappl_supply_color_t; .fi .SS pappl_supply_t Supply data .PP .nf typedef struct pappl_supply_s pappl_supply_t; .fi .SS pappl_supply_type_t IPP "printer-supply" type values .PP .nf typedef enum pappl_supply_type_e pappl_supply_type_t; .fi .SS pappl_uoptions_t USB gadget options bitfield .PP .nf typedef unsigned pappl_uoptions_t; .fi .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-resource-body.man000066400000000000000000000032071403603036100177710ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_printer_s .B pappl_printer_t; .PP .I typedef struct _pappl_system_s .B pappl_system_t; .PP .I void .br .BI papplPrinterAddLink "(pappl_printer_t *printer, const char *label, const char *path_or_url, bool secure);" .PP .I void .br .BI papplSystemAddLink "(pappl_system_t *system, const char *label, const char *path_or_url, bool secure);" .PP .I void .br .BI papplSystemAddResourceCallback "(pappl_system_t *system, const char *path, const char *format, pappl_resource_cb_t cb, void *data);" .PP .I void .br .BI papplSystemAddResourceData "(pappl_system_t *system, const char *path, const char *format, const void *data, size_t datalen);" .PP .I void .br .BI papplSystemAddResourceDirectory "(pappl_system_t *system, const char *basepath, const char *directory);" .PP .I void .br .BI papplSystemAddResourceFile "(pappl_system_t *system, const char *path, const char *format, const char *filename);" .PP .I void .br .BI papplSystemAddResourceString "(pappl_system_t *system, const char *path, const char *format, const char *data);" .PP .I void .br .BI papplSystemAddStringsData "(pappl_system_t *system, const char *path, const char *language, const char *data);" .PP .I void .br .BI papplSystemAddStringsFile "(pappl_system_t *system, const char *path, const char *language, const char *filename);" .PP .I void .br .BI papplSystemRemoveResource "(pappl_system_t *system, const char *path);" .SH DESCRIPTION The .B PAPPL resource functions manage resource files, callbacks, and links in the printer application. pappl-1.0.3/man/pappl-resource.3000066400000000000000000000211471403603036100164300ustar00rootroot00000000000000.TH pappl-resource 3 "pappl resource functions" "2021-02-15" "pappl resource functions" .SH NAME pappl-resource \- pappl resource functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_printer_s .B pappl_printer_t; .PP .I typedef struct _pappl_system_s .B pappl_system_t; .PP .I void .br .BI papplPrinterAddLink "(pappl_printer_t *printer, const char *label, const char *path_or_url, bool secure);" .PP .I void .br .BI papplSystemAddLink "(pappl_system_t *system, const char *label, const char *path_or_url, bool secure);" .PP .I void .br .BI papplSystemAddResourceCallback "(pappl_system_t *system, const char *path, const char *format, pappl_resource_cb_t cb, void *data);" .PP .I void .br .BI papplSystemAddResourceData "(pappl_system_t *system, const char *path, const char *format, const void *data, size_t datalen);" .PP .I void .br .BI papplSystemAddResourceDirectory "(pappl_system_t *system, const char *basepath, const char *directory);" .PP .I void .br .BI papplSystemAddResourceFile "(pappl_system_t *system, const char *path, const char *format, const char *filename);" .PP .I void .br .BI papplSystemAddResourceString "(pappl_system_t *system, const char *path, const char *format, const char *data);" .PP .I void .br .BI papplSystemAddStringsData "(pappl_system_t *system, const char *path, const char *language, const char *data);" .PP .I void .br .BI papplSystemAddStringsFile "(pappl_system_t *system, const char *path, const char *language, const char *filename);" .PP .I void .br .BI papplSystemRemoveResource "(pappl_system_t *system, const char *path);" .SH DESCRIPTION The .B PAPPL resource functions manage resource files, callbacks, and links in the printer application. .SH FUNCTIONS .SS papplPrinterAddLink Add a printer link to the navigation header. .PP .nf void papplPrinterAddLink ( pappl_printer_t *printer, const char *label, const char *path_or_url, pappl_loptions_t options ); .fi .PP This function adds a navigation link for a printer. The "path_or_url" argument specifies a absolute path such as "/ipp/print/example/page" or an absolute URL such as "https://www.example.com/". The "options" argument specifies where the link is shown and whether the link should redirect an absolute path to the secure ("https://.../path") web interface. .SS papplPrinterRemoveLink Remove a printer link from the navigation header. .PP .nf void papplPrinterRemoveLink ( pappl_printer_t *printer, const char *label ); .fi .PP This function removes the named link for the printer. .SS papplSystemAddLink Add a link to the navigation header. .PP .nf void papplSystemAddLink ( pappl_system_t *system, const char *label, const char *path_or_url, pappl_loptions_t options ); .fi .PP This function adds a navigation link for the system. The "path_or_url" argument specifies a absolute path such as "/page" or an absolute URL such as "https://www.example.com/". The "options" argument specifies where the link is shown and whether the link should redirect an absolute path to the secure ("https://.../path") web interface. .SS papplSystemAddResourceCallback Add a dynamic resource that uses a callback function. .PP .nf void papplSystemAddResourceCallback ( pappl_system_t *system, const char *path, const char *format, pappl_resource_cb_t cb, void *data ); .fi .PP This function adds a dynamic resource at the specified path. When a client GET or POST request is received at the specified path, the "cb" function will be called with the client pointer and "data" pointer to respond to it. .PP Resource callbacks are most often used to implement custom web pages. .PP .IP 5 Note: Any custom web page that is added prior to calling the .IP 5 \fIpapplSystemRun\fR function will replace the corresponding standard web .IP 5 page at the same path. .SS papplSystemAddResourceData Add a static data resource. .PP .nf void papplSystemAddResourceData ( pappl_system_t *system, const char *path, const char *format, const void *data, size_t datalen ); .fi .PP This function adds a static resource at the specified path. The provided data is not copied to the resource and must remain stable for as long as the resource is added to the system. .PP .IP 5 Note: Any resource that is added prior to calling the \fIpapplSystemRun\fR .IP 5 function will replace the corresponding standard resource at the same path. .SS papplSystemAddResourceDirectory Add external files in a directory as resources. .PP .nf void papplSystemAddResourceDirectory ( pappl_system_t *system, const char *basepath, const char *directory ); .fi .PP This function adds static resources from the specified directory under the specified path. The directory is scanned and only those files present at the time of the call are available, and those files must remain stable for as long as the resources are added to the system.. .PP .IP 5 Note: Any resource that is added prior to calling the \fIpapplSystemRun\fR .IP 5 function will replace the corresponding standard resource at the same path. .SS papplSystemAddResourceFile Add an external file as a resource. .PP .nf void papplSystemAddResourceFile ( pappl_system_t *system, const char *path, const char *format, const char *filename ); .fi .PP This function adds a static resource at the specified path. The provided file is not copied to the resource and must remain stable for as long as the resource is added to the system. .PP .IP 5 Note: Any resource that is added prior to calling the \fIpapplSystemRun\fR .IP 5 function will replace the corresponding standard resource at the same path. .SS papplSystemAddResourceString Add a static data resource as a C string. .PP .nf void papplSystemAddResourceString ( pappl_system_t *system, const char *path, const char *format, const char *data ); .fi .PP This function adds a static resource at the specified path. The provided data is not copied to the resource and must remain stable for as long as the resource is added to the system. .PP .IP 5 Note: Any resource that is added prior to calling the \fIpapplSystemRun\fR .IP 5 function will replace the corresponding standard resource at the same path. .SS papplSystemAddStringsData Add a static localization file resource. .PP .nf void papplSystemAddStringsData ( pappl_system_t *system, const char *path, const char *language, const char *data ); .fi .PP This function adds a static localization resource at the specified path. Localization files use the NeXTStep strings ("text/strings") format defined in PWG Candidate Standard 5100.13-2013. The provided data is not copied to the resource and must remain stable for as long as the resource is added to the system. .PP .IP 5 Note: Any resource that is added prior to calling the \fIpapplSystemRun\fR .IP 5 function will replace the corresponding standard resource at the same path. .SS papplSystemAddStringsFile Add an external localization file resource. .PP .nf void papplSystemAddStringsFile ( pappl_system_t *system, const char *path, const char *language, const char *filename ); .fi .PP This function adds a static localization resource at the specified path. Localization files use the NeXTStep strings ("text/strings") format defined in PWG Candidate Standard 5100.13-2013. The provided file is not copied to the resource and must remain stable for as long as the resource is added to the system. .PP .IP 5 Note: Any resource that is added prior to calling the \fIpapplSystemRun\fR .IP 5 function will replace the corresponding standard resource at the same path. .SS papplSystemRemoveLink Remove a link from the navigation header. .PP .nf void papplSystemRemoveLink ( pappl_system_t *system, const char *label ); .fi .PP This function removes the named link for the system. .SS papplSystemRemoveResource Remove a resource at the specified path. .PP .nf void papplSystemRemoveResource ( pappl_system_t *system, const char *path ); .fi .PP This function removes a resource at the specified path. .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl-system-body.man000066400000000000000000000152671403603036100174770ustar00rootroot00000000000000.SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_system_s .B pappl_system_t; .PP .I bool .br .BI papplSystemAddListeners "(pappl_system_t *system, const char *name);" .PP .I void .br .BI papplSystemAddMIMEFilter "(pappl_system_t *system, const char *srctype, const char *dsttype, pappl_mime_filter_cb_t cb, void *data);" .PP .I void .br .BI papplSystemCleanJobs "(pappl_system_t *system);" .PP .I pappl_system_t * .br .BI papplSystemCreate "(pappl_soptions_t options, const char *name, int port, const char *subtypes, const char *spooldir, const char *logfile, pappl_loglevel_t loglevel, const char *auth_service, bool tls_only);" .PP .I void .br .BI papplSystemDelete "(pappl_system_t *system);" .PP .I pappl_printer_t * .br .BI papplSystemFindPrinter "(pappl_system_t *system, const char *resource, int printer_id, const char *device_uri);" .PP .I char * .br .BI papplSystemGetAdminGroup "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I const char * .br .BI papplSystemGetAuthService "(pappl_system_t *system);" .PP .I pappl_contact_t * .br .BI papplSystemGetContact "(pappl_system_t *system, pappl_contact_t *contact);" .PP .I int .br .BI papplSystemGetDefaultPrinterID "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetDefaultPrintGroup "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetDNSSDName "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I const char * .br .BI papplSystemGetFooterHTML "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetGeoLocation "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetHostname "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetLocation "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I pappl_loglevel_t .br .BI papplSystemGetLogLevel "(pappl_system_t *system);" .PP .I size_t .br .BI papplSystemGetMaxLogSize "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetName "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I int .br .BI papplSystemGetNextPrinterID "(pappl_system_t *system);" .PP .I pappl_soptions_t .br .BI papplSystemGetOptions "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetOrganization "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetOrganizationalUnit "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetPassword "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I int .br .BI papplSystemGetPort "(pappl_system_t *system);" .PP .I const char * .br .BI papplSystemGetServerHeader "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetSessionKey "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I bool .br .BI papplSystemGetTLSOnly "(pappl_system_t *system);" .PP .I const char * .br .BI papplSystemGetUUID "(pappl_system_t *system);" .PP .I int .br .BI papplSystemGetVersions "(pappl_system_t *system, int max_versions, pappl_version_t *versions);" .PP .I char * .br .BI papplSystemHashPassword "(pappl_system_t *system, const char *salt, const char *password, char *buffer, size_t bufsize);" .PP .I bool .br .BI papplSystemIsRunning "(pappl_system_t *system);" .PP .I bool .br .BI papplSystemIsShutdown "(pappl_system_t *system);" .PP .I void .br .BI papplSystemIteratePrinters "(pappl_system_t *system, pappl_printer_cb_t cb, void *data);" .PP .I bool .br .BI papplSystemLoadState "(pappl_system_t *system, const char *filename);" .PP .I const char * .br .BI papplSystemMatchDriver "(pappl_system_t *system, const char *device_id);" .PP .I void .br .BI papplSystemRemoveResource "(pappl_system_t *system, const char *path);" .PP .I void .br .BI papplSystemRun "(pappl_system_t *system);" .PP .I bool .br .BI papplSystemSaveState "(pappl_system_t *system, const char *filename);" .PP .I void .br .BI papplSystemSetAdminGroup "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetContact "(pappl_system_t *system, pappl_contact_t *contact);" .PP .I void .br .BI papplSystemSetDefaultPrinterID "(pappl_system_t *system, int default_printer_id);" .PP .I void .br .BI papplSystemSetDefaultPrintGroup "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetDrivers "(pappl_system_t *system, int num_drivers, pappl_driver_t *drivers, pappl_driver_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetDNSSDName "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetFooterHTML "(pappl_system_t *system, const char *html);" .PP .I void .br .BI papplSystemSetGeoLocation "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetHostname "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetLocation "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetLogLevel "(pappl_system_t *system, pappl_loglevel_t loglevel);" .PP .I void .br .BI papplSystemSetMaxLogSize "(pappl_system_t *system, size_t maxSize);" .PP .I void .br .BI papplSystemSetMIMECallback "(pappl_system_t *system, pappl_mime_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetNextPrinterID "(pappl_system_t *system, int next_printer_id);" .PP .I void .br .BI papplSystemSetOperationCallback "(pappl_system_t *system, pappl_ipp_op_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetOrganization "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetOrganizationalUnit "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetPassword "(pappl_system_t *system, const char *hash);" .PP .I void .br .BI papplSystemSetSaveCallback "(pappl_system_t *system, pappl_save_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetUUID "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetVersions "(pappl_system_t *system, int num_versions, pappl_version_t *versions);" .PP .I void .br .BI papplSystemShutdown "(pappl_system_t *system);" .SH DESCRIPTION The .B PAPPL system functions provide access to the system object. System are created and deleted by the printer application while the life cycle of the .B pappl_system_t object is managed automatically for the printer application. The .B papplSystemCreate function creates a new system while the .B papplSystemDelete function deletes a system. .PP The .B papplSystemRun function starts a system while the .B papplSystemShutdown function stops a running system. .PP The .B papplSystemGet functions get the current values associated with a system while the .B papplSystemSet functions set the current values associated with a system. pappl-1.0.3/man/pappl-system.3000066400000000000000000001026201403603036100161210ustar00rootroot00000000000000.TH pappl-system 3 "pappl system functions" "2021-02-15" "pappl system functions" .SH NAME pappl-system \- pappl system functions .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .PP .I typedef struct _pappl_system_s .B pappl_system_t; .PP .I bool .br .BI papplSystemAddListeners "(pappl_system_t *system, const char *name);" .PP .I void .br .BI papplSystemAddMIMEFilter "(pappl_system_t *system, const char *srctype, const char *dsttype, pappl_mime_filter_cb_t cb, void *data);" .PP .I void .br .BI papplSystemCleanJobs "(pappl_system_t *system);" .PP .I pappl_system_t * .br .BI papplSystemCreate "(pappl_soptions_t options, const char *name, int port, const char *subtypes, const char *spooldir, const char *logfile, pappl_loglevel_t loglevel, const char *auth_service, bool tls_only);" .PP .I void .br .BI papplSystemDelete "(pappl_system_t *system);" .PP .I pappl_printer_t * .br .BI papplSystemFindPrinter "(pappl_system_t *system, const char *resource, int printer_id, const char *device_uri);" .PP .I char * .br .BI papplSystemGetAdminGroup "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I const char * .br .BI papplSystemGetAuthService "(pappl_system_t *system);" .PP .I pappl_contact_t * .br .BI papplSystemGetContact "(pappl_system_t *system, pappl_contact_t *contact);" .PP .I int .br .BI papplSystemGetDefaultPrinterID "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetDefaultPrintGroup "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetDNSSDName "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I const char * .br .BI papplSystemGetFooterHTML "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetGeoLocation "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetHostname "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetLocation "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I pappl_loglevel_t .br .BI papplSystemGetLogLevel "(pappl_system_t *system);" .PP .I size_t .br .BI papplSystemGetMaxLogSize "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetName "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I int .br .BI papplSystemGetNextPrinterID "(pappl_system_t *system);" .PP .I pappl_soptions_t .br .BI papplSystemGetOptions "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetOrganization "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetOrganizationalUnit "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I char * .br .BI papplSystemGetPassword "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I int .br .BI papplSystemGetPort "(pappl_system_t *system);" .PP .I const char * .br .BI papplSystemGetServerHeader "(pappl_system_t *system);" .PP .I char * .br .BI papplSystemGetSessionKey "(pappl_system_t *system, char *buffer, size_t bufsize);" .PP .I bool .br .BI papplSystemGetTLSOnly "(pappl_system_t *system);" .PP .I const char * .br .BI papplSystemGetUUID "(pappl_system_t *system);" .PP .I int .br .BI papplSystemGetVersions "(pappl_system_t *system, int max_versions, pappl_version_t *versions);" .PP .I char * .br .BI papplSystemHashPassword "(pappl_system_t *system, const char *salt, const char *password, char *buffer, size_t bufsize);" .PP .I bool .br .BI papplSystemIsRunning "(pappl_system_t *system);" .PP .I bool .br .BI papplSystemIsShutdown "(pappl_system_t *system);" .PP .I void .br .BI papplSystemIteratePrinters "(pappl_system_t *system, pappl_printer_cb_t cb, void *data);" .PP .I bool .br .BI papplSystemLoadState "(pappl_system_t *system, const char *filename);" .PP .I const char * .br .BI papplSystemMatchDriver "(pappl_system_t *system, const char *device_id);" .PP .I void .br .BI papplSystemRemoveResource "(pappl_system_t *system, const char *path);" .PP .I void .br .BI papplSystemRun "(pappl_system_t *system);" .PP .I bool .br .BI papplSystemSaveState "(pappl_system_t *system, const char *filename);" .PP .I void .br .BI papplSystemSetAdminGroup "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetContact "(pappl_system_t *system, pappl_contact_t *contact);" .PP .I void .br .BI papplSystemSetDefaultPrinterID "(pappl_system_t *system, int default_printer_id);" .PP .I void .br .BI papplSystemSetDefaultPrintGroup "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetDrivers "(pappl_system_t *system, int num_drivers, pappl_driver_t *drivers, pappl_driver_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetDNSSDName "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetFooterHTML "(pappl_system_t *system, const char *html);" .PP .I void .br .BI papplSystemSetGeoLocation "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetHostname "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetLocation "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetLogLevel "(pappl_system_t *system, pappl_loglevel_t loglevel);" .PP .I void .br .BI papplSystemSetMaxLogSize "(pappl_system_t *system, size_t maxSize);" .PP .I void .br .BI papplSystemSetMIMECallback "(pappl_system_t *system, pappl_mime_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetNextPrinterID "(pappl_system_t *system, int next_printer_id);" .PP .I void .br .BI papplSystemSetOperationCallback "(pappl_system_t *system, pappl_ipp_op_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetOrganization "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetOrganizationalUnit "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetPassword "(pappl_system_t *system, const char *hash);" .PP .I void .br .BI papplSystemSetSaveCallback "(pappl_system_t *system, pappl_save_cb_t cb, void *data);" .PP .I void .br .BI papplSystemSetUUID "(pappl_system_t *system, const char *value);" .PP .I void .br .BI papplSystemSetVersions "(pappl_system_t *system, int num_versions, pappl_version_t *versions);" .PP .I void .br .BI papplSystemShutdown "(pappl_system_t *system);" .SH DESCRIPTION The .B PAPPL system functions provide access to the system object. System are created and deleted by the printer application while the life cycle of the .B pappl_system_t object is managed automatically for the printer application. The .B papplSystemCreate function creates a new system while the .B papplSystemDelete function deletes a system. .PP The .B papplSystemRun function starts a system while the .B papplSystemShutdown function stops a running system. .PP The .B papplSystemGet functions get the current values associated with a system while the .B papplSystemSet functions set the current values associated with a system. .SH ENUMERATIONS .SS pappl_soptions_e System option bits .TP 5 PAPPL_SOPTIONS_DNSSD_HOST .br Use hostname in DNS-SD service names instead of serial number/UUID .TP 5 PAPPL_SOPTIONS_MULTI_QUEUE .br Support multiple printers .TP 5 PAPPL_SOPTIONS_NONE .br No options .TP 5 PAPPL_SOPTIONS_RAW_SOCKET .br Accept jobs via raw sockets .TP 5 PAPPL_SOPTIONS_USB_PRINTER .br Accept jobs via USB for default printer (embedded Linux only) .TP 5 PAPPL_SOPTIONS_WEB_INTERFACE .br Enable the standard web pages .TP 5 PAPPL_SOPTIONS_WEB_LOG .br Enable the log file page .TP 5 PAPPL_SOPTIONS_WEB_NETWORK .br Enable the network settings page .TP 5 PAPPL_SOPTIONS_WEB_REMOTE .br Allow remote queue management (vs. localhost only) .TP 5 PAPPL_SOPTIONS_WEB_SECURITY .br Enable the user/password settings page .TP 5 PAPPL_SOPTIONS_WEB_TLS .br Enable the TLS settings page .SH FUNCTIONS .SS papplSystemAddListeners Add network or domain socket listeners. .PP .nf bool papplSystemAddListeners ( pappl_system_t *system, const char *name ); .fi .PP This function adds socket listeners. The "name" parameter specifies the listener address. Names starting with a slash (/) specify a UNIX domain socket path, otherwise the name is treated as a fully-qualified domain name or numeric IPv4 or IPv6 address. If name is \fBNULL\fR, the "any" addresses are used ("0.0.0.0" and "[::]"). .PP Listeners cannot be added after \fIpapplSystemRun\fR is called. .SS papplSystemAddMIMEFilter Add a file filter to the system. .PP .nf void papplSystemAddMIMEFilter ( pappl_system_t *system, const char *srctype, const char *dsttype, pappl_mime_filter_cb_t cb, void *data ); .fi .PP This function adds a file filter to the system to be used for processing different kinds of document data in print jobs. The "srctype" and "dsttype" arguments specify the source and destination MIME media types as constant strings. A destination MIME media type of "image/pwg-raster" specifies a filter that uses the driver's raster interface. Other destination types imply direct submission to the output device using the \fBpapplDeviceXxx\fR functions. .PP .IP 5 Note: This function may not be called while the system is running. .SS papplSystemCreate Create a system object. .PP .nf pappl_system_t * papplSystemCreate ( pappl_soptions_t options, const char *name, int port, const char *subtypes, const char *spooldir, const char *logfile, pappl_loglevel_t loglevel, const char *auth_service, bool tls_only ); .fi .PP This function creates a new system object, which is responsible for managing all the printers, jobs, and resources used by the printer application. .PP The "options" argument specifies which options are enabled for the server: .PP .IP \(bu 5 \fBPAPPL_SOPTIONS_NONE\fR: No options. .IP \(bu 5 \fBPAPPL_SOPTIONS_DNSSD_HOST\fR: When resolving DNS-SD service name collisions, use the DNS-SD hostname instead of a serial number or UUID. .IP \(bu 5 \fBPAPPL_SOPTIONS_WEB_LOG\fR: Include the log file web page. .IP \(bu 5 \fBPAPPL_SOPTIONS_MULTI_QUEUE\fR: Support multiple printers. .IP \(bu 5 \fBPAPPL_SOPTIONS_WEB_NETWORK\fR: Include the network settings web page. .IP \(bu 5 \fBPAPPL_SOPTIONS_RAW_SOCKET\fR: Accept jobs via raw sockets starting on port 9100. .IP \(bu 5 \fBPAPPL_SOPTIONS_WEB_REMOTE\fR: Allow remote queue management. .IP \(bu 5 \fBPAPPL_SOPTIONS_WEB_SECURITY\fR: Include the security settings web page. .IP \(bu 5 \fBPAPPL_SOPTIONS_WEB_INTERFACE\fR: Include the standard printer and job monitoring web pages. .IP \(bu 5 \fBPAPPL_SOPTIONS_WEB_TLS\fR: Include the TLS settings page. .IP \(bu 5 \fBPAPPL_SOPTIONS_USB_PRINTER\fR: Accept jobs via USB for the default printer (embedded Linux only). .PP The "name" argument specifies a human-readable name for the system. .PP The "port" argument specifies the port number to bind to. A value of \fB0\fR will cause an available port number to be assigned when the first listener is added with the \fIpapplSystemAddListeners\fR function. .PP The "subtypes" argument specifies one or more comma-delimited DNS-SD service sub-types such as "_print" and "_universal". .PP The "spooldir" argument specifies the location of job files. If \fBNULL\fR, a temporary directory is created. .PP The "logfile" argument specifies where to send log messages. If \fBNULL\fR, the log messages are written to a temporary file. .PP The "loglevel" argument specifies the initial logging level. .PP The "auth_service" argument specifies a PAM authentication service name. If \fBNULL\fR, no user authentication will be provided. .PP The "tls_only" argument controls whether the printer application will accept unencrypted connections. In general, this argument should always be \fBfalse\fR (allow unencrypted connections) since not all clients support encrypted printing. .SS papplSystemDelete Delete a system object. .PP .nf void papplSystemDelete ( pappl_system_t *system ); .fi .PP .IP 5 Note: A system object cannot be deleted while the system is running. .SS papplSystemFindPrinter Find a printer by resource, ID, or device URI. .PP .nf pappl_printer_t * papplSystemFindPrinter ( pappl_system_t *system, const char *resource, int printer_id, const char *device_uri ); .fi .PP This function finds a printer contained in the system using its resource path, unique integer identifier, or device URI. If none of these is specified, the current default printer is returned. .SS papplSystemGetAdminGroup Get the current administrative group, if any. .PP .nf char * papplSystemGetAdminGroup ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current administrative group, if any, to the specified buffer. .SS papplSystemGetAuthService Get the PAM authorization service, if any. .PP .nf const char * papplSystemGetAuthService ( pappl_system_t *system ); .fi .PP This function returns the PAM authorization service being used by the system for authentication, if any. .SS papplSystemGetContact Get the "system-contact" value. .PP .nf pappl_contact_t * papplSystemGetContact ( pappl_system_t *system, pappl_contact_t *contact ); .fi .PP This function copies the current system contact information to the specified buffer. .SS papplSystemGetDNSSDName Get the current DNS-SD service name. .PP .nf char * papplSystemGetDNSSDName ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current DNS-SD service name of the system, if any, to the specified buffer. .SS papplSystemGetDefaultPrintGroup Get the default print group, if any. .PP .nf char * papplSystemGetDefaultPrintGroup ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current default print group, if any, to the specified buffer. .SS papplSystemGetDefaultPrinterID Get the current "default-printer-id" value. .PP .nf int papplSystemGetDefaultPrinterID ( pappl_system_t *system ); .fi .PP This function returns the positive integer identifier for the current default printer or \fB0\fR if there is no default printer. .SS papplSystemGetFooterHTML Get the footer HTML for the web interface, if any. .PP .nf const char * papplSystemGetFooterHTML ( pappl_system_t *system ); .fi .PP This function returns the HTML for the web page footer, if any. The footer HTML can be set using the \fIpapplSystemSetFooterHTML\fR function. .SS papplSystemGetGeoLocation Get the system geo-location string, if any. .PP .nf char * papplSystemGetGeoLocation ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current system geographic location as a "geo:" URI to the specified buffer. .SS papplSystemGetHostname Get the system hostname. .PP .nf char * papplSystemGetHostname ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current system hostname to the specified buffer. .SS papplSystemGetLocation Get the system location string, if any. .PP .nf char * papplSystemGetLocation ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current human-readable location, if any, to the specified buffer. .SS papplSystemGetLogLevel .PP .nf pappl_loglevel_t papplSystemGetLogLevel ( pappl_system_t *system ); .fi .SS papplSystemGetMaxLogSize Get the maximum log file size. .PP .nf size_t papplSystemGetMaxLogSize ( pappl_system_t *system ); .fi .PP This function gets the maximum log file size, which is only used when logging directly to a file. When the limit is reached, the current log file is renamed to "filename.O" and a new log file is created. Set the maximum size to \fB0\fR to disable log file rotation. .PP The default maximum log file size is 1MiB or \fB1048576\fR bytes. .SS papplSystemGetName Get the system name. .PP .nf char * papplSystemGetName ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current system name to the specified buffer. .SS papplSystemGetNextPrinterID Get the next "printer-id" value. .PP .nf int papplSystemGetNextPrinterID ( pappl_system_t *system ); .fi .PP This function returns the positive integer identifier that will be used for the next printer that is created. .SS papplSystemGetOptions Get the system options. .PP .nf pappl_soptions_t papplSystemGetOptions ( pappl_system_t *system ); .fi .PP This function returns the system options as a bitfield. .SS papplSystemGetOrganization Get the system organization string, if any. .PP .nf char * papplSystemGetOrganization ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current organization name, if any, to the specified buffer. .SS papplSystemGetOrganizationalUnit Get the system organizational unit string, if any. .PP .nf char * papplSystemGetOrganizationalUnit ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current organizational unit name, if any, to the specified buffer. .SS papplSystemGetPassword Get the current web site access password. .PP .nf char * papplSystemGetPassword ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current web site password hash, if any, to the specified buffer. .PP Note: The access password is only used when the PAM authentication service is not set. .SS papplSystemGetPort Get the port number for network connections to the system. .PP .nf int papplSystemGetPort ( pappl_system_t *system ); .fi .PP This function returns the port number that is used for network connections to the system. .SS papplSystemGetServerHeader Get the Server: header for HTTP responses. .PP .nf const char * papplSystemGetServerHeader ( pappl_system_t *system ); .fi .PP This function returns the value of the HTTP "Server:" header that is used by the system. .SS papplSystemGetSessionKey Get the current session key. .PP .nf char * papplSystemGetSessionKey ( pappl_system_t *system, char *buffer, size_t bufsize ); .fi .PP This function copies the current session key to the specified buffer. The session key is used for web interface forms to provide CSRF protection and is refreshed periodically. .SS papplSystemGetTLSOnly Get the TLS-only state of the system. .PP .nf bool papplSystemGetTLSOnly ( pappl_system_t *system ); .fi .PP This function returns whether the system will only accept encrypted connections. .SS papplSystemGetUUID Get the "system-uuid" value. .PP .nf const char * papplSystemGetUUID ( pappl_system_t *system ); .fi .PP This function returns the system's UUID value. .SS papplSystemGetVersions Get the firmware names and versions. .PP .nf int papplSystemGetVersions ( pappl_system_t *system, int max_versions, pappl_version_t *versions ); .fi .PP This function copies the system firmware information to the specified buffer. The return value is always the number of firmware versions that have been set using the \fIpapplSystemSetVersions\fR function, regardless of the value of the "max_versions" argument. .SS papplSystemHashPassword Generate a password hash using salt and password strings. .PP .nf char * papplSystemHashPassword ( pappl_system_t *system, const char *salt, const char *password, char *buffer, size_t bufsize ); .fi .PP This function generates a password hash using the "salt" and "password" strings. The "salt" string should be \fBNULL\fR to generate a new password hash or the value of an existing password hash to verify that a given plaintext "password" string matches the password hash. .PP .IP 5 Note: Hashed access passwords are only used when the PAM authentication .IP 5 service is not set. .SS papplSystemIsRunning Return whether the system is running. .PP .nf bool papplSystemIsRunning ( pappl_system_t *system ); .fi .PP This function returns whether the system is running. .SS papplSystemIsShutdown Return whether the system has been shutdown. .PP .nf bool papplSystemIsShutdown ( pappl_system_t *system ); .fi .PP This function returns whether the system is shutdown or scheduled to shutdown. .SS papplSystemIteratePrinters Iterate all of the printers. .PP .nf void papplSystemIteratePrinters ( pappl_system_t *system, pappl_printer_cb_t cb, void *data ); .fi .PP This function iterates each of the printers managed by the system. The "cb" function is called once per printer with the "system" and "data" values. .SS papplSystemLoadState Load the previous system state. .PP .nf bool papplSystemLoadState ( pappl_system_t *system, const char *filename ); .fi .PP This function loads the previous system state from a file created by the \fIpapplSystemSaveState\fR function. The system state contains all of the system object values, the list of printers, and the jobs for each printer. .PP When loading a printer definition, if the printer cannot be created (e.g., because the driver name is no longer valid) then that printer and all of its job history will be lost. In the case of a bad driver name, a printer application's driver callback can perform any necessary mapping of the driver name, including the use its auto-add callback to find a compatible new driver. .PP .IP 5 Note: This function must be called prior to \fIpapplSystemRun\fR. .SS papplSystemMatchDriver .PP .nf const char * papplSystemMatchDriver ( pappl_system_t *system, const char *device_id ); .fi .SS papplSystemRun Run the printer application. .PP .nf void papplSystemRun ( pappl_system_t *system ); .fi .PP This function runs the printer application, accepting new connections, handling requests, and processing jobs as needed. It returns once the system is shutdown, either through an IPP request or \fBSIGTERM\fR. .SS papplSystemSaveState Save the current system state. .PP .nf bool papplSystemSaveState ( pappl_system_t *system, const char *filename ); .fi .PP This function saves the current system state to a file. It is typically used with the \fIpapplSystemSetSaveCallback\fR function to periodically save the state: .PP .nf papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState, (void *)filename); ``` .fi inter cannot be created (e.g., because the driver name is no longer valid) then that printer and all of its job history will be lost. In the case of a bad driver name, a printer application's driver callback can perform any necessary mapping of the driver name, including the use its auto-add callback to find a compatible new driver. .PP .IP 5 Note: This function must be called prior to \fIpapplSystemRun\fR .SS papplSystemSetAdminGroup Set the administrative group. .PP .nf void papplSystemSetAdminGroup ( pappl_system_t *system, const char *value ); .fi .PP This function sets the group name used for administrative requests such as adding or deleting a printer. .PP .IP 5 Note: The administrative group is only used when the PAM authorization .IP 5 service is also set when the system is created. .SS papplSystemSetContact Set the "system-contact" value. .PP .nf void papplSystemSetContact ( pappl_system_t *system, pappl_contact_t *contact ); .fi .PP This function sets the system contact value. .SS papplSystemSetDNSSDName Set the DNS-SD service name. .PP .nf void papplSystemSetDNSSDName ( pappl_system_t *system, const char *value ); .fi .PP This function sets the DNS-SD service name of the system. If \fBNULL\fR, the DNS-SD registration is removed. .SS papplSystemSetDefaultPrintGroup Set the default print group. .PP .nf void papplSystemSetDefaultPrintGroup ( pappl_system_t *system, const char *value ); .fi .PP This function sets the default group name used for print requests. .PP .IP 5 Note: The default print group is only used when the PAM authorization .IP 5 service is also set when the system is created. .SS papplSystemSetDefaultPrinterID Set the "default-printer-id" value. .PP .nf void papplSystemSetDefaultPrinterID ( pappl_system_t *system, int default_printer_id ); .fi .PP This function sets the default printer using its unique positive integer identifier. .SS papplSystemSetFooterHTML Set the footer HTML for the web interface. .PP .nf void papplSystemSetFooterHTML ( pappl_system_t *system, const char *html ); .fi .PP This function sets the footer HTML for the web interface. .PP .IP 5 Note: The footer HTML can only be set prior to calling .IP 5 \fIpapplSystemRun\fR. .SS papplSystemSetGeoLocation Set the geographic location string. .PP .nf void papplSystemSetGeoLocation ( pappl_system_t *system, const char *value ); .fi .PP This function sets the geographic location of the system as a "geo:" URI. If \fBNULL\fR, the location is cleared. .SS papplSystemSetHostname Set the system hostname. .PP .nf void papplSystemSetHostname ( pappl_system_t *system, const char *value ); .fi .PP This function sets the system hostname. If \fBNULL\fR, the default hostname is used. .SS papplSystemSetLocation Set the system location string, if any. .PP .nf void papplSystemSetLocation ( pappl_system_t *system, const char *value ); .fi .PP This function sets the human-readable location of the system. If \fBNULL\fR, the location is cleared. .SS papplSystemSetLogLevel Set the system log level .PP .nf void papplSystemSetLogLevel ( pappl_system_t *system, pappl_loglevel_t loglevel ); .fi .PP This function sets the log level as an enumeration. .SS papplSystemSetMIMECallback Set the MIME typing callback for the system. .PP .nf void papplSystemSetMIMECallback ( pappl_system_t *system, pappl_mime_cb_t cb, void *data ); .fi .PP This function sets a custom MIME typing callback for the system. The MIME typing callback extends the built-in MIME typing support for other media types that are supported by the application, typically vendor print formats. .PP The callback function receives a buffer containing the initial bytes of the document data, the length of the buffer, and the callback data. It can then return \fBNULL\fR if the content is not recognized or a constant string containing the MIME media type, for example "application/vnd.hp-pcl" for HP PCL print data. .SS papplSystemSetMaxLogSize Set the maximum log file size in bytes. .PP .nf void papplSystemSetMaxLogSize ( pappl_system_t *system, size_t maxsize ); .fi .PP This function sets the maximum log file size in bytes, which is only used when logging directly to a file. When the limit is reached, the current log file is renamed to "filename.O" and a new log file is created. Set the maximum size to \fB0\fR to disable log file rotation. .PP The default maximum log file size is 1MiB or \fB1048576\fR bytes. .SS papplSystemSetNextPrinterID Set the next "printer-id" value. .PP .nf void papplSystemSetNextPrinterID ( pappl_system_t *system, int next_printer_id ); .fi .PP This function sets the unique positive integer identifier that will be used for the next printer that is created. It is typically only called as part of restoring the state of a system. .PP .IP 5 Note: The next printer ID can only be set prior to calling .IP 5 \fIpapplSystemRun\fR. .SS papplSystemSetOperationCallback Set the IPP operation callback. .PP .nf void papplSystemSetOperationCallback ( pappl_system_t *system, pappl_ipp_op_cb_t cb, void *data ); .fi .PP This function sets a custom IPP operation handler for the system that is called for any IPP operations that are not handled by the built-in IPP services. .PP .IP 5 Note: The operation callback can only be set prior to calling .IP 5 \fIpapplSystemRun\fR. .SS papplSystemSetOrganization Set the system organization string, if any. .PP .nf void papplSystemSetOrganization ( pappl_system_t *system, const char *value ); .fi .PP This function sets the organization name for the system. If \fBNULL\fR, the name is cleared. .SS papplSystemSetOrganizationalUnit Set the system organizational unit string, if any. .PP .nf void papplSystemSetOrganizationalUnit ( pappl_system_t *system, const char *value ); .fi .PP This function sets the organizational unit name for the system. If \fBNULL\fR, the name is cleared. .SS papplSystemSetPassword Set the access password hash string. .PP .nf void papplSystemSetPassword ( pappl_system_t *system, const char *hash ); .fi .PP This function sets the hash for the web access password. The hash string is generated using the \fIpapplSystemHashPassword\fR function. .PP .IP 5 Note: The access password is only used when the PAM authentication service .IP 5 is not set. .SS papplSystemSetPrinterDrivers Set the list of drivers and the driver callbacks. .PP .nf void papplSystemSetPrinterDrivers ( pappl_system_t *system, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_create_cb_t create_cb, pappl_pr_driver_cb_t driver_cb, void *data ); .fi .PP This function sets the lists of printer drivers, the optional auto-add callback function, the optional creation callback, and the required driver initialization callback function. .PP The auto-add callback ("autoadd_cb") finds a compatible driver name for the specified printer. It is used when the client or user specifies the "auto" driver name, and for the "autoadd" sub-command for the \fBpapplMainloop\fR API. .PP The creation callback ("create_cb") is called at the end of printer creation to make any common changes or additions to a new printer. It is typically used to add extra web pages, add per-printer static resources, and/or initialize the contact and location information. .PP The driver initialization callback ("driver_cb") is called to initialize the \fBpappl_pr_driver_data_t\fR structure, which provides all of the printer capabilities and callbacks for printing. .SS papplSystemSetSaveCallback Set the save callback. .PP .nf void papplSystemSetSaveCallback ( pappl_system_t *system, pappl_save_cb_t cb, void *data ); .fi .PP This function sets a callback that is used to periodically save the current system state. Typically the callback function ("cb") is \fIpapplSystemSaveState\fR and the callback data ("data") is the name of the state file: .PP .nf papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState, (void *)filename); .fi .IP 5 Note: The save callback can only be set prior to calling .IP 5 \fIpapplSystemRun\fR. .SS papplSystemSetUUID Set the system UUID. .PP .nf void papplSystemSetUUID ( pappl_system_t *system, const char *value ); .fi .PP This function sets the system UUID value, overriding the default (generated) value. It is typically used when restoring the state of a previous incarnation of the system. .PP .IP 5 Note: The UUID can only be set prior to calling \fIpapplSystemRun\fR. .SS papplSystemSetVersions Set the firmware names and versions. .PP .nf void papplSystemSetVersions ( pappl_system_t *system, int num_versions, pappl_version_t *versions ); .fi .PP This function sets the names and versions of each firmware/software component of the printer application. .SS papplSystemShutdown Shutdown the system. .PP .nf void papplSystemShutdown ( pappl_system_t *system ); .fi .PP This function tells the system to perform an orderly shutdown of all printers and to terminate the main loop. .SH STRUCTURES .SS pappl_pr_driver_s Printer driver information .PP .nf struct pappl_pr_driver_s { const char *description; const char *device_id; void *extension; const char *name; }; .fi .SS pappl_version_s Firmware version information .PP .nf struct pappl_version_s { char name[64]; char patches[64]; char sversion[64]; unsigned short version[4]; }; .fi .SH TYPES .SS pappl_ipp_op_cb_t IPP operation callback function .PP .nf typedef bool (*pappl_ipp_op_cb_t)(pappl_client_t *client, void *data); .fi .SS pappl_mime_cb_t MIME typing callback function .PP .nf typedef const char * (*pappl_mime_cb_t)(const unsigned char *header, size_t headersize, void *data); .fi .SS pappl_mime_filter_cb_t Filter callback function .PP .nf typedef bool (*pappl_mime_filter_cb_t)(pappl_job_t *job, pappl_device_t *device, void *data); .fi .SS pappl_pr_autoadd_cb_t Auto-add callback .PP .nf typedef const char * (*pappl_pr_autoadd_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data); .fi .SS pappl_pr_create_cb_t Printer creation callback .PP .nf typedef void (*pappl_pr_create_cb_t)(pappl_printer_t *printer, void *data); .fi .SS pappl_pr_driver_cb_t Driver callback function .PP .nf typedef bool (*pappl_pr_driver_cb_t)(pappl_system_t *system, const char *driver_name, const char *device_uri, const char *device_id, pappl_pr_driver_data_t *driver_data, ipp_t **driver_attrs, void *data); .fi .SS pappl_pr_driver_t Printer driver information .PP .nf typedef struct pappl_pr_driver_s pappl_pr_driver_t; .fi .SS pappl_printer_cb_t Printer iterator callback function .PP .nf typedef void (*pappl_printer_cb_t)(pappl_printer_t *printer, void *data); .fi .SS pappl_resource_cb_t Dynamic resource callback function .PP .nf typedef bool (*pappl_resource_cb_t)(pappl_client_t *client, void *data); .fi .SS pappl_save_cb_t Save callback function .PP .nf typedef bool (*pappl_save_cb_t)(pappl_system_t *system, void *data); .fi .SS pappl_soptions_t Bitfield for system options .PP .nf typedef unsigned pappl_soptions_t; .fi .SS pappl_version_t Firmware version information .PP .nf typedef struct pappl_version_s pappl_version_t; .fi .SH SEE ALSO .BR pappl (1), .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/man/pappl.1000066400000000000000000000044361403603036100146030ustar00rootroot00000000000000.\" .\" pappl man page .\" .\" Copyright © 2019-2020 by Michael R Sweet .\" .\" Licensed under Apache License v2.0. See the file "LICENSE" for more .\" information. .\" .TH pappl 1 "pappl" "2020-10-25" "Michael R Sweet" .SH NAME pappl \- printer application framework .SH LIBRARY Printer Application Framework (libpappl, "pkg-config --cflags --libs pappl") .SH SYNOPSIS .B #include .SH DESCRIPTION .B PAPPL is a simple C-based framework/library for developing CUPS Printer Applications, which are the recommended replacement for printer drivers. It is sufficiently general purpose to support any kind of printer or driver that can be used on desktops, servers, and in embedded environments. .PP .B PAPPL embeds a multi-threaded HTTP / IPP Everywhere\[tm] server and provides callbacks for a variety of events that allows a GUI or command-line application to interact with both the local user that is running the Printer Application and any network clients that are submitting print jobs, querying printer status and capabilities, and so forth. .PP .B PAPPL provides a simple driver interface for raster graphics printing, and developers of CUPS Raster drivers will readily adapt to it. .B PAPPL can also be used with printers that support vector graphics printing although you'll have to develop more code to support them. Drivers provide configuration and capability information to .B PAPPL, and .B PAPPL then calls the driver to print things as needed. .B PAPPL automatically supports printing of JPEG, PNG, PWG Raster, Apple Raster, and "raw" files to printers connected via USB and network (AppSocket/JetDirect) connections. Other formats can be supported through "filter" callbacks you register. .SH SEE ALSO .BR pappl-client (3), .BR pappl-device (3), .BR pappl-job (3), .BR pappl-log (3), .BR pappl-mainline (3), .BR pappl-makeresheader (1), .BR pappl-printer (3), .BR pappl-resource (3), .BR pappl-system (3), https://www.msweet.org/pappl .SH COPYRIGHT Copyright \[co] 2019-2020 by Michael R Sweet. .PP .B PAPPL is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used .I freely in any project you'd like. See the files "LICENSE" and "NOTICE" in the source distribution for more information. pappl-1.0.3/pappl.spec000066400000000000000000000047231403603036100146210ustar00rootroot00000000000000# # RPM spec file for the Printer Application Framework # # Copyright © 2020-2021 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # Summary: Printer Application Framework (PAPPL) Name: pappl Version: 1.0.2 Release: 1 License: Apache 2.0 Group: Development/Libraries Source: https://github.com/michaelrsweet/pappl/releases/download/release-%{version}/pappl-%{version}.tar.gz Url: https://www.msweet.org/pappl Packager: John Doe Vendor: Michael R Sweet BuildRequires: avahi-devel, cups-devel, gnutls-devel, libjpeg-turbo-devel, libpng-devel, libusbx-devel, pam-devel, zlib-devel Requires: cups-devel # Use buildroot so as not to disturb the version already installed BuildRoot: /var/tmp/%{name}-root %description PAPPL is a simple C-based framework/library for developing CUPS Printer Applications, which are the recommended replacement for printer drivers. PAPPL supports JPEG, PNG, PWG Raster, Apple Raster, and "raw" printing to printers connected via USB and network (AppSocket/JetDirect) connections. PAPPL provides access to the printer via its embedded IPP Everywhere™ service, either local to the computer or on your whole network, which can then be discovered and used by any application. PAPPL is licensed under the Apache License Version 2.0 with an exception to allow linking against GPL2/LGPL2 software (like older versions of CUPS), so it can be used freely in any project you'd like. %package devel Summary: PAPPL - development environment Requires: %{name} = %{version} %description devel This package provides the PAPPL headers and development environment. %prep %setup %build CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" LDFLAGS="$RPM_OPT_FLAGS" ./configure --enable-shared --prefix=/usr # If we got this far, all prerequisite libraries must be here. make %install # Make sure the RPM_BUILD_ROOT directory exists. rm -rf $RPM_BUILD_ROOT make BUILDROOT=$RPM_BUILD_ROOT install %clean rm -rf $RPM_BUILD_ROOT %post /sbin/ldconfig %postun /sbin/ldconfig %files %defattr(-,root,root) %dir /usr/lib /usr/lib/libpappl.so* %files devel %defattr(-,root,root) %dir /usr/bin /usr/bin/* %dir /usr/include/pappl /usr/include/pappl/*.h %dir /usr/lib /usr/lib/libpappl.a %dir /usr/lib/pkgconfig /usr/lib/pkgconfig/pappl.pc %dir /usr/share/doc/pappl /usr/share/doc/pappl/* %dir /usr/share/man/man1 /usr/share/man/man1/* %dir /usr/share/man/man3 /usr/share/man/man3/* %dir /usr/share/pappl /usr/share/pappl/* pappl-1.0.3/pappl/000077500000000000000000000000001403603036100137375ustar00rootroot00000000000000pappl-1.0.3/pappl/Dependencies000066400000000000000000000176751403603036100162700ustar00rootroot00000000000000client.o: client.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h client-accessors.o: client-accessors.c client-private.h base-private.h \ base.h ../config.h client.h log.h system.h client-auth.o: client-auth.c client-private.h base-private.h base.h \ ../config.h client.h log.h system-private.h dnssd-private.h system.h client-ipp.o: client-ipp.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h client-webif.o: client-webif.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h contact.o: contact.c base-private.h base.h ../config.h device.o: device.c device-private.h base-private.h base.h ../config.h \ device.h printer.h device-file.o: device-file.c device-private.h base-private.h base.h \ ../config.h device.h device-network.o: device-network.c device-private.h base-private.h base.h \ ../config.h device.h dnssd-private.h snmp-private.h printer.h device-usb.o: device-usb.c device-private.h base-private.h base.h \ ../config.h device.h printer.h dnssd.o: dnssd.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h job-accessors.o: job-accessors.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h job-filter.o: job-filter.c pappl.h device.h base.h system.h log.h \ client.h printer.h job.h mainloop.h job-private.h base-private.h \ ../config.h \ \ job-ipp.o: job-ipp.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h job-process.o: job-process.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h job.o: job.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h link.o: link.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h log.o: log.c client-private.h base-private.h base.h ../config.h client.h \ log.h job-private.h job.h log-private.h printer-private.h \ dnssd-private.h printer.h device.h system-private.h system.h lookup.o: lookup.c base-private.h base.h ../config.h mainloop.o: mainloop.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h mainloop-subcommands.o: mainloop-subcommands.c pappl-private.h device.h \ base.h dnssd-private.h base-private.h ../config.h system-private.h \ system.h log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h mainloop-support.o: mainloop-support.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h printer.o: printer.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h printer-accessors.o: printer-accessors.c printer-private.h \ dnssd-private.h base-private.h base.h ../config.h printer.h log.h \ device.h system-private.h system.h printer-driver.o: printer-driver.c printer-private.h dnssd-private.h \ base-private.h base.h ../config.h printer.h log.h device.h \ system-private.h system.h printer-ipp.o: printer-ipp.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h printer-raw.o: printer-raw.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h printer-support.o: printer-support.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h printer-usb.o: printer-usb.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h printer-webif.o: printer-webif.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h resource.o: resource.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h snmp.o: snmp.c snmp-private.h base-private.h base.h ../config.h system.o: system.c pappl-private.h device.h base.h dnssd-private.h \ base-private.h ../config.h system-private.h system.h log.h \ client-private.h client.h printer-private.h printer.h job-private.h \ job.h mainloop-private.h mainloop.h log-private.h resource-private.h \ device-private.h system-accessors.o: system-accessors.c system-private.h dnssd-private.h \ base-private.h base.h ../config.h system.h log.h \ \ \ system-ipp.o: system-ipp.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h system-loadsave.o: system-loadsave.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h system-printer.o: system-printer.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h system-webif.o: system-webif.c pappl-private.h device.h base.h \ dnssd-private.h base-private.h ../config.h system-private.h system.h \ log.h client-private.h client.h printer-private.h printer.h \ job-private.h job.h mainloop-private.h mainloop.h log-private.h util.o: util.c base-private.h base.h ../config.h pappl-1.0.3/pappl/Info.plist000066400000000000000000000016101403603036100157050ustar00rootroot00000000000000 CFBundleDevelopmentRegion en CFBundleExecutable pappl CFBundleIdentifier org.msweet.pappl CFBundleInfoDictionaryVersion 6.0 CFBundleName pappl CFBundlePackageType FMWK CFBundleShortVersionString 0.1 CFBundleSupportedPlatforms MacOSX CFBundleVersion 0.1 LSMinimumSystemVersion 10.14 NSHumanReadableCopyright Copyright © 2019-2020 Michael Sweet. All rights reserved. pappl-1.0.3/pappl/Makefile000066400000000000000000000144771403603036100154140ustar00rootroot00000000000000# # Library makefile for the Printer Application Framework # # Copyright © 2020 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # include ../Makedefs OBJS = \ client.o \ client-accessors.o \ client-auth.o \ client-ipp.o \ client-webif.o \ contact.o \ device.o \ device-file.o \ device-network.o \ device-usb.o \ dnssd.o \ job-accessors.o \ job-filter.o \ job-ipp.o \ job-process.o \ job.o \ link.o \ log.o \ lookup.o \ mainloop.o \ mainloop-subcommands.o \ mainloop-support.o \ printer.o \ printer-accessors.o \ printer-driver.o \ printer-ipp.o \ printer-raw.o \ printer-support.o \ printer-usb.o \ printer-webif.o \ resource.o \ snmp.o \ system.o \ system-accessors.o \ system-ipp.o \ system-loadsave.o \ system-printer.o \ system-webif.o \ util.o HEADERS = \ base.h \ client.h \ device.h \ job.h \ log.h \ mainloop.h \ pappl.h \ printer.h \ system.h RESOURCES = \ icon-sm.png \ icon-md.png \ icon-lg.png \ style.css DOCFLAGS = \ --author "Michael R Sweet" \ --copyright "Copyright (c) 2020 by Michael R Sweet" \ --docversion $(PAPPL_VERSION) .SUFFIXES: md DOCFILES = \ device.md \ hello-world.md \ introduction.md \ job.md \ mainloop.md \ printer.md \ resource.md \ system.md TARGETS = $(LIBPAPPL) libpappl.a # Make everything all: $(TARGETS) # Clean everything clean: $(RM) $(OBJS) $(RM) $(TARGETS) $(RM) libpappl.dylib libpappl.so # Clean all non-distribution files distclean: clean # Update dependencies depend: $(CC) -MM $(CFLAGS) $(OBJS:.o=.c) | sed -e '1,$$s/ \/usr\/include\/[^ ]*//g' -e '1,$$s/ \/usr\/local\/include\/[^ ]*//g' >Dependencies # Generate documentation uaing codedoc (https://www.msweet.org/codedoc) doc: echo Generating pappl.html... codedoc $(DOCFLAGS) --title "PAPPL v$(PAPPL_VERSION_MAJOR).$(PAPPL_VERSION_MINOR) Programmers Manual" --coverimage ../doc/pappl-512.png --body ../doc/pappl-body.md pappl.xml $(HEADERS) $(OBJS:.o=.c) >../doc/pappl.html echo Generating pappl.epub... codedoc $(DOCFLAGS) --title "PAPPL v$(PAPPL_VERSION_MAJOR).$(PAPPL_VERSION_MINOR) Programmers Manual" --coverimage epub.png --body ../doc/pappl-body.md pappl.xml --epub ../doc/pappl.epub $(RM) pappl.xml echo Generating pappl-client.3... codedoc $(DOCFLAGS) --title "pappl client functions" --man pappl-client --section 3 --body ../man/pappl-client-body.man --footer ../man/pappl-footer.man client.h client*.c >../man/pappl-client.3 echo Generating pappl-device.3... codedoc $(DOCFLAGS) --title "pappl device functions" --man pappl-device --section 3 --body ../man/pappl-device-body.man --footer ../man/pappl-footer.man device.h device.c >../man/pappl-device.3 echo Generating pappl-job.3... codedoc $(DOCFLAGS) --title "pappl job functions" --man pappl-job --section 3 --body ../man/pappl-job-body.man --footer ../man/pappl-footer.man job.h job*.c >../man/pappl-job.3 echo Generating pappl-log.3... codedoc $(DOCFLAGS) --title "pappl logging functions" --man pappl-log --section 3 --body ../man/pappl-log-body.man --footer ../man/pappl-footer.man log.h log.c >../man/pappl-log.3 echo Generating pappl-mainloop.3... codedoc $(DOCFLAGS) --title "pappl main loop functions" --man pappl-mainloop --section 3 --body ../man/pappl-mainloop-body.man --footer ../man/pappl-footer.man mainloop.h mainloop*.c >../man/pappl-mainloop.3 echo Generating pappl-printer.3... codedoc $(DOCFLAGS) --title "pappl printer functions" --man pappl-printer --section 3 --body ../man/pappl-printer-body.man --footer ../man/pappl-footer.man printer.h printer*.c >../man/pappl-printer.3 echo Generating pappl-resource.3... codedoc $(DOCFLAGS) --title "pappl resource functions" --man pappl-resource --section 3 --body ../man/pappl-resource-body.man --footer ../man/pappl-footer.man link.c resource.c >../man/pappl-resource.3 echo Generating pappl-system.3... codedoc $(DOCFLAGS) --title "pappl system functions" --man pappl-system --section 3 --body ../man/pappl-system-body.man --footer ../man/pappl-footer.man system.h system*.c >../man/pappl-system.3 # Install everything install: $(TARGETS) echo Installing pappl-makeresheader in $(BUILDROOT)$(bindir)... $(INSTALL) -d -m 755 $(BUILDROOT)$(bindir) $(INSTALL) -c -m 755 makeresheader.sh $(BUILDROOT)$(bindir)/pappl-makeresheader echo Installing libpappl in $(BUILDROOT)$(libdir)... $(INSTALL) -d -m 755 $(BUILDROOT)$(libdir) $(INSTALL) -c -m 644 libpappl.a $(BUILDROOT)$(libdir) $(INSTALL) -c -m 755 $(LIBPAPPL) $(BUILDROOT)$(libdir) if test "$(LIBPAPPL)" = libpappl.so.1; then \ $(RM) $(BUILDROOT)$(libdir)/libpappl.so; \ $(LN) -s libpappl.so.1 $(BUILDROOT)$(libdir)/libpappl.so; \ elif test "$(LIBPAPPL)" = libpappl.1.dylib; then \ $(RM) $(BUILDROOT)$(libdir)/libpappl.dylib; \ $(LN) -s libpappl.1.dylib $(BUILDROOT)$(libdir)/libpappl.dylib; \ fi echo Installing pkg-config files in $(BUILDROOT)$(libdir)/pkgconfig... $(INSTALL) -d -m 755 $(BUILDROOT)$(libdir)/pkgconfig $(INSTALL) -c -m 644 pappl.pc $(BUILDROOT)$(libdir)/pkgconfig echo Installing data files in $(BUILDROOT)$(datadir)/pappl... $(INSTALL) -d -m 755 $(BUILDROOT)$(datadir)/pappl for file in $(RESOURCES); do \ $(INSTALL) -c -m 644 $$file $(BUILDROOT)$(datadir)/pappl; \ done echo Installing header files in $(BUILDROOT)$(includedir)/pappl... $(INSTALL) -d -m 755 $(BUILDROOT)$(includedir)/pappl for file in $(HEADERS); do \ $(INSTALL) -c -m 644 $$file $(BUILDROOT)$(includedir)/pappl; \ done # Test everything test: # pappl static library libpappl.a: $(OBJS) echo Archiving $@... $(RM) $@ $(AR) $(ARFLAGS) $@ $(OBJS) $(RANLIB) $@ # pappl shared library libpappl.so.1: $(OBJS) echo Linking $@... $(CC) $(DSOFLAGS) -shared -o $@ -Wl,-soname,$@ $(OBJS) $(LIBS) $(RM) `basename $@ .1` $(LN) $@ `basename $@ .1` # pappl shared library (macOS) libpappl.1.dylib: $(OBJS) echo Linking $@... $(CC) $(DSOFLAGS) -dynamiclib -o $@ \ -install_name $(libdir)/$@ \ -current_version $(PAPPL_VERSION_MAJOR).$(PAPPL_VERSION_MINOR) -compatibility_version 1.0 \ $(OBJS) $(LIBS) $(CODE_SIGN) $(CSFLAGS) -i org.msweet.pappl.`basename $@ .1.dylib` $@ $(RM) `basename $@ .1.dylib`.dylib $(LN) $@ `basename $@ .1.dylib`.dylib # Static resource header... resheader: $(RESOURCES) echo Generating $@... ./makeresheader.sh $(RESOURCES) >resource-private.h # Dependencies include Dependencies pappl-1.0.3/pappl/base-private.h000066400000000000000000000042211403603036100164710ustar00rootroot00000000000000// // Private base definitions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_BASE_PRIVATE_H_ # define _PAPPL_BASE_PRIVATE_H_ // // Include necessary headers... // # include "base.h" # include # include # include # include # include # include extern char **environ; // // Macros... // # ifdef DEBUG # define _PAPPL_DEBUG(...) fprintf(stderr, __VA_ARGS__) # else # define _PAPPL_DEBUG(...) # endif // DEBUG # define _PAPPL_LOOKUP_STRING(bit,strings) _papplLookupString(bit, sizeof(strings) / sizeof(strings[0]), strings) # define _PAPPL_LOOKUP_VALUE(keyword,strings) _papplLookupValue(keyword, sizeof(strings) / sizeof(strings[0]), strings) # ifndef HAVE_STRLCPY # define strlcpy(dst,src,dstsize) _pappl_strlcpy(dst,src,dstsize) # endif // !HAVE_STRLCPY // // Types and structures... // typedef struct _pappl_ipp_filter_s // Attribute filter { cups_array_t *ra; // Requested attributes ipp_tag_t group_tag; // Group to copy } _pappl_ipp_filter_t; typedef struct _pappl_link_s // Web interface navigation link { char *label, // Label *path_or_url; // Path or URL pappl_loptions_t options; // Link options } _pappl_link_t; // // Utility functions... // # ifndef HAVE_STRLCPY extern size_t _pappl_strlcpy(char *dst, const char *src, size_t dstsize) _PAPPL_PRIVATE; # endif // !HAVE_STRLCPY extern ipp_t *_papplContactExport(pappl_contact_t *contact) _PAPPL_PRIVATE; extern void _papplContactImport(ipp_t *col, pappl_contact_t *contact) _PAPPL_PRIVATE; extern void _papplCopyAttributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy) _PAPPL_PRIVATE; extern unsigned _papplGetRand(void) _PAPPL_PRIVATE; extern const char *_papplLookupString(unsigned bit, size_t num_strings, const char * const *strings) _PAPPL_PRIVATE; extern unsigned _papplLookupValue(const char *keyword, size_t num_strings, const char * const *strings) _PAPPL_PRIVATE; #endif // !_PAPPL_BASE_PRIVATE_H_ pappl-1.0.3/pappl/base.h000066400000000000000000000074151403603036100150310ustar00rootroot00000000000000// // Base definitions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_BASE_H_ # define _PAPPL_BASE_H_ // // Include necessary headers... // # include # include # include # include # include # include # include # include # include # include # include // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // New operations/tags defined in CUPS 2.3 and later... // # if CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR < 3 # define IPP_OP_CREATE_PRINTER (ipp_op_t)0x004C # define IPP_OP_DELETE_PRINTER (ipp_op_t)0x004E # define IPP_OP_GET_PRINTERS (ipp_op_t)0x004F # define IPP_OP_GET_SYSTEM_ATTRIBUTES (ipp_op_t)0x005B # define IPP_OP_SET_SYSTEM_ATTRIBUTES (ipp_op_t)0x0062 # define IPP_OP_SHUTDOWN_ALL_PRINTERS (ipp_op_t)0x0063 # define IPP_TAG_SYSTEM (ipp_tag_t)0x000A # endif // CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR < 3 // // Visibility and other annotations... // # if defined(__has_extension) || defined(__GNUC__) # define _PAPPL_INTERNAL __attribute__ ((visibility("hidden"))) # define _PAPPL_PRIVATE __attribute__ ((visibility("default"))) # define _PAPPL_PUBLIC __attribute__ ((visibility("default"))) # define _PAPPL_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b))) # define _PAPPL_NONNULL(...) __attribute__ ((nonnull(__VA_ARGS__))) # define _PAPPL_NORETURN __attribute__ ((noreturn)) # else # define _PAPPL_INTERNAL # define _PAPPL_PRIVATE # define _PAPPL_PUBLIC # define _PAPPL_FORMAT(a,b) # define _PAPPL_NONNULL(...) # define _PAPPL_NORETURN # endif // __has_extension || __GNUC__ // // Common types... // typedef struct _pappl_client_s pappl_client_t; // Client connection object typedef struct _pappl_device_s pappl_device_t; // Device connection object typedef unsigned char pappl_dither_t[16][16]; // 16x16 dither array typedef struct pappl_pr_driver_data_s pappl_pr_driver_data_t; // Print driver data typedef struct _pappl_job_s pappl_job_t;// Job object typedef struct pappl_pr_options_s pappl_pr_options_t; // Combined print job options typedef unsigned int pappl_preason_t; // Bitfield for IPP "printer-state-reasons" values typedef struct _pappl_printer_s pappl_printer_t; // Printer object typedef struct _pappl_system_s pappl_system_t; // System object typedef struct pappl_contact_s // Contact information { char name[256]; // Contact name char email[256]; // Contact email address char telephone[256]; // Contact phone number } pappl_contact_t; enum pappl_loptions_e // Link option bits { PAPPL_LOPTIONS_NAVIGATION = 0x0001, // Link shown in navigation bar PAPPL_LOPTIONS_CONFIGURATION = 0x0002, // Link shown in configuration section PAPPL_LOPTIONS_JOB = 0x0004, // Link shown in job(s) section PAPPL_LOPTIONS_LOGGING = 0x0008, // Link shown in logging section PAPPL_LOPTIONS_NETWORK = 0x0010, // Link shown in network section PAPPL_LOPTIONS_PRINTER = 0x0020, // Link shown in printer(s) section PAPPL_LOPTIONS_SECURITY = 0x0040, // Link shown in security section PAPPL_LOPTIONS_STATUS = 0x0080, // Link shown in status section PAPPL_LOPTIONS_TLS = 0x0100, // Link shown in TLS section PAPPL_LOPTIONS_OTHER = 0x0200, // Link shown in other section PAPPL_LOPTIONS_HTTPS_REQUIRED = 0x8000 // Link requires HTTPS }; typedef unsigned short pappl_loptions_t;// Bitfield for link options // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_BASE_H_ pappl-1.0.3/pappl/client-accessors.c000066400000000000000000000161101403603036100173430ustar00rootroot00000000000000// // Client accessor functions for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "client-private.h" #include "system.h" // // 'papplClientGetCSRFToken()' - Get a unique Cross-Site Request Forgery token // string. // // This function generates and returns a unique Cross-Site Request Forgery // token string to be used as the value of a hidden variable in all HTML forms // sent in the response and then compared when validating the form data in the // subsequent request. // // The value is based on the current system session key and client address in // order to make replay attacks infeasible. // // > Note: The @link papplClientHTMLStartForm@ function automatically adds the // > hidden CSRF variable, and the @link papplClientIsValidForm@ function // > validates the value. // char * // O - Token string papplClientGetCSRFToken( pappl_client_t *client, // I - Client char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char session_key[65], // Current session key csrf_data[1024]; // CSRF data to hash unsigned char csrf_sum[32]; // SHA2-256 sum of data if (!client || !buffer || bufsize < 65) { if (buffer) *buffer = '\0'; return (NULL); } snprintf(csrf_data, sizeof(csrf_data), "%s:%s", papplSystemGetSessionKey(client->system, session_key, sizeof(session_key)), client->hostname); cupsHashData("sha2-256", csrf_data, strlen(csrf_data), csrf_sum, sizeof(csrf_sum)); cupsHashString(csrf_sum, sizeof(csrf_sum), buffer, bufsize); return (buffer); } // // 'papplClientGetHostName()' - Get the hostname from the client-supplied Host: // field. // // This function returns the hostname that was used in the request and should // be used in any URLs or URIs that you generate. // const char * // O - Hostname or `NULL` for none papplClientGetHostName( pappl_client_t *client) // I - Client { return (client ? client->host_field : NULL); } // // 'papplClientGetHostPort()' - Get the port from the client-supplied Host: // field. // // This function returns the port number that was used in the request and should // be used in any URLs or URIs that you generate. // int // O - Port number or `0` for none papplClientGetHostPort( pappl_client_t *client) // I - Client { return (client ? client->host_port : 0); } // // 'papplClientGetHTTP()' - Get the HTTP connection associated with a client // object. // // This function returns the HTTP connection associated with the client and is // used when sending response data directly to the client using the CUPS // `httpXxx` functions. // http_t * // O - HTTP connection papplClientGetHTTP( pappl_client_t *client) // I - Client { return (client ? client->http : NULL); } // // 'papplClientGetJob()' - Get the target job for an IPP request. // // This function returns the job associated with the current IPP request. // `NULL` is returned if the request does not target a job. // pappl_job_t * // O - Target job or `NULL` if none papplClientGetJob( pappl_client_t *client) // I - Client { return (client ? client->job : NULL); } // // 'papplClientGetMethod()' - Get the HTTP request method. // // This function returns the HTTP request method that was used, for example // `HTTP_STATE_GET` for a GET request or `HTTP_STATE_POST` for a POST request. // http_state_t // O - HTTP method papplClientGetMethod( pappl_client_t *client) // I - Client { return (client ? client->operation : HTTP_STATE_ERROR); } // // 'papplClientGetOperation()' - Get the IPP operation code. // // This function returns the IPP operation code associated with the current IPP // request. // ipp_op_t // O - IPP operation code papplClientGetOperation( pappl_client_t *client) // I - Client { return (client ? client->operation_id : IPP_OP_CUPS_NONE); } // // 'papplClientGetOptions()' - Get the options from the request URI. // // This function returns any options that were passed in the HTTP request URI. // The options are the characters after the "?" character, for example a // request URI of "/mypage?name=value" will have an options string of // "name=value". // // `NULL` is returned if the request URI did not contain any options. // // > Note: HTTP GET form variables are normally accessed using the // > @link papplClientGetForm@ function. This function should only be used when // > getting non-form data. // const char * // O - Options or `NULL` if none papplClientGetOptions( pappl_client_t *client) // I - Client { return (client ? client->options : NULL); } // // 'papplClientGetPrinter()' - Get the target printer for an IPP request. // // This function returns the printer associated with the current IPP request. // `NULL` is returned if the request does not target a printer. // pappl_printer_t * // O - Target printer or `NULL` if none papplClientGetPrinter( pappl_client_t *client) // I - Client { return (client ? client->printer : NULL); } // // 'papplClientGetRequest()' - Get the IPP request message. // // This function returns the attributes in the current IPP request, for use // with the CUPS `ippFindAttribute`, `ippFindNextAttribute`, // `ippFirstAttribute`, and `ippNextAttribute` functions. // ipp_t * // O - IPP request message papplClientGetRequest( pappl_client_t *client) // I - Client { return (client ? client->request : NULL); } // // 'papplClientGetResponse()' - Get the IPP response message. // // This function returns the attributes in the current IPP response, for use // with the CUPS `ippAddXxx` and `ippSetXxx` functions. Use the // @link papplClientRespondIPP@ function to set the status code and message, // if any. // ipp_t * // O - IPP response message papplClientGetResponse( pappl_client_t *client) // I - Client { return (client ? client->response : NULL); } // // 'papplClientGetSystem()' - Get the containing system for the client. // // This function returns the system object that contains the client. // pappl_system_t * // O - System papplClientGetSystem( pappl_client_t *client) // I - Client { return (client ? client->system : NULL); } // // 'papplClientGetURI()' - Get the HTTP request URI. // // This function returns the URI that was sent in the current HTTP request. // // > Note: Any options in the URI are removed and can be accessed separately // > using the @link papplClientGetOptions@ function. // const char * // O - Request URI papplClientGetURI( pappl_client_t *client) // I - Client { return (client ? client->uri : NULL); } // // 'papplClientGetUsername()' - Get the authenticated username, if any. // // This function returns the current authenticated username, if any. // const char * // O - Authenticated username or `NULL` if none papplClientGetUsername( pappl_client_t *client) // I - Client { return (client && client->username[0] ? client->username : NULL); } pappl-1.0.3/pappl/client-auth.c000066400000000000000000000216641403603036100163310ustar00rootroot00000000000000// // Authentication support for the Printer Application Framework // // Copyright © 2017-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "client-private.h" #include "system-private.h" #include #ifdef HAVE_LIBPAM # ifdef HAVE_PAM_PAM_APPL_H # include # else # include # endif // HAVE_PAM_PAM_APPL_H #endif // HAVE_LIBPAM // // Types... // typedef struct _pappl_authdata_s // PAM authentication data { const char *username, // Username string *password; // Password string } _pappl_authdata_t; // // Local functions... // static int pappl_authenticate_user(pappl_client_t *client, const char *username, const char *password); #ifdef HAVE_LIBPAM static int pappl_pam_func(int num_msg, const struct pam_message **msg, struct pam_response **resp, _pappl_authdata_t *data); #endif // HAVE_LIBPAM // // 'papplClientIsAuthorized()' - Determine whether a client is authorized for // administrative requests. // // This function determines whether a client is authorized to submit an // administrative request. // // The return value is `HTTP_STATUS_CONTINUE` if access is authorized, // `HTTP_STATUS_FORBIDDEN` if access is not allowed, `HTTP_STATUS_UNAUTHORIZED` // if authorization is required, or `HTTP_STATUS_UPGRADE_REQUIRED` if the // connection needs to be encrypted. All of these values can be passed to the // @link papplClientRespond@ function. // http_status_t // O - HTTP status papplClientIsAuthorized( pappl_client_t *client) // I - Client { const char *authorization; // Authorization: header value // Local access is always allowed... if (httpAddrFamily(httpGetAddress(client->http)) == AF_LOCAL) return (HTTP_STATUS_CONTINUE); if (httpAddrLocalhost(httpGetAddress(client->http)) && !client->system->auth_service) return (HTTP_STATUS_CONTINUE); // Remote access is only allowed if a PAM authentication service is configured... if (!client->system->auth_service) return (HTTP_STATUS_FORBIDDEN); // Remote admin access requires encryption... if (!httpIsEncrypted(client->http) && !httpAddrLocalhost(httpGetAddress(client->http))) return (HTTP_STATUS_UPGRADE_REQUIRED); // Get the authorization header... if ((authorization = httpGetField(client->http, HTTP_FIELD_AUTHORIZATION)) != NULL && *authorization) { if (!strncmp(authorization, "Basic ", 6)) { // Basic authentication... char username[512], // Username value *password; // Password value int userlen = sizeof(username); // Length of username:password struct passwd *user; // User information int num_groups; // Number of autbenticated groups, if any # ifdef __APPLE__ int groups[32]; // Authenticated groups, if any # else gid_t groups[32]; // Authenticated groups, if any # endif // __APPLE__ for (authorization += 6; *authorization && isspace(*authorization & 255); authorization ++) ; // Skip whitespace httpDecode64_2(username, &userlen, authorization); if ((password = strchr(username, ':')) != NULL) { *password++ = '\0'; // Authenticate the username and password... if (pappl_authenticate_user(client, username, password)) { // Get the user information (groups, etc.) if ((user = getpwnam(username)) != NULL) { papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Authenticated as \"%s\" using Basic.", username); strlcpy(client->username, username, sizeof(client->username)); num_groups = (int)(sizeof(groups) / sizeof(groups[0])); #ifdef __APPLE__ if (getgrouplist(username, (int)user->pw_gid, groups, &num_groups)) #else if (getgrouplist(username, user->pw_gid, groups, &num_groups)) #endif // __APPLE__ { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to lookup groups for user '%s': %s", username, strerror(errno)); num_groups = 0; } // Check group membership... if (client->system->admin_gid != (gid_t)-1) { if (user->pw_gid != client->system->admin_gid) { int i; // Looping var for (i = 0; i < num_groups; i ++) { if ((gid_t)groups[i] == client->system->admin_gid) break; } if (i >= num_groups) { // Not in the admin group, access is forbidden... return (HTTP_STATUS_FORBIDDEN); } } } // If we get this far, authentication and authorization are good... return (HTTP_STATUS_CONTINUE); } else { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to lookup user '%s'.", username); return (HTTP_STATUS_SERVER_ERROR); } } else { papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Basic authentication of '%s' failed.", username); } } else { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Bad Basic Authorization header value seen."); return (HTTP_STATUS_BAD_REQUEST); } } else { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unsupported Authorization header value seen."); return (HTTP_STATUS_BAD_REQUEST); } } // If we get there then we don't have any authorization value we can use... return (HTTP_STATUS_UNAUTHORIZED); } // // 'pappl_authenticate_user()' - Validate a username + password combination. // static int // O - 1 if correct, 0 otherwise pappl_authenticate_user( pappl_client_t *client, // I - Client const char *username, // I - Username string const char *password) // I - Password string { int status = 0; // Return status #ifdef HAVE_LIBPAM _pappl_authdata_t data; // Authorization data pam_handle_t *pamh; // PAM authentication handle int pamerr; // PAM error code struct pam_conv pamdata; // PAM conversation data data.username = username; data.password = password; pamdata.conv = (int (*)(int, const struct pam_message **, struct pam_response **, void *))pappl_pam_func; pamdata.appdata_ptr = &data; pamh = NULL; if ((pamerr = pam_start(client->system->auth_service, data.username, &pamdata, &pamh)) != PAM_SUCCESS) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "pam_start() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr)); } # ifdef PAM_RHOST else if ((pamerr = pam_set_item(pamh, PAM_RHOST, client->hostname)) != PAM_SUCCESS) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "pam_set_item(PAM_RHOST) returned %d (%s)", pamerr, pam_strerror(pamh, pamerr)); } # endif // PAM_RHOST # ifdef PAM_TTY else if ((pamerr = pam_set_item(pamh, PAM_TTY, "lprint")) != PAM_SUCCESS) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "pam_set_item(PAM_TTY) returned %d (%s)", pamerr, pam_strerror(pamh, pamerr)); } # endif // PAM_TTY else if ((pamerr = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "pam_authenticate() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr)); } else if ((pamerr = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT)) != PAM_SUCCESS) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "pam_setcred() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr)); } else if ((pamerr = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "pam_acct_mgmt() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr)); } if (pamh) pam_end(pamh, PAM_SUCCESS); if (pamerr == PAM_SUCCESS) { papplLogClient(client, PAPPL_LOGLEVEL_INFO, "PAM authentication of '%s' succeeded.", username); status = 1; } #endif // HAVE_LIBPAM return (status); } #ifdef HAVE_LIBPAM // // 'pappl_pam_func()' - PAM conversation function. // static int // O - Success or failure pappl_pam_func( int num_msg, // I - Number of messages const struct pam_message **msg, // I - Messages struct pam_response **resp, // O - Responses _pappl_authdata_t *data) // I - Authentication data { int i; // Looping var struct pam_response *replies; // Replies // Allocate memory for the responses... if ((replies = calloc((size_t)num_msg, sizeof(struct pam_response))) == NULL) return (PAM_CONV_ERR); // Answer all of the messages... for (i = 0; i < num_msg; i ++) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON : replies[i].resp_retcode = PAM_SUCCESS; replies[i].resp = strdup(data->username); break; case PAM_PROMPT_ECHO_OFF : replies[i].resp_retcode = PAM_SUCCESS; replies[i].resp = strdup(data->password); break; default : replies[i].resp_retcode = PAM_SUCCESS; replies[i].resp = NULL; break; } } // Return the responses back to PAM... *resp = replies; return (PAM_SUCCESS); } #endif // HAVE_LIBPAM pappl-1.0.3/pappl/client-ipp.c000066400000000000000000000234471403603036100161610ustar00rootroot00000000000000// // Common client IPP processing for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // '_papplClientFlushDocumentData()' - Safely flush remaining document data. // void _papplClientFlushDocumentData( pappl_client_t *client) // I - Client { char buffer[8192]; // Read buffer if (httpGetState(client->http) == HTTP_STATE_POST_RECV) { while (httpRead2(client->http, buffer, sizeof(buffer)) > 0) ; // Read all data } } // // '_papplClientHaveDocumentData()' - Determine whether we have more document data. // bool // O - `true` if data is present, `false` otherwise _papplClientHaveDocumentData( pappl_client_t *client) // I - Client { char temp; // Data if (httpGetState(client->http) != HTTP_STATE_POST_RECV) return (false); else return (httpPeek(client->http, &temp, 1) > 0); } // // '_papplClientProcessIPP()' - Process an IPP request. // bool // O - `true` on success, `false` on error _papplClientProcessIPP( pappl_client_t *client) // I - Client { ipp_tag_t group; // Current group tag ipp_attribute_t *attr; // Current attribute ipp_attribute_t *charset; // Character set attribute ipp_attribute_t *language; // Language attribute ipp_attribute_t *uri; // Printer URI attribute int major, minor; // Version number ipp_op_t op; // Operation code const char *name; // Name of attribute bool printer_op = true; // Printer operation? // First build an empty response message for this request... client->operation_id = ippGetOperation(client->request); client->response = ippNewResponse(client->request); // Then validate the request header and required attributes... major = ippGetVersion(client->request, &minor); op = ippGetOperation(client->request); _papplLogAttributes(client, ippOpString(op), client->request, false); if (major < 1 || major > 2) { // Return an error, since we only support IPP 1.x and 2.x. papplClientRespondIPP(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor); } else if (ippGetRequestId(client->request) <= 0) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request)); } else if (!ippFirstAttribute(client->request)) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request."); } else { // Make sure that the attributes are provided in the correct order and // don't repeat groups... for (attr = ippFirstAttribute(client->request), group = ippGetGroupTag(attr); attr; attr = ippNextAttribute(client->request)) { if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO) { // Out of order; return an error... papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Attribute groups are out of order (%x < %x).", ippGetGroupTag(attr), group); break; } else group = ippGetGroupTag(attr); } if (!attr) { // Then make sure that the first three attributes are: // // attributes-charset // attributes-natural-language // system-uri/printer-uri/job-uri attr = ippFirstAttribute(client->request); name = ippGetName(attr); if (attr && name && !strcmp(name, "attributes-charset") && ippGetValueTag(attr) == IPP_TAG_CHARSET) charset = attr; else charset = NULL; attr = ippNextAttribute(client->request); name = ippGetName(attr); if (attr && name && !strcmp(name, "attributes-natural-language") && ippGetValueTag(attr) == IPP_TAG_LANGUAGE) language = attr; else language = NULL; if ((attr = ippFindAttribute(client->request, "system-uri", IPP_TAG_URI)) != NULL) uri = attr; else if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL) uri = attr; else if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL) uri = attr; else uri = NULL; client->printer = NULL; client->job = NULL; if (charset && strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") && strcasecmp(ippGetString(charset, 0, NULL), "utf-8")) { // Bad character set... papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unsupported character set \"%s\".", ippGetString(charset, 0, NULL)); } else if (!charset || !language || (!uri && op != IPP_OP_CUPS_GET_DEFAULT && op != IPP_OP_CUPS_GET_PRINTERS)) { // Return an error, since attributes-charset, // attributes-natural-language, and system/printer/job-uri are required // for all operations. papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required attributes."); } else { if (uri) { char scheme[32], // URI scheme userpass[32], // Username/password in URI host[256], // Host name in URI resource[256], // Resource path in URI *resptr; // Pointer into resource int port, // Port number in URI job_id; // Job ID name = ippGetName(uri); if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL), scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) { papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL)); } else if (!strcmp(name, "system-uri")) { printer_op = false; if (strcmp(resource, "/ipp/system")) { papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL)); } else client->printer = papplSystemFindPrinter(client->system, NULL, ippGetInteger(ippFindAttribute(client->request, "printer-id", IPP_TAG_INTEGER), 0), NULL); } else if ((client->printer = papplSystemFindPrinter(client->system, resource, 0, NULL)) != NULL) { if (!strcmp(name, "job-uri") && (resptr = strrchr(resource, '/')) != NULL) { char *endptr; // Pointer after job ID job_id = (int)strtol(resptr + 1, &endptr, 10); if (errno == ERANGE || *endptr) job_id = 0; } else job_id = ippGetInteger(ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER), 0); if (job_id) { if ((client->job = papplPrinterFindJob(client->printer, job_id)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "job-id %d not found.", job_id); } } } else { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.", name, ippGetString(uri, 0, NULL)); } } else printer_op = false; if (ippGetStatusCode(client->response) == IPP_STATUS_OK) { if (printer_op) { // Process job or printer operation... if (client->job) _papplJobProcessIPP(client); else _papplPrinterProcessIPP(client); } else { // Process system operation... _papplSystemProcessIPP(client); } } } } } // Send the HTTP header and return... if (httpGetState(client->http) != HTTP_STATE_POST_SEND) _papplClientFlushDocumentData(client); // Flush trailing (junk) data return (papplClientRespond(client, HTTP_STATUS_OK, NULL, "application/ipp", 0, ippLength(client->response))); } // // 'papplClientRespondIPP()' - Send an IPP response. // // This function sets the return status for an IPP request and returns the // current IPP response message. The "status" and "message" arguments replace // any existing status-code and "status-message" attribute value that may be // already present in the response. // // > Note: You should call this function prior to adding any response // > attributes. // ipp_t * // O - IPP response message papplClientRespondIPP( pappl_client_t *client, // I - Client ipp_status_t status, // I - status-code const char *message, // I - printf-style status-message ...) // I - Additional args as needed { const char *formatted = NULL; // Formatted message ippSetStatusCode(client->response, status); if (message) { va_list ap; // Pointer to additional args ipp_attribute_t *attr; // New status-message attribute va_start(ap, message); if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL) ippSetStringfv(client->response, &attr, 0, message, ap); else attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap); va_end(ap); formatted = ippGetString(attr, 0, NULL); } if (formatted) papplLogClient(client, PAPPL_LOGLEVEL_INFO, "%s %s (%s)", ippOpString(client->operation_id), ippErrorString(status), formatted); else papplLogClient(client, PAPPL_LOGLEVEL_INFO, "%s %s", ippOpString(client->operation_id), ippErrorString(status)); return (client->response); } // // 'papplClientRespondIPPUnsupported()' - Respond with an unsupported IPP attribute. // // This function returns a 'client-error-attributes-or-values-not-supported' // status code and adds the specified attribute to the unsupported attributes // group in the response. // void papplClientRespondIPPUnsupported( pappl_client_t *client, // I - Client ipp_attribute_t *attr) // I - Atribute { ipp_attribute_t *temp; // Copy of attribute papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); temp = ippCopyAttribute(client->response, attr, 0); ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP); } pappl-1.0.3/pappl/client-private.h000066400000000000000000000047121403603036100170420ustar00rootroot00000000000000// // Private client header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_CLIENT_PRIVATE_H_ # define _PAPPL_CLIENT_PRIVATE_H_ // // Include necessary headers... // # include "base-private.h" # include "client.h" # include "log.h" // // Client structure... // struct _pappl_client_s // Client data { pappl_system_t *system; // Containing system int number; // Connection number pthread_t thread_id; // Thread ID http_t *http; // HTTP connection ipp_t *request, // IPP request *response; // IPP response time_t start; // Request start time http_state_t operation; // Request operation ipp_op_t operation_id; // IPP operation-id char uri[1024], // Request URI *options, // URI options host_field[HTTP_MAX_VALUE]; // Host: header int host_port; // Port number from Host: header http_addr_t addr; // Client address char hostname[256]; // Client hostname char username[256]; // Authenticated username, if any pappl_printer_t *printer; // Printer, if any pappl_job_t *job; // Job, if any int num_files; // Number of temporary files char *files[10]; // Temporary files }; // // Functions... // extern void _papplClientCleanTempFiles(pappl_client_t *client) _PAPPL_PRIVATE; extern pappl_client_t *_papplClientCreate(pappl_system_t *system, int sock) _PAPPL_PRIVATE; extern char *_papplClientCreateTempFile(pappl_client_t *client, const void *data, size_t datasize) _PAPPL_PRIVATE; extern void _papplClientDelete(pappl_client_t *client) _PAPPL_PRIVATE; extern void _papplClientFlushDocumentData(pappl_client_t *client) _PAPPL_PRIVATE; extern bool _papplClientHaveDocumentData(pappl_client_t *client) _PAPPL_PRIVATE; extern bool _papplClientProcessHTTP(pappl_client_t *client) _PAPPL_PRIVATE; extern bool _papplClientProcessIPP(pappl_client_t *client) _PAPPL_PRIVATE; extern void *_papplClientRun(pappl_client_t *client) _PAPPL_PRIVATE; extern void _papplClientHTMLInfo(pappl_client_t *client, bool is_form, const char *dns_sd_name, const char *location, const char *geo_location, const char *organization, const char *org_unit, pappl_contact_t *contact); extern void _papplClientHTMLPutLinks(pappl_client_t *client, cups_array_t *links, pappl_loptions_t which); #endif // !_PAPPL_CLIENT_PRIVATE_H_ pappl-1.0.3/pappl/client-webif.c000066400000000000000000001213331403603036100164560ustar00rootroot00000000000000// // Core client web interface functions for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" #include // // 'papplClientGetCookie()' - Get a cookie from the client. // // This function gets a HTTP "cookie" value from the client request. `NULL` // is returned if no cookie has been set by a prior request, or if the user has // disabled or removed the cookie. // // Use the @link papplClientSetCookie@ function to set a cookie in a response // to a request. // // > Note: Cookies set with @link papplClientSetCookie@ will not be available to // > this function until the following request. // char * // O - Cookie value or `NULL` if not set papplClientGetCookie( pappl_client_t *client, // I - Client const char *name, // I - Name of cookie char *buffer, // I - Value buffer size_t bufsize) // I - Size of value buffer { const char *cookie = httpGetCookie(client->http); // Cookies from client char temp[256], // Temporary string *ptr, // Pointer into temporary string *end; // End of temporary string bool found; // Did we find it? // Make sure the buffer is initialize, and return if we don't have any // cookies... *buffer = '\0'; if (!cookie) return (NULL); // Scan the cookie string for 'name=value' or 'name="value"', separated by // semicolons... while (*cookie) { while (*cookie && isspace(*cookie & 255)) cookie ++; if (!*cookie) break; for (ptr = temp, end = temp + sizeof(temp) - 1; *cookie && *cookie != '='; cookie ++) { if (ptr < end) *ptr++ = *cookie; } if (*cookie == '=') { cookie ++; *ptr = '\0'; found = !strcmp(temp, name); if (found) { ptr = buffer; end = buffer + bufsize - 1; } else { ptr = temp; end = temp + sizeof(temp) - 1; } if (*cookie == '\"') { for (cookie ++; *cookie && *cookie != '\"'; cookie ++) { if (ptr < end) *ptr++ = *cookie; } if (*cookie == '\"') cookie ++; } else { for (; *cookie && *cookie != ';'; cookie ++) { if (ptr < end) *ptr++ = *cookie; } } *ptr = '\0'; if (found) return (buffer); else if (*cookie == ';') cookie ++; } } return (NULL); } // // 'papplClientGetForm()' - Get form data from the web client. // // For HTTP GET requests, the form data is collected from the request URI. For // HTTP POST requests, the form data is read from the client. // // The returned form values must be freed using the @code cupsFreeOptions@ // function. // // > Note: Because the form data is read from the client connection, this // > function can only be called once per request. // int // O - Number of form variables read papplClientGetForm( pappl_client_t *client, // I - Client cups_option_t **form) // O - Form variables { const char *content_type; // Content-Type header const char *boundary; // boundary value for multi-part char *body, // Message body *bodyptr, // Pointer into message body *bodyend; // End of message body size_t body_alloc, // Allocated message body size body_size = 0; // Size of message body ssize_t bytes; // Bytes read int num_form = 0; // Number of form variables http_state_t initial_state; // Initial HTTP state if (!client || !form) { if (form) *form = NULL; return (0); } content_type = httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE); if (client->operation == HTTP_STATE_GET) { // Copy form data from the request URI... if (!client->options) { *form = NULL; return (0); } if ((body = strdup(client->options)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for form data."); *form = NULL; return (0); } body_size = strlen(body); content_type = "application/x-www-form-urlencoded"; } else { // Read up to 2MB of data from the client... *form = NULL; initial_state = httpGetState(client->http); body_alloc = 65536; if ((body = malloc(body_alloc)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for form data."); *form = NULL; return (0); } for (bodyptr = body, bodyend = body + body_alloc; (bytes = httpRead2(client->http, bodyptr, (size_t)(bodyend - bodyptr))) > 0; bodyptr += bytes) { body_size += (size_t)bytes; if (body_size >= body_alloc) { char *temp; // Temporary pointer if (body_alloc >= (2 * 1024 * 1024)) break; body_alloc += 65536; if ((temp = realloc(body, body_alloc)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for form data."); free(body); *form = NULL; return (0); } bodyptr = temp + (bodyptr - body); bodyend = temp + body_alloc; body = temp; } } papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Read %ld bytes of form data (%s).", (long)body_size, content_type); // Flush remaining data... if (httpGetState(client->http) == initial_state) httpFlush(client->http); } // Parse the data in memory... bodyend = body + body_size; if (!strcmp(content_type, "application/x-www-form-urlencoded")) { // Read URL-encoded form data... char name[64], // Variable name *nameptr, // Pointer into name value[1024], // Variable value *valptr; // Pointer into value for (bodyptr = body; bodyptr < bodyend;) { // Get the name... nameptr = name; while (bodyptr < bodyend && *bodyptr != '=') { int ch = *bodyptr++; // Name character if (ch == '%' && isxdigit(bodyptr[0] & 255) && isxdigit(bodyptr[1] & 255)) { // Hex-encoded character if (isdigit(*bodyptr)) ch = (*bodyptr++ - '0') << 4; else ch = (tolower(*bodyptr++) - 'a' + 10) << 4; if (isdigit(*bodyptr)) ch |= *bodyptr++ - '0'; else ch |= tolower(*bodyptr++) - 'a' + 10; } else if (ch == '+') ch = ' '; if (nameptr < (name + sizeof(name) - 1)) *nameptr++ = (char)ch; } *nameptr = '\0'; if (bodyptr >= bodyend) break; // Get the value... bodyptr ++; valptr = value; while (bodyptr < bodyend && *bodyptr != '&') { int ch = *bodyptr++; // Name character if (ch == '%' && isxdigit(bodyptr[0] & 255) && isxdigit(bodyptr[1] & 255)) { // Hex-encoded character if (isdigit(*bodyptr)) ch = (*bodyptr++ - '0') << 4; else ch = (tolower(*bodyptr++) - 'a' + 10) << 4; if (isdigit(*bodyptr)) ch |= *bodyptr++ - '0'; else ch |= tolower(*bodyptr++) - 'a' + 10; } else if (ch == '+') ch = ' '; if (valptr < (value + sizeof(value) - 1)) *valptr++ = (char)ch; } *valptr = '\0'; if (bodyptr < bodyend) bodyptr ++; // Add the name + value to the option array... num_form = cupsAddOption(name, value, num_form, form); } } else if (!strncmp(content_type, "multipart/form-data; ", 21) && (boundary = strstr(content_type, "boundary=")) != NULL) { // Read multi-part form data... char name[1024], // Form variable name filename[1024], // Form filename bstring[256], // Boundary string to look for *bend, // End of value (boundary) *line, // Start of line *ptr; // Pointer into name/filename size_t blen; // Length of boundary string // Format the boundary string we are looking for... snprintf(bstring, sizeof(bstring), "\r\n--%s", boundary + 9); blen = strlen(bstring); // Parse lines in the message body... name[0] = '\0'; filename[0] = '\0'; for (bodyptr = body; bodyptr < bodyend;) { // Split out a line... for (line = bodyptr; bodyptr < bodyend; bodyptr ++) { if (!memcmp(bodyptr, "\r\n", 2)) { *bodyptr = '\0'; bodyptr += 2; break; } } if (bodyptr >= bodyend) break; papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Line '%s'.", line); if (!*line) { // End of headers, grab value... if (!name[0]) { // No name value... papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Invalid multipart form data."); break; } for (bend = bodyend - blen, ptr = memchr(bodyptr, '\r', (size_t)(bend - bodyptr)); ptr; ptr = memchr(ptr + 1, '\r', (size_t)(bend - ptr - 1))) { // Check for boundary string... if (!memcmp(ptr, bstring, blen)) break; } if (!ptr) { // No boundary string, invalid data... papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Invalid multipart form data."); break; } // Point to the start of the boundary string... bend = ptr; ptr = bodyptr; bodyptr = bend + blen; if (filename[0]) { // Save an embedded file... const char *tempfile; // Temporary file if ((tempfile = _papplClientCreateTempFile(client, ptr, (size_t)(bend - ptr))) == NULL) break; num_form = cupsAddOption(name, tempfile, num_form, form); } else { // Save the form variable... *bend = '\0'; num_form = cupsAddOption(name, ptr, num_form, form); } name[0] = '\0'; filename[0] = '\0'; if (bodyptr < (bodyend - 1) && bodyptr[0] == '\r' && bodyptr[1] == '\n') bodyptr += 2; } else if (!strncasecmp(line, "Content-Disposition:", 20)) { if ((ptr = strstr(line + 20, " name=\"")) != NULL) { strlcpy(name, ptr + 7, sizeof(name)); if ((ptr = strchr(name, '\"')) != NULL) *ptr = '\0'; } if ((ptr = strstr(line + 20, " filename=\"")) != NULL) { strlcpy(filename, ptr + 11, sizeof(filename)); if ((ptr = strchr(filename, '\"')) != NULL) *ptr = '\0'; } } } } free(body); // Return whatever we got... return (num_form); } // // 'papplClientHTMLAuthorize()' - Handle authorization for the web interface. // // The web interface supports both authentication against user accounts and // authentication using a single administrative access password. This function // handles the details of authentication for the web interface based on the // system authentication service configuration (the "auth_service" argument to // @link papplSystemCreate@). // // > Note: IPP operation callbacks needing to perform authorization should use // > the @link papplClientIsAuthorized@ function instead. // bool // O - `true` if authorized, `false` otherwise papplClientHTMLAuthorize( pappl_client_t *client) // I - Client { char auth_cookie[65], // Authorization cookie session_key[65], // Current session key password_hash[100], // Password hash auth_text[256]; // Authorization string unsigned char auth_hash[32]; // Authorization hash const char *status = NULL; // Status message, if any // Don't authorize if we have no auth service or we don't have a password set. if (!client || (!client->system->auth_service && !client->system->password_hash[0])) return (true); // When using an auth service, use HTTP Basic authentication... if (client->system->auth_service) { http_status_t code = papplClientIsAuthorized(client); if (code != HTTP_STATUS_CONTINUE) { papplClientRespond(client, code, NULL, NULL, 0, 0); return (false); } else return (true); } // Otherwise look for the authorization cookie... if (papplClientGetCookie(client, "auth", auth_cookie, sizeof(auth_cookie))) { snprintf(auth_text, sizeof(auth_text), "%s:%s", papplSystemGetSessionKey(client->system, session_key, sizeof(session_key)), papplSystemGetPassword(client->system, password_hash, sizeof(password_hash))); cupsHashData("sha2-256", (unsigned char *)auth_text, strlen(auth_text), auth_hash, sizeof(auth_hash)); cupsHashString(auth_hash, sizeof(auth_hash), auth_text, sizeof(auth_text)); if (!strcmp(auth_cookie, auth_text)) { // Hashes match so we are authorized. Use "web-admin" as the username. strlcpy(client->username, "web-admin", sizeof(client->username)); return (true); } } // No cookie, so see if this is a form submission... if (client->operation == HTTP_STATE_POST) { // Yes, grab the login information and try to authorize... int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables const char *password; // Password from user if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else if ((password = cupsGetOption("password", num_form, form)) == NULL) { status = "Login password required."; } else { // Hash the user-supplied password with the salt from the stored password papplSystemGetPassword(client->system, password_hash, sizeof(password_hash)); papplSystemHashPassword(client->system, password_hash, password, auth_text, sizeof(auth_text)); if (!strncmp(password_hash, auth_text, strlen(password_hash))) { // Password hashes match, generate the cookie from the session key and // password hash... snprintf(auth_text, sizeof(auth_text), "%s:%s", papplSystemGetSessionKey(client->system, session_key, sizeof(session_key)), password_hash); cupsHashData("sha2-256", (unsigned char *)auth_text, strlen(auth_text), auth_hash, sizeof(auth_hash)); cupsHashString(auth_hash, sizeof(auth_hash), auth_text, sizeof(auth_text)); papplClientSetCookie(client, "auth", auth_text, 3600); } else { status = "Password incorrect."; } } cupsFreeOptions(num_form, form); // Make the caller think this is a GET request... client->operation = HTTP_STATE_GET; if (!status) { // Hashes match so we are authorized. Use "web-admin" as the username. strlcpy(client->username, "web-admin", sizeof(client->username)); return (true); } } // If we get this far, show the standard login form... papplClientRespond(client, HTTP_STATUS_OK, NULL, "text/html", 0, 0); papplClientHTMLHeader(client, "Login", 0); papplClientHTMLPuts(client, "
\n" "
\n" "
\n" "

Login

\n"); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, "

\n" " \n" "
\n" "
\n"); papplClientHTMLFooter(client); return (false); } // // 'papplClientHTMLEscape()' - Send a string to a web browser client. // // This function sends the specified string to the web browser client and // escapes special characters as HTML entities as needed, for example "&" is // sent as `&`. // void papplClientHTMLEscape( pappl_client_t *client, // I - Client const char *s, // I - String to write size_t slen) // I - Number of characters to write (`0` for nul-terminated) { const char *start, // Start of segment *end; // End of string start = s; end = s + (slen > 0 ? slen : strlen(s)); while (*s && s < end) { if (*s == '&' || *s == '<' || *s == '\"') { if (s > start) httpWrite2(client->http, start, (size_t)(s - start)); if (*s == '&') httpWrite2(client->http, "&", 5); else if (*s == '<') httpWrite2(client->http, "<", 4); else httpWrite2(client->http, """, 6); start = s + 1; } s ++; } if (s > start) httpWrite2(client->http, start, (size_t)(s - start)); } // // 'papplClientHTMLFooter()' - Show the web interface footer. // // This function sends the standard web interface footer followed by a // trailing 0-length chunk to finish the current HTTP response. Use the // @link papplSystemSetFooterHTML@ function to add any custom HTML needed in // the footer. // void papplClientHTMLFooter( pappl_client_t *client) // I - Client { const char *footer = papplSystemGetFooterHTML(papplClientGetSystem(client)); // Footer HTML if (footer) { papplClientHTMLPuts(client, "
\n" "
\n" "
"); papplClientHTMLPuts(client, footer); papplClientHTMLPuts(client, "
\n" "
\n" "
\n"); } papplClientHTMLPuts(client, " \n" "\n"); httpWrite2(client->http, "", 0); } // // 'papplClientHTMLHeader()' - Show the web interface header and title. // // This function sends the standard web interface header and title. If the // "refresh" argument is greater than zero, the page will automatically reload // after that many seconds. // // Use the @link papplSystemAddLink@ function to add system-wide navigation // links to the header. Similarly, use @link papplPrinterAddLink@ to add // printer-specific links, which will appear in the web interface printer if // the system is not configured to support multiple printers // (the `PAPPL_SOPTIONS_MULTI_QUEUE` option to @link papplSystemCreate@). // void papplClientHTMLHeader( pappl_client_t *client, // I - Client const char *title, // I - Title int refresh) // I - Refresh time in seconds (`0` for no refresh) { pappl_system_t *system = client->system; // System pappl_printer_t *printer; // Printer const char *name; // Name for title/header pthread_rwlock_rdlock(&system->rwlock); printer = (pappl_printer_t *)cupsArrayFirst(system->printers); pthread_rwlock_unlock(&system->rwlock); if ((system->options & PAPPL_SOPTIONS_MULTI_QUEUE) || !printer) name = system->name; else name = printer->name; papplClientHTMLPrintf(client, "\n" "\n" " \n" " %s%s%s\n" " \n" " \n" " \n", title ? title : "", title ? " - " : "", name); if (refresh > 0) papplClientHTMLPrintf(client, "\n", refresh); papplClientHTMLPuts(client, " \n" " \n" " \n" "
\n" "
\n" "
\n" " \n"); pthread_rwlock_rdlock(&system->rwlock); _papplClientHTMLPutLinks(client, system->links, PAPPL_LOPTIONS_NAVIGATION); pthread_rwlock_unlock(&system->rwlock); if (!(system->options & PAPPL_SOPTIONS_MULTI_QUEUE) && printer) { if (cupsArrayCount(system->links) > 0) papplClientHTMLPuts(client, " \n"); pthread_rwlock_rdlock(&printer->rwlock); _papplClientHTMLPutLinks(client, printer->links, PAPPL_LOPTIONS_NAVIGATION); pthread_rwlock_unlock(&printer->rwlock); } papplClientHTMLPuts(client, "
\n" "
\n" "
\n"); } // // '_papplCLientHTMLInfo()' - Show system/printer information. // void _papplClientHTMLInfo( pappl_client_t *client, // I - Client bool is_form, // I - `true` to show a form, `false` otherwise const char *dns_sd_name, // I - DNS-SD name, if any const char *location, // I - Location, if any const char *geo_location, // I - Geo-location, if any const char *organization, // I - Organization, if any const char *org_unit, // I - Organizational unit, if any pappl_contact_t *contact) // I - Contact, if any { double lat = 0.0, lon = 0.0; // Latitude and longitude in degrees if (geo_location) sscanf(geo_location, "geo:%lf,%lf", &lat, &lon); if (is_form) papplClientHTMLStartForm(client, client->uri, false); // DNS-SD name... papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "
Name:"); if (is_form) papplClientHTMLPrintf(client, "", dns_sd_name ? dns_sd_name : ""); else papplClientHTMLEscape(client, dns_sd_name ? dns_sd_name : "Not set", 0); // Location and geo-location... papplClientHTMLPuts(client, "
Location:"); if (is_form) { papplClientHTMLPrintf(client, "
\n" " ° latitude x  ° longitude ", location ? location : "", lat, lon); } else { papplClientHTMLPrintf(client, "%s", location ? location : "Not set"); if (geo_location) papplClientHTMLPrintf(client, "
\n%g° %c latitude x %g° %c longitude", fabs(lat), lat < 0.0 ? 'S' : 'N', fabs(lon), lon < 0.0 ? 'W' : 'E'); } // Show an embedded map of the location... if (geo_location || is_form) papplClientHTMLPrintf(client, "
\n" "", lon - 0.00025, lat - 0.00025, lon + 0.00025, lat + 0.00025, lat, lon); // Organization papplClientHTMLPuts(client, "
Organization:"); if (is_form) papplClientHTMLPrintf(client, "
\n" "", organization ? organization : "", org_unit ? org_unit : ""); else papplClientHTMLPrintf(client, "%s%s%s", organization ? organization : "Not set", org_unit ? ", " : "", org_unit ? org_unit : ""); // Contact papplClientHTMLPuts(client, "
Contact:"); if (is_form) { papplClientHTMLPrintf(client, "
\n" "
\n" "
", contact->name, contact->email, contact->telephone); } else if (contact->email[0]) { papplClientHTMLPrintf(client, "%s", contact->email, contact->name[0] ? contact->name : contact->email); if (contact->telephone[0]) papplClientHTMLPrintf(client, "
%s", contact->telephone, contact->telephone); } else if (contact->name[0]) { papplClientHTMLEscape(client, contact->name, 0); if (contact->telephone[0]) papplClientHTMLPrintf(client, "
%s", contact->telephone, contact->telephone); } else if (contact->telephone[0]) { papplClientHTMLPrintf(client, "%s", contact->telephone, contact->telephone); } else papplClientHTMLPuts(client, "Not set"); papplClientHTMLPuts(client, "
\n"); if (is_form) { // The following Javascript updates the map and lat/lon fields. // // Note: I should probably use the Openstreetmap Javascript API so that // the marker position gets updated. Right now I'm setting the marker // value in the URL but the OSM simple embedding URL doesn't update the // marker position after the page is loaded... papplClientHTMLPuts(client, " \n" " \n"); } } // // 'papplClientHTMLPrinterFooter()' - Show the web interface footer for printers. // // This function sends the standard web interface footer for a printer followed // by a trailing 0-length chunk to finish the current HTTP response. Use the // @link papplSystemSetFooterHTML@ function to add any custom HTML needed in // the footer. // void papplClientHTMLPrinterFooter(pappl_client_t *client) // I - Client { papplClientHTMLPuts(client, "
\n" " \n" " \n"); papplClientHTMLFooter(client); } // // 'papplClientHTMLPrinterHeader()' - Show the web interface header and title // for printers. // // This function sends the standard web interface header and title for a // printer. If the "refresh" argument is greater than zero, the page will // automatically reload after that many seconds. // // If "label" and "path_or_url" are non-`NULL` strings, an additional navigation // link is included with the title header - this is typically used for an // action button ("Change"). // // Use the @link papplSystemAddLink@ function to add system-wide navigation // links to the header. Similarly, use @link papplPrinterAddLink@ to add // printer-specific links, which will appear in the web interface printer if // the system is not configured to support multiple printers // (the `PAPPL_SOPTIONS_MULTI_QUEUE` option to @link papplSystemCreate@). // void papplClientHTMLPrinterHeader( pappl_client_t *client, // I - Client pappl_printer_t *printer, // I - Printer const char *title, // I - Title int refresh, // I - Refresh time in seconds or 0 for none const char *label, // I - Button label or `NULL` for none const char *path_or_url) // I - Button path or `NULL` for none { if (!papplClientRespond(client, HTTP_STATUS_OK, NULL, "text/html", 0, 0)) return; if (printer->system->options & PAPPL_SOPTIONS_MULTI_QUEUE) { // Multi-queue mode, need to add the printer name to the title... if (title) { char full_title[1024]; // Full title snprintf(full_title, sizeof(full_title), "%s - %s", title, printer->name); papplClientHTMLHeader(client, full_title, refresh); } else { papplClientHTMLHeader(client, printer->name, refresh); } } else { // Single queue mode - the function will automatically add the printer name... papplClientHTMLHeader(client, title, refresh); } if (printer->system->options & PAPPL_SOPTIONS_MULTI_QUEUE) { pthread_rwlock_rdlock(&printer->rwlock); papplClientHTMLPrintf(client, "
\n" "
\n" "
%s:\n", printer->uriname, printer->name); _papplClientHTMLPutLinks(client, printer->links, PAPPL_LOPTIONS_NAVIGATION); papplClientHTMLPuts(client, "
\n" "
\n" "
\n"); pthread_rwlock_unlock(&printer->rwlock); } else if (client->system->versions[0].sversion[0]) papplClientHTMLPrintf(client, "
\n" "
\n" "
\n" " Version %s\n" "
\n" "
\n" "
\n", client->system->versions[0].sversion); papplClientHTMLPuts(client, "
\n"); if (title) { papplClientHTMLPrintf(client, "
\n" "
\n" "

%s", title); if (label && path_or_url) papplClientHTMLPrintf(client, " %s", path_or_url, label); papplClientHTMLPuts(client, "

\n"); } } // // 'papplClientHTMLPrintf()' - Send formatted text to the web browser client, // escaping as needed. // // This function sends formatted text to the web browser client using // `printf`-style formatting codes. The format string itself is not escaped // to allow for embedded HTML, however strings inserted using the '%c' or `%s` // codes are escaped properly for HTML - "&" is sent as `&`, etc. // void papplClientHTMLPrintf( pappl_client_t *client, // I - Client const char *format, // I - Printf-style format string ...) // I - Additional arguments as needed { va_list ap; // Pointer to arguments const char *start; // Start of string char size, // Size character (h, l, L) type; // Format type character int width, // Width of field prec; // Number of characters of precision char tformat[100], // Temporary format string for snprintf() *tptr, // Pointer into temporary format temp[1024]; // Buffer for formatted numbers const char *s; // Pointer to string // Loop through the format string, formatting as needed... va_start(ap, format); start = format; while (*format) { if (*format == '%') { if (format > start) httpWrite2(client->http, start, (size_t)(format - start)); tptr = tformat; *tptr++ = *format++; if (*format == '%') { httpWrite2(client->http, "%", 1); format ++; start = format; continue; } else if (strchr(" -+#\'", *format)) *tptr++ = *format++; if (*format == '*') { // Get width from argument... format ++; width = va_arg(ap, int); snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width); tptr += strlen(tptr); } else { width = 0; while (isdigit(*format & 255)) { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; width = width * 10 + *format++ - '0'; } } if (*format == '.') { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; format ++; if (*format == '*') { // Get precision from argument... format ++; prec = va_arg(ap, int); snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec); tptr += strlen(tptr); } else { prec = 0; while (isdigit(*format & 255)) { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; prec = prec * 10 + *format++ - '0'; } } } if (*format == 'l' && format[1] == 'l') { size = 'L'; if (tptr < (tformat + sizeof(tformat) - 2)) { *tptr++ = 'l'; *tptr++ = 'l'; } format += 2; } else if (*format == 'h' || *format == 'l' || *format == 'L') { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; size = *format++; } else size = 0; if (!*format) { start = format; break; } if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *format; type = *format++; *tptr = '\0'; start = format; switch (type) { case 'E' : // Floating point formats case 'G' : case 'e' : case 'f' : case 'g' : if ((size_t)(width + 2) > sizeof(temp)) break; snprintf(temp, sizeof(temp), tformat, va_arg(ap, double)); httpWrite2(client->http, temp, strlen(temp)); break; case 'B' : // Integer formats case 'X' : case 'b' : case 'd' : case 'i' : case 'o' : case 'u' : case 'x' : if ((size_t)(width + 2) > sizeof(temp)) break; # ifdef HAVE_LONG_LONG if (size == 'L') snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long)); else # endif // HAVE_LONG_LONG if (size == 'l') snprintf(temp, sizeof(temp), tformat, va_arg(ap, long)); else snprintf(temp, sizeof(temp), tformat, va_arg(ap, int)); httpWrite2(client->http, temp, strlen(temp)); break; case 'p' : // Pointer value if ((size_t)(width + 2) > sizeof(temp)) break; snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *)); httpWrite2(client->http, temp, strlen(temp)); break; case 'c' : // Character or character array if (width <= 1) { temp[0] = (char)va_arg(ap, int); temp[1] = '\0'; papplClientHTMLEscape(client, temp, 1); } else papplClientHTMLEscape(client, va_arg(ap, char *), (size_t)width); break; case 's' : // String if ((s = va_arg(ap, const char *)) == NULL) s = "(null)"; papplClientHTMLEscape(client, s, strlen(s)); break; } } else format ++; } if (format > start) httpWrite2(client->http, start, (size_t)(format - start)); va_end(ap); } // // '_papplClientHTMLPutLinks()' - Print an array of links. // void _papplClientHTMLPutLinks( pappl_client_t *client, // I - Client cups_array_t *links, // I - Array of links pappl_loptions_t which) // I - Which links to show { int i, // Looping var count; // Number of links _pappl_link_t *l; // Current link // Loop through the links. // // Note: We use a loop and not cupsArrayFirst/Last because other threads may // be enumerating the same array of links. for (i = 0, count = cupsArrayCount(links); i < count; i ++) { l = (_pappl_link_t *)cupsArrayIndex(links, i); if (!l || !(l->options & which)) continue; if (strcmp(client->uri, l->path_or_url)) { if (l->path_or_url[0] != '/' || !(l->options & PAPPL_LOPTIONS_HTTPS_REQUIRED) || (!client->system->auth_service && !client->system->password_hash[0])) papplClientHTMLPrintf(client, " %s\n", l->path_or_url, l->label); else papplClientHTMLPrintf(client, " %s\n", client->host_field, client->host_port, l->path_or_url, l->label); } else papplClientHTMLPrintf(client, " %s\n", l->label); } } // // 'papplClientHTMLPuts()' - Send a HTML string to the web browser client. // // This function sends a HTML string to the client without performing any // escaping of special characters. // void papplClientHTMLPuts( pappl_client_t *client, // I - Client const char *s) // I - String { if (client && s && *s) httpWrite2(client->http, s, strlen(s)); } // // 'papplClientHTMLStartForm()' - Start a HTML form. // // This function starts a HTML form with the specified "action" path and // includes the CSRF token as a hidden variable. If the "multipart" argument // is `true`, the form is annotated to support file attachments up to 2MiB in // size. // void papplClientHTMLStartForm( pappl_client_t *client, // I - Client const char *action, // I - Form action URL bool multipart) // I - `true` if the form allows file uploads, `false` otherwise { char token[256]; // CSRF token if (multipart) { // When allowing file attachments, the maximum size is 2MB... papplClientHTMLPrintf(client, "
\n" " \n" " \n", action, papplClientGetCSRFToken(client, token, sizeof(token))); } else { papplClientHTMLPrintf(client, " \n" " \n", action, papplClientGetCSRFToken(client, token, sizeof(token))); } } // // 'papplClientIsValidForm()' - Validate HTML form variables. // // This function validates the contents of a HTML form using the CSRF token // included as a hidden variable. When sending a HTML form you should use the // @link papplClientStartForm@ function to start the HTML form and insert the // CSRF token for later validation. // // > Note: Callers are expected to validate all other form variables. // bool // O - `true` if the CSRF token is valid, `false` otherwise papplClientIsValidForm( pappl_client_t *client, // I - Client int num_form, // I - Number of form variables cups_option_t *form) // I - Form variables { char token[256]; // Expected CSRF token const char *session; // Form variable if ((session = cupsGetOption("session", num_form, form)) == NULL) return (false); return (!strcmp(session, papplClientGetCSRFToken(client, token, sizeof(token)))); } // // 'papplClientSetCookie()' - Set a cookie for the web browser client. // // This function sets the value of a cookie for the client by updating the // `Set-Cookie` header in the HTTP response that will be sent. The "name" and // "value" strings must contain only valid characters for a cookie and its // value as documented in RFC 6265, which basically means letters, numbers, "@", // "-", ".", and "_". // // The "expires" argument specifies how long the cookie will remain active in // seconds, for example `3600` seconds is one hour and `86400` seconds is one // day. If the value is zero or less, a "session" cookie is created instead // which will expire as soon as the web browser is closed. // void papplClientSetCookie( pappl_client_t *client, // I - Client const char *name, // I - Cookie name const char *value, // I - Cookie value int expires) // I - Expiration in seconds from now, `0` for a session cookie { const char *client_cookie = httpGetCookie(client->http); // Current cookie char buffer[1024], // New cookie buffer cookie[256], // New authorization cookie expireTime[64]; // Expiration date/time if (!name) return; if (expires > 0) snprintf(cookie, sizeof(cookie), "%s=%s; path=/; expires=%s; httponly; secure;", name, value, httpGetDateString2(time(NULL) + expires, expireTime, sizeof(expireTime))); else snprintf(cookie, sizeof(cookie), "%s=%s; path=/; httponly; secure;", name, value); if (!client_cookie || !*client_cookie) { // No other cookies set... httpSetCookie(client->http, cookie); } else { // Append the new cookie with a Set-Cookie: header since libcups only // directly supports setting a single Set-Cookie header... snprintf(buffer, sizeof(buffer), "%s\r\nSet-Cookie: %s", client_cookie, cookie); httpSetCookie(client->http, buffer); } } pappl-1.0.3/pappl/client.c000066400000000000000000000526751403603036100154000ustar00rootroot00000000000000// // Client processing code for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local functions... // static bool eval_if_modified(pappl_client_t *client, _pappl_resource_t *r); // // '_papplClientCleanTempFiles()' - Clean temporary files... // void _papplClientCleanTempFiles( pappl_client_t *client) // I - Client { int i; // Looping var for (i = 0; i < client->num_files; i ++) { unlink(client->files[i]); free(client->files[i]); } client->num_files = 0; } // // '_papplClientCreate()' - Accept a new network connection and create a client // object. // // The new network connection is accepted from the specified listen socket. // The client object is managed by the system and is automatically freed when // the connection is closed. // // > Note: This function is normally only called from @link papplSystemRun@. // pappl_client_t * // O - Client _papplClientCreate( pappl_system_t *system, // I - Printer int sock) // I - Listen socket { pappl_client_t *client; // Client if ((client = calloc(1, sizeof(pappl_client_t))) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for client connection: %s", strerror(errno)); return (NULL); } client->system = system; pthread_rwlock_wrlock(&system->rwlock); client->number = system->next_client ++; pthread_rwlock_unlock(&system->rwlock); // Accept the client and get the remote address... if ((client->http = httpAcceptConnection(sock, 1)) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to accept client connection: %s", strerror(errno)); free(client); return (NULL); } httpGetHostname(client->http, client->hostname, sizeof(client->hostname)); papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Accepted connection from '%s'.", client->hostname); return (client); } // // '_papplClientCreateTempFile()' - Create a temporary file. // char * // O - Temporary filename or `NULL` on error _papplClientCreateTempFile( pappl_client_t *client, // I - Client const void *data, // I - Data size_t datasize) // I - Size of data { int fd; // File descriptor char tempfile[1024]; // Temporary filename // See if we have room for another temp file... if (client->num_files >= (int)(sizeof(client->files) / sizeof(client->files[0]))) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Too many temporary files."); return (NULL); } // Write the data to a temporary file... if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create temporary file: %s", strerror(errno)); return (NULL); } if (write(fd, data, datasize) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to write to temporary file: %s", strerror(errno)); close(fd); unlink(tempfile); return (NULL); } close(fd); client->files[client->num_files ++] = strdup(tempfile); return (client->files[client->num_files - 1]); } // // '_papplClientDelete()' - Close the client connection and free all memory used // by a client object. // // > Note: This function is normally only called by // void _papplClientDelete( pappl_client_t *client) // I - Client { papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Closing connection from '%s'.", client->hostname); // Flush pending writes before closing... httpFlushWrite(client->http); _papplClientCleanTempFiles(client); // Free memory... httpClose(client->http); ippDelete(client->request); ippDelete(client->response); free(client); } // // '_papplClientProcessHTTP()' - Process a HTTP request. // bool // O - `true` on success, `false` on failure _papplClientProcessHTTP( pappl_client_t *client) // I - Client connection { char uri[1024]; // URI http_state_t http_state; // HTTP state http_status_t http_status; // HTTP status http_version_t http_version; // HTTP version ipp_state_t ipp_state; // State of IPP transfer char scheme[32], // Method/scheme userpass[128], // Username:password hostname[HTTP_MAX_HOST]; // Hostname int port; // Port number char *ptr; // Pointer into string _pappl_resource_t *resource; // Current resource static const char * const http_states[] = { // Strings for logging HTTP method "WAITING", "OPTIONS", "GET", "GET_SEND", "HEAD", "POST", "POST_RECV", "POST_SEND", "PUT", "PUT_RECV", "DELETE", "TRACE", "CONNECT", "STATUS", "UNKNOWN_METHOD", "UNKNOWN_VERSION" }; // Clear state variables... ippDelete(client->request); ippDelete(client->response); client->request = NULL; client->response = NULL; client->operation = HTTP_STATE_WAITING; // Read a request from the connection... while ((http_state = httpReadRequest(client->http, uri, sizeof(uri))) == HTTP_STATE_WAITING) usleep(1); // Parse the request line... if (http_state == HTTP_STATE_ERROR) { if (httpError(client->http) != EPIPE && httpError(client->http)) papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Bad request line (%s).", strerror(httpError(client->http))); return (false); } else if (http_state == HTTP_STATE_UNKNOWN_METHOD) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Bad/unknown operation."); papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); return (false); } else if (http_state == HTTP_STATE_UNKNOWN_VERSION) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Bad HTTP version."); papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); return (false); } // Separate the URI into its components... if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK && (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*"))) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Bad URI '%s'.", uri); papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); return (false); } if ((client->options = strchr(client->uri, '?')) != NULL) *(client->options)++ = '\0'; // Process the request... client->start = time(NULL); client->operation = httpGetState(client->http); // Parse incoming parameters until the status changes... while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE) ; // Read all HTTP headers... if (http_status != HTTP_STATUS_OK) { papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); return (false); } http_version = httpGetVersion(client->http); papplLogClient(client, PAPPL_LOGLEVEL_INFO, "%s %s://%s%s HTTP/%d.%d", http_states[http_state], httpIsEncrypted(client->http) ? "https" : "http", httpGetField(client->http, HTTP_FIELD_HOST), uri, http_version / 100, http_version % 100); // Validate the host header... if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] && httpGetVersion(client->http) >= HTTP_VERSION_1_1) { // HTTP/1.1 and higher require the "Host:" field... papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); return (false); } strlcpy(client->host_field, httpGetField(client->http, HTTP_FIELD_HOST), sizeof(client->host_field)); if ((ptr = strrchr(client->host_field, ':')) != NULL) { char *end; // End of port number // Grab port number from Host: header... *ptr++ = '\0'; client->host_port = (int)strtol(ptr, &end, 10); if (errno == ERANGE || *end) client->host_port = client->system->port; } else { // Use the default port number... client->host_port = client->system->port; } ptr = strstr(client->host_field, ".local"); if (!isdigit(client->host_field[0] & 255) && client->host_field[0] != '[' && strcmp(client->host_field, client->system->hostname) && strcmp(client->host_field, "localhost") && (!ptr || (strcmp(ptr, ".local") && strcmp(ptr, ".local.")))) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Bad Host: header '%s'.", client->host_field); papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); return (false); } // Handle HTTP Upgrade... if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), "Upgrade")) { if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http)) { if (!papplClientRespond(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0, 0)) return (false); papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Upgrading to encrypted connection."); if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED)) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to encrypt connection: %s", cupsLastErrorString()); return (false); } papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Connection now encrypted."); } else if (!papplClientRespond(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0, 0)) return (false); } // Handle HTTP Expect... if (httpGetExpect(client->http) && (client->operation == HTTP_STATE_POST || client->operation == HTTP_STATE_PUT)) { if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE) { // Send 100-continue header... if (!papplClientRespond(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0, 0)) return (false); } else { // Send 417-expectation-failed header... if (!papplClientRespond(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0, 0)) return (false); } } // Handle new transfers... switch (client->operation) { case HTTP_STATE_OPTIONS : // Do OPTIONS command... return (papplClientRespond(client, HTTP_STATUS_OK, NULL, NULL, 0, 0)); case HTTP_STATE_HEAD : // See if we have a matching resource to serve... if ((resource = _papplSystemFindResource(client->system, client->uri)) != NULL) { if (eval_if_modified(client, resource)) return (papplClientRespond(client, HTTP_STATUS_OK, NULL, resource->format, resource->last_modified, 0)); else return (papplClientRespond(client, HTTP_STATUS_NOT_MODIFIED, NULL, NULL, resource->last_modified, 0)); } // If we get here the resource wasn't found... return (papplClientRespond(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0, 0)); case HTTP_STATE_GET : // See if we have a matching resource to serve... if ((resource = _papplSystemFindResource(client->system, client->uri)) != NULL) { if (!eval_if_modified(client, resource)) { return (papplClientRespond(client, HTTP_STATUS_NOT_MODIFIED, NULL, NULL, resource->last_modified, 0)); } else if (resource->cb) { // Send output of a callback... return ((resource->cb)(client, resource->cbdata)); } else if (resource->filename) { // Send an external file... int fd; // Resource file descriptor char buffer[8192]; // Copy buffer ssize_t bytes; // Bytes read/written if ((fd = open(resource->filename, O_RDONLY)) >= 0) { if (!papplClientRespond(client, HTTP_STATUS_OK, NULL, resource->format, resource->last_modified, 0)) { close(fd); return (false); } while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) httpWrite2(client->http, buffer, (size_t)bytes); httpWrite2(client->http, "", 0); close(fd); return (true); } } else { // Send a static resource file... if (!papplClientRespond(client, HTTP_STATUS_OK, NULL, resource->format, resource->last_modified, resource->length)) return (false); httpWrite2(client->http, (const char *)resource->data, resource->length); httpFlushWrite(client->http); return (true); } } // If we get here then the resource wasn't found... return (papplClientRespond(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0, 0)); case HTTP_STATE_POST : if (!strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE), "application/ipp")) { // Read the IPP request... client->request = ippNew(); while ((ipp_state = ippRead(client->http, client->request)) != IPP_STATE_DATA) { if (ipp_state == IPP_STATE_ERROR) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "IPP read error (%s).", cupsLastErrorString()); papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); return (false); } } // Now that we have the IPP request, process the request... return (_papplClientProcessIPP(client)); } else if ((resource = _papplSystemFindResource(client->system, client->uri)) != NULL) { // Serve a matching resource... if (resource->cb) { // Handle a post request through the callback... return ((resource->cb)(client, resource->cbdata)); } else { // Otherwise you can't POST to a resource... return (papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0)); } } else { // Not an IPP request or form, return an error... return (papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0)); } default : break; // Anti-compiler-warning-code } return (true); } // // 'papplClientRespond()' - Send a regular HTTP response. // // This function sends all of the required HTTP fields and includes standard // messages for errors. The following values for "code" are explicitly // supported: // // - `HTTP_STATUS_OK`: The request is successful. // - `HTTP_STATUS_BAD_REQUEST`: The client submitted a bad request. // - `HTTP_STATUS_CONTINUE`: An authentication challenge is not needed. // - `HTTP_STATUS_FORBIDDEN`: Authenticated but not allowed. // - `HTTP_STATUS_METHOD_NOT_ALLOWED`: The HTTP method is not supported for the // given URI. // - `HTTP_STATUS_UNAUTHORIZED`: Not authenticated. // - `HTTP_STATUS_UPGRADE_REQUIRED`: Redirects the client to a secure page. // // Use the @link papplClientRespondRedirect@ when you need to redirect the // client to another page. // bool // O - `true` on success, `false` on failure papplClientRespond( pappl_client_t *client, // I - Client http_status_t code, // I - HTTP status of response const char *content_encoding, // I - Content-Encoding of response const char *type, // I - MIME media type of response time_t last_modified, // I - Last-Modified date/time or `0` for none size_t length) // I - Length of response or `0` for variable-length { char message[1024]; // Text message if (type) papplLogClient(client, PAPPL_LOGLEVEL_INFO, "%s %s %d", httpStatus(code), type, (int)length); else papplLogClient(client, PAPPL_LOGLEVEL_INFO, "%s", httpStatus(code)); if (code == HTTP_STATUS_CONTINUE) { // 100-continue doesn't send any headers... return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0); } // Format an error message... if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS) { snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code)); type = "text/plain"; length = strlen(message); } else message[0] = '\0'; // Send the HTTP response header... httpClearFields(client->http); httpSetField(client->http, HTTP_FIELD_SERVER, papplSystemGetServerHeader(client->system)); if (last_modified) httpSetField(client->http, HTTP_FIELD_LAST_MODIFIED, httpGetDateString(last_modified)); if (code == HTTP_STATUS_METHOD_NOT_ALLOWED || client->operation == HTTP_STATE_OPTIONS) httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST"); if (code == HTTP_STATUS_UNAUTHORIZED) { char value[HTTP_MAX_VALUE]; // WWW-Authenticate value snprintf(value, sizeof(value), "Basic realm=\"%s\"", client->system->name); httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value); } if (type) { if (!strcmp(type, "text/html")) httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, "text/html; charset=utf-8"); else httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type); if (content_encoding) httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding); } httpSetLength(client->http, length); if (code == HTTP_STATUS_UPGRADE_REQUIRED && client->operation == HTTP_STATE_GET) { char redirect[1024]; // Redirect URI code = HTTP_STATUS_MOVED_PERMANENTLY; httpAssembleURI(HTTP_URI_CODING_ALL, redirect, sizeof(redirect), "https", NULL, client->host_field, client->host_port, client->uri); httpSetField(client->http, HTTP_FIELD_LOCATION, redirect); } if (httpWriteResponse(client->http, code) < 0) return (false); // Send the response data... if (message[0]) { // Send a plain text message. if (httpPrintf(client->http, "%s", message) < 0) return (false); if (httpWrite2(client->http, "", 0) < 0) return (false); } else if (client->response) { // Send an IPP response... _papplLogAttributes(client, ippOpString(client->operation_id), client->response, true); ippSetState(client->response, IPP_STATE_IDLE); if (ippWrite(client->http, client->response) != IPP_STATE_DATA) return (false); } return (true); } // // 'papplClientRespondRedirect()' - Respond with a redirect to another page. // // This function sends a HTTP response that redirects the client to another // page or URL. The most common "code" value to return is `HTTP_STATUS_FOUND`. // bool // O - `true` on success, `false` otherwise papplClientRespondRedirect( pappl_client_t *client, // I - Client http_status_t code, // I - `HTTP_STATUS_MOVED_PERMANENTLY` or `HTTP_STATUS_FOUND` const char *path) // I - Redirection path/URL { papplLogClient(client, PAPPL_LOGLEVEL_INFO, "%s %s", httpStatus(code), path); // Send the HTTP response header... httpClearFields(client->http); httpSetField(client->http, HTTP_FIELD_SERVER, papplSystemGetServerHeader(client->system)); httpSetLength(client->http, 0); if (*path == '/' || !strchr(path, ':')) { // Generate an absolute URL... char url[1024]; // Absolute URL if (*path == '/') httpAssembleURI(HTTP_URI_CODING_ALL, url, sizeof(url), "https", NULL, client->host_field, client->host_port, path); else httpAssembleURIf(HTTP_URI_CODING_ALL, url, sizeof(url), "https", NULL, client->host_field, client->host_port, "/%s", path); httpSetField(client->http, HTTP_FIELD_LOCATION, url); } else { // The path is already an absolute URL... httpSetField(client->http, HTTP_FIELD_LOCATION, path); } if (httpWriteResponse(client->http, code) < 0) return (false); return (httpWrite2(client->http, "", 0) >= 0); } // // '_papplClientRun()' - Process client requests on a thread. // void * // O - Exit status _papplClientRun( pappl_client_t *client) // I - Client { int first_time = 1; // First time request? // Loop until we are out of requests or timeout (30 seconds)... while (httpWait(client->http, 30000)) { if (first_time) { // See if we need to negotiate a TLS connection... char buf[1]; // First byte from client if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0]))) { papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Starting HTTPS session."); if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS)) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to encrypt connection: %s", cupsLastErrorString()); break; } papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Connection now encrypted."); } first_time = 0; } if (!_papplClientProcessHTTP(client)) break; _papplClientCleanTempFiles(client); } // Close the conection to the client and return... _papplClientDelete(client); return (NULL); } // // 'eval_if_modified()' - Evaluate an "If-Modified-Since" header. // static bool // O - `true` if modified, `false` otherwise eval_if_modified( pappl_client_t *client, // I - Client _pappl_resource_t *r) // I - Resource { const char *ptr; // Pointer into field time_t date = 0; // Time/date value off_t size = 0; // Size/length value // Dynamic content always needs to be updated... if (r->cb) return (true); // Get "If-Modified-Since:" header ptr = httpGetField(client->http, HTTP_FIELD_IF_MODIFIED_SINCE); if (*ptr == '\0') return (true); // Decode the If-Modified-Since: header... while (*ptr != '\0') { while (isspace(*ptr) || *ptr == ';') ptr ++; if (!strncasecmp(ptr, "length=", 7)) { char *next; // Next character size = strtoll(ptr + 7, &next, 10); if (!next) { papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "If-Modified-Since: Bad length."); return (true); } ptr = next; } else if (isalpha(*ptr & 255)) { date = httpGetDateTime(ptr); while (*ptr != '\0' && *ptr != ';') ptr ++; } else ptr ++; } // Return the evaluation based on the last modified date, time, and size... return ((size != 0 && size != (off_t)r->length) || (date != 0 && date < r->last_modified) || (size == 0 && date == 0)); } pappl-1.0.3/pappl/client.h000066400000000000000000000071451403603036100153750ustar00rootroot00000000000000// // Public client header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_CLIENT_H_ # define _PAPPL_CLIENT_H_ // // Include necessary headers... // # include "base.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Functions... // extern char *papplClientGetCSRFToken(pappl_client_t *client, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplClientGetCookie(pappl_client_t *client, const char *name, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern int papplClientGetForm(pappl_client_t *client, cups_option_t **form) _PAPPL_PUBLIC; extern const char *papplClientGetHostName(pappl_client_t *client) _PAPPL_PUBLIC; extern int papplClientGetHostPort(pappl_client_t *client) _PAPPL_PUBLIC; extern http_t *papplClientGetHTTP(pappl_client_t *client) _PAPPL_PUBLIC; extern pappl_job_t *papplClientGetJob(pappl_client_t *client) _PAPPL_PUBLIC; extern http_state_t papplClientGetMethod(pappl_client_t *client) _PAPPL_PUBLIC; extern ipp_op_t papplClientGetOperation(pappl_client_t *client) _PAPPL_PUBLIC; extern const char *papplClientGetOptions(pappl_client_t *client) _PAPPL_PUBLIC; extern pappl_printer_t *papplClientGetPrinter(pappl_client_t *client) _PAPPL_PUBLIC; extern ipp_t *papplClientGetRequest(pappl_client_t *client) _PAPPL_PUBLIC; extern ipp_t *papplClientGetResponse(pappl_client_t *client) _PAPPL_PUBLIC; extern pappl_system_t *papplClientGetSystem(pappl_client_t *client) _PAPPL_PUBLIC; extern const char *papplClientGetURI(pappl_client_t *client) _PAPPL_PUBLIC; extern const char *papplClientGetUsername(pappl_client_t *client) _PAPPL_PUBLIC; extern bool papplClientHTMLAuthorize(pappl_client_t *client) _PAPPL_PUBLIC; extern void papplClientHTMLEscape(pappl_client_t *client, const char *s, size_t slen) _PAPPL_PUBLIC; extern void papplClientHTMLFooter(pappl_client_t *client) _PAPPL_PUBLIC; extern void papplClientHTMLHeader(pappl_client_t *client, const char *title, int refresh) _PAPPL_PUBLIC; extern void papplClientHTMLPrinterFooter(pappl_client_t *client) _PAPPL_PUBLIC; extern void papplClientHTMLPrinterHeader(pappl_client_t *client, pappl_printer_t *printer, const char *title, int refresh, const char *label, const char *path_or_url) _PAPPL_PUBLIC; extern void papplClientHTMLPrintf(pappl_client_t *client, const char *format, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(2, 3); extern void papplClientHTMLPuts(pappl_client_t *client, const char *s) _PAPPL_PUBLIC; extern void papplClientHTMLStartForm(pappl_client_t *client, const char *action, bool multipart) _PAPPL_PUBLIC; extern http_status_t papplClientIsAuthorized(pappl_client_t *client) _PAPPL_PUBLIC; extern bool papplClientIsValidForm(pappl_client_t *client, int num_form, cups_option_t *form) _PAPPL_PUBLIC; extern bool papplClientRespond(pappl_client_t *client, http_status_t code, const char *content_coding, const char *type, time_t last_modified, size_t length) _PAPPL_PUBLIC; extern ipp_t *papplClientRespondIPP(pappl_client_t *client, ipp_status_t status, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3, 4); extern void papplClientRespondIPPUnsupported(pappl_client_t *client, ipp_attribute_t *attr) _PAPPL_PUBLIC; extern bool papplClientRespondRedirect(pappl_client_t *client, http_status_t code, const char *path) _PAPPL_PUBLIC; extern void papplClientSetCookie(pappl_client_t *client, const char * name, const char *value, int expires) _PAPPL_PUBLIC; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_CLIENT_H_ pappl-1.0.3/pappl/contact.c000066400000000000000000000100501403603036100155320ustar00rootroot00000000000000// // Contact functions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "base-private.h" // // '_papplContactExport()' - Export contact information to an "xxx-contact-col" value. // ipp_t * // O - "xxx-contact-col" value _papplContactExport( pappl_contact_t *contact) // I - Contact information { ipp_t *col = ippNew(); // "xxx-contact-col" value char tel_uri[256], // Telephone URI value mailto_uri[256], // Email URI value vcard[1024]; // VCARD value // Build values... if (contact->email[0]) httpAssembleURI(HTTP_URI_CODING_ALL, mailto_uri, sizeof(mailto_uri), "mailto", NULL, contact->email, 0, NULL); else mailto_uri[0] = '\0'; if (contact->telephone[0]) httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel", NULL, contact->telephone, 0, NULL); else tel_uri[0] = '\0'; snprintf(vcard, sizeof(vcard), "BEGIN:VCARD\r\n" "VERSION:4.0\r\n" "FN:%s\r\n" "TEL;VALUE=URI;TYPE=work:%s\r\n" "EMAIL;TYPE=work:%s\r\n" "END:VCARD\r\n", contact->name, tel_uri, contact->email); // Add values... ippAddString(col, IPP_TAG_ZERO, IPP_TAG_NAME, "contact-name", NULL, contact->name); if (mailto_uri[0]) ippAddString(col, IPP_TAG_ZERO, IPP_TAG_URI, "contact-uri", NULL, mailto_uri); else if (tel_uri[0]) ippAddString(col, IPP_TAG_ZERO, IPP_TAG_URI, "contact-uri", NULL, tel_uri); else ippAddString(col, IPP_TAG_ZERO, IPP_CONST_TAG(IPP_TAG_URI), "contact-uri", NULL, "data:,"); ippAddString(col, IPP_TAG_ZERO, IPP_TAG_TEXT, "contact-vcard", NULL, vcard); return (col); } // // '_papplContactImport()' - Import contact information from an "xxx-contact-col" value. // void _papplContactImport( ipp_t *col, // I - "xxx-contact-col" value pappl_contact_t *contact) // O - Contact information { const char *val; // Value memset(contact, 0, sizeof(pappl_contact_t)); if ((val = ippGetString(ippFindAttribute(col, "contact-name", IPP_TAG_NAME), 0, NULL)) != NULL) strlcpy(contact->name, val, sizeof(contact->name)); if ((val = ippGetString(ippFindAttribute(col, "contact-uri", IPP_TAG_NAME), 0, NULL)) != NULL) { char scheme[32], // URI scheme userpass[32], // User:pass from URI host[256], // Host from URI resource[256]; // Resource from URI int port; // Port number from URI if (httpSeparateURI(HTTP_URI_CODING_ALL, val, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) >= HTTP_URI_STATUS_OK) { if (!strcmp(scheme, "tel")) strlcpy(contact->telephone, resource, sizeof(contact->telephone)); else if (!strcmp(scheme, "mailto")) strlcpy(contact->email, resource, sizeof(contact->email)); } } if ((val = ippGetString(ippFindAttribute(col, "contact-vcard", IPP_TAG_TEXT), 0, NULL)) != NULL) { char vcard[1024], // Local copy of vcard data *ptr, // Pointer in data *next, // Next line *crlf; // CR LF at end of line // Note: Only VCARD data <= 1023 bytes currently supported... strlcpy(vcard, val, sizeof(vcard)); for (ptr = vcard; *ptr; ptr = next) { // Find the end of the current line... if ((crlf = strstr(ptr, "\r\n")) != NULL) { *crlf = '\0'; next = crlf + 2; } else next = ptr + strlen(ptr); if (!strncmp(ptr, "FN:", 3) && !contact->name[0]) { strlcpy(contact->name, ptr + 3, sizeof(contact->name)); } else if (!strncmp(ptr, "TEL;", 4) && !contact->telephone[0]) { if ((ptr = strstr(ptr, ":tel:")) != NULL) strlcpy(contact->telephone, ptr + 5, sizeof(contact->telephone)); } else if (!strncmp(ptr, "EMAIL;", 6) && !contact->email[0]) { if ((ptr = strchr(ptr, ':')) != NULL) strlcpy(contact->email, ptr + 1, sizeof(contact->email)); } } } } pappl-1.0.3/pappl/device-file.c000066400000000000000000000113041403603036100162560ustar00rootroot00000000000000// // File device support code for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2007-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "device-private.h" // // Local functions... // static void pappl_file_close(pappl_device_t *device); static bool pappl_file_open(pappl_device_t *device, const char *device_uri, const char *name); static ssize_t pappl_file_write(pappl_device_t *device, const void *buffer, size_t bytes); // // '_papplDeviceAddFileScheme()' - Add the "file" device URI scheme. // void _papplDeviceAddFileScheme(void) { papplDeviceAddScheme("file", PAPPL_DEVTYPE_FILE, NULL, pappl_file_open, pappl_file_close, NULL, pappl_file_write, NULL, NULL); } // // 'pappl_file_close()' - Close a file. // static void pappl_file_close(pappl_device_t *device)// I - Device { int *fd; // File descriptor // Make sure we have a valid file descriptor... if ((fd = papplDeviceGetData(device)) == NULL || *fd < 0) return; close(*fd); free(fd); papplDeviceSetData(device, NULL); } // // 'pappl_file_open()' - Open a file. // static bool // O - `true` on success, `false` otherwise pappl_file_open( pappl_device_t *device, // I - Device const char *device_uri, // I - Device URI const char *name) // I - Job name { int *fd; // File descriptor char scheme[32], // URI scheme userpass[32], // Username/password (not used) host[256], // Host name or make resource[256], // Resource path, if any *options, // Pointer to options, if any filename[1024], // Filename *fileptr; // Pointer into filename const char *ext = "prn"; // Extension int port; // Port number struct stat resinfo; // Resource path information // Allocate memory for a file descriptor if ((fd = (int *)malloc(sizeof(int))) == NULL) { papplDeviceError(device, "Unable to allocate memory for file: %s", strerror(errno)); return (false); } // Get the resource path for the filename... httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)); if ((options = strchr(resource, '?')) != NULL) { *options++ = '\0'; if (!strncmp(options, "ext=", 4) && !strchr(options, '/')) ext = options + 4; } if (stat(resource, &resinfo)) resinfo.st_mode = S_IFREG | 0644; if (S_ISDIR(resinfo.st_mode)) { // Resource is a directory, so create an output filename using the job name snprintf(filename, sizeof(filename), "%s/%s.%s", resource, name, ext); for (fileptr = filename + strlen(resource) + 1; *fileptr; fileptr ++) { if (*fileptr < ' ' || *fileptr == 0x7f || *fileptr & 0x80 || *fileptr == '/') *fileptr = '_'; } if ((*fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { papplDeviceError(device, "Unable to create '%s': %s", filename, strerror(errno)); goto error; } } else if (S_ISCHR(resinfo.st_mode)) { // Resource is a character device... if ((*fd = open(resource, O_WRONLY | O_EXCL)) < 0) { papplDeviceError(device, "Unable to open '%s': %s", resource, strerror(errno)); goto error; } } else if (S_ISREG(resinfo.st_mode)) { // Resource is a regular file... if ((*fd = open(resource, O_WRONLY | O_APPEND | O_CREAT, 0666)) < 0) { papplDeviceError(device, "Unable to open '%s': %s", resource, strerror(errno)); goto error; } } else { *fd = -1; errno = EINVAL; } // Otherwise, save the file descriptor and return success... papplDeviceSetData(device, fd); return (true); // If we were unable to open the file, return an error... error: free(fd); return (false); } // // 'pappl_file_write()' - Write to a file. // static ssize_t // O - Bytes written pappl_file_write(pappl_device_t *device,// I - Device const void *buffer,// I - Buffer to write size_t bytes) // I - Bytes to write { int *fd; // File descriptor const char *ptr; // Pointer into buffer ssize_t count, // Total bytes written written; // Bytes written this time // Make sure we have a valid file descriptor... if ((fd = papplDeviceGetData(device)) == NULL || *fd < 0) return (-1); for (count = 0, ptr = (const char *)buffer; count < (ssize_t)bytes; count += written, ptr += written) { if ((written = write(*fd, ptr, bytes - (size_t)count)) < 0) { if (errno == EINTR || errno == EAGAIN) { written = 0; continue; } count = -1; break; } } return (count); } pappl-1.0.3/pappl/device-network.c000066400000000000000000001307441403603036100170420ustar00rootroot00000000000000// // Network device support code for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2007-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "device-private.h" #include "dnssd-private.h" #include "snmp-private.h" #include "printer.h" #include #include // // Local types... // typedef struct _pappl_socket_s // Socket device data { int fd; // File descriptor connection to device char *host; // Hostname int port; // Port number http_addrlist_t *list; // Address list } _pappl_socket_t; typedef struct _pappl_dns_sd_dev_t // DNS-SD browse data { #ifdef HAVE_DNSSD DNSServiceRef ref; // Service reference for query #endif // HAVE_DNSSD #ifdef HAVE_AVAHI AvahiRecordBrowser *ref; // Browser for query #endif // HAVE_AVAHI char *name, // Service name *domain, // Domain name *fullName, // Full name *make_and_model, // Make and model from TXT record *device_id, // 1284 device ID from TXT record *uuid; // UUID from TXT record } _pappl_dns_sd_dev_t; typedef struct _pappl_snmp_dev_s // SNMP browse data { http_addr_t address; // Address of device char *addrname, // Name of device *uri, // Device URI *device_id; // IEEE-1284 device id int port; // Port number } _pappl_snmp_dev_t; typedef enum _pappl_snmp_query_e // SNMP query request IDs for each field { _PAPPL_SNMP_QUERY_DEVICE_TYPE = 0x01, // Device type OID _PAPPL_SNMP_QUERY_DEVICE_ID, // IEEE-1284 device ID OIDs _PAPPL_SNMP_QUERY_DEVICE_SYSNAME, // sysName OID _PAPPL_SNMP_QUERY_DEVICE_PORT // Raw socket port number OIDs } _pappl_snmp_query_t; // // Local functions... // #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) # ifdef HAVE_DNSSD static void pappl_dnssd_browse_cb(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context); static void pappl_dnssd_query_cb(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context); static void pappl_dnssd_resolve_cb(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context); # else static void pappl_dnssd_browse_cb(AvahiServiceBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *serviceName, const char *serviceType, const char *replyDomain, AvahiLookupResultFlags flags, void *context); static void pappl_dnssd_query_cb(AvahiRecordBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, uint16_t rrclass, uint16_t rrtype, const void *rdata, size_t rdlen, AvahiLookupResultFlags flags, void *context); static void pappl_dnssd_resolve_cb(AvahiServiceResolver *resolver, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *context); # endif // HAVE_DNSSD static int pappl_dnssd_compare_devices(_pappl_dns_sd_dev_t *a, _pappl_dns_sd_dev_t *b); static void pappl_dnssd_free(_pappl_dns_sd_dev_t *d); static _pappl_dns_sd_dev_t *pappl_dnssd_get_device(cups_array_t *devices, const char *serviceName, const char *replyDomain); static bool pappl_dnssd_list(pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data); static void pappl_dnssd_unescape(char *dst, const char *src, size_t dstsize); #endif // HAVE_DNSSD || HAVE_AVAHI static int pappl_snmp_compare_devices(_pappl_snmp_dev_t *a, _pappl_snmp_dev_t *b); static bool pappl_snmp_find(pappl_device_cb_t cb, void *data, _pappl_socket_t *sock, pappl_deverror_cb_t err_cb, void *err_data); static void pappl_snmp_free(_pappl_snmp_dev_t *d); static http_addrlist_t *pappl_snmp_get_interface_addresses(void); static bool pappl_snmp_list(pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data); static bool pappl_snmp_open_cb(const char *device_info, const char *device_uri, const char *device_id, void *data); static void pappl_snmp_read_response(cups_array_t *devices, int fd, pappl_deverror_cb_t err_cb, void *err_data); static void pappl_socket_close(pappl_device_t *device); static char *pappl_socket_getid(pappl_device_t *device, char *buffer, size_t bufsize); static bool pappl_socket_open(pappl_device_t *device, const char *device_uri, const char *name); static ssize_t pappl_socket_read(pappl_device_t *device, void *buffer, size_t bytes); static pappl_preason_t pappl_socket_status(pappl_device_t *device); static ssize_t pappl_socket_write(pappl_device_t *device, const void *buffer, size_t bytes); // // '_papplDeviceAddNetworkSchemes()' - Add all of the supported network schemes. // void _papplDeviceAddNetworkSchemes(void) { #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) papplDeviceAddScheme("dnssd", PAPPL_DEVTYPE_DNS_SD, pappl_dnssd_list, pappl_socket_open, pappl_socket_close, pappl_socket_read, pappl_socket_write, pappl_socket_status, pappl_socket_getid); #endif // HAVE_DNSSD || HAVE_AVAHI papplDeviceAddScheme("snmp", PAPPL_DEVTYPE_SNMP, pappl_snmp_list, pappl_socket_open, pappl_socket_close, pappl_socket_read, pappl_socket_write, pappl_socket_status, pappl_socket_getid); papplDeviceAddScheme("socket", PAPPL_DEVTYPE_SOCKET, NULL, pappl_socket_open, pappl_socket_close, pappl_socket_read, pappl_socket_write, pappl_socket_status, pappl_socket_getid); } #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) # ifdef HAVE_DNSSD // // 'pappl_dnssd_browse_cb()' - Browse for DNS-SD devices. // static void pappl_dnssd_browse_cb( DNSServiceRef sdRef, // I - Service reference DNSServiceFlags flags, // I - Option flags uint32_t interfaceIndex, // I - Interface number DNSServiceErrorType errorCode, // I - Error, if any const char *serviceName, // I - Name of service/device const char *regtype, // I - Registration type const char *replyDomain, // I - Service domain void *context) // I - Devices array { _PAPPL_DEBUG("DEBUG: pappl_browse_cb(sdRef=%p, flags=%x, interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", regtype=\"%s\", replyDomain=\"%s\", context=%p)\n", sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain, context); (void)regtype; // Only process "add" data... if (errorCode == kDNSServiceErr_NoError && (flags & kDNSServiceFlagsAdd)) { // Get the device... pappl_dnssd_get_device((cups_array_t *)context, serviceName, replyDomain); } } # else // // 'pappl_dnssd_browse_cb()' - Browse for DNS-SD devices. // static void pappl_dnssd_browse_cb( AvahiServiceBrowser *browser, // I - Browser AvahiIfIndex interface, // I - Interface index AvahiProtocol protocol, // I - Network protocol AvahiBrowserEvent event, // I - What happened const char *name, // I - Service name const char *type, // I - Service type const char *domain, // I - Domain AvahiLookupResultFlags flags, // I - Flags void *context) // I - Devices array { if (event == AVAHI_BROWSER_NEW) pappl_dnssd_get_device((cups_array_t *)context, name, domain); } # endif // HAVE_DNSSD // // 'pappl_dnssd_compare_devices()' - Compare two DNS-SD devices. // static int // O - Result of comparison pappl_dnssd_compare_devices( _pappl_dns_sd_dev_t *a, // I - First device _pappl_dns_sd_dev_t *b) // I - Second device { _PAPPL_DEBUG("pappl_dnssd_compare_devices(a=%p(%s), b=%p(%s))\n", a, a->name, b, b->name); return (strcmp(a->name, b->name)); } // // 'pappl_dnssd_free()' - Free the memory used for a DNS-SD device. // static void pappl_dnssd_free(_pappl_dns_sd_dev_t *d)// I - Device { // Free all memory... free(d->name); free(d->domain); free(d->fullName); free(d->make_and_model); free(d->device_id); free(d->uuid); free(d); } // // 'pappl_dnssd_get_device()' - Create or update a DNS-SD device. // static _pappl_dns_sd_dev_t * // O - Device pappl_dnssd_get_device( cups_array_t *devices, // I - Device array const char *serviceName, // I - Name of service/device const char *replyDomain) // I - Service domain { _pappl_dns_sd_dev_t key, // Search key *device; // Device char fullName[1024]; // Full name for query _PAPPL_DEBUG("pappl_dnssd_get_device(devices=%p, serviceName=\"%s\", replyDomain=\"%s\")\n", devices, serviceName, replyDomain); // See if this is a new device... key.name = (char *)serviceName; if ((device = cupsArrayFind(devices, &key)) != NULL) { // Nope, see if this is for a different domain... if (!strcasecmp(device->domain, "local.") && strcasecmp(device->domain, replyDomain)) { // Update the .local listing to use the "global" domain name instead. free(device->domain); device->domain = strdup(replyDomain); # ifdef HAVE_DNSSD DNSServiceConstructFullName(fullName, device->name, "_pdl-datastream._tcp.", replyDomain); # else avahi_service_name_join(fullName, sizeof(fullName), serviceName, "_pdl-datastream._tcp.", replyDomain); # endif // HAVE_DNSSD free(device->fullName); if ((device->fullName = strdup(fullName)) == NULL) { cupsArrayRemove(devices, device); return (NULL); } } return (device); } // Yes, add the device... if ((device = calloc(sizeof(_pappl_dns_sd_dev_t), 1)) == NULL) return (NULL); if ((device->name = strdup(serviceName)) == NULL) { free(device); return (NULL); } device->domain = strdup(replyDomain); cupsArrayAdd(devices, device); // Set the "full name" of this service, which is used for queries... # ifdef HAVE_DNSSD DNSServiceConstructFullName(fullName, serviceName, "_pdl-datastream._tcp.", replyDomain); # else avahi_service_name_join(fullName, sizeof(fullName), serviceName, "_pdl-datastream._tcp.", replyDomain); # endif /* HAVE_DNSSD */ if ((device->fullName = strdup(fullName)) == NULL) { cupsArrayRemove(devices, device); return (NULL); } // Query the TXT record for the device ID and make and model... #ifdef HAVE_DNSSD device->ref = _papplDNSSDInit(NULL); DNSServiceQueryRecord(&(device->ref), kDNSServiceFlagsShareConnection, 0, device->fullName, kDNSServiceType_TXT, kDNSServiceClass_IN, pappl_dnssd_query_cb, device); #else device->ref = avahi_record_browser_new(_papplDNSSDInit(NULL), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, device->fullName, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, 0, pappl_dnssd_query_cb, device); #endif /* HAVE_AVAHI */ return (device); } // // 'pappl_dnssd_list()' - List printers using DNS-SD. // static bool // O - `true` if the callback returned `true`, `false` otherwise pappl_dnssd_list( pappl_device_cb_t cb, // I - Callback function void *data, // I - User data for callback pappl_deverror_cb_t err_cb, // I - Error callback void *err_data) // I - Data for error callback { bool ret = false; // Return value cups_array_t *devices; // DNS-SD devices _pappl_dns_sd_dev_t *device; // Current DNS-SD device char device_name[1024], // Network device name device_uri[1024]; // Network device URI int last_count, // Last number of devices timeout; // Timeout counter # ifdef HAVE_DNSSD int error; // Error code, if any DNSServiceRef pdl_ref; // Browse reference for _pdl-datastream._tcp # else AvahiServiceBrowser *pdl_ref; // Browse reference for _pdl-datastream._tcp # endif // HAVE_DNSSD devices = cupsArrayNew3((cups_array_func_t)pappl_dnssd_compare_devices, NULL, NULL, 0, NULL, (cups_afree_func_t)pappl_dnssd_free); _PAPPL_DEBUG("pappl_dnssd_find: devices=%p\n", devices); _papplDNSSDLock(); # ifdef HAVE_DNSSD pdl_ref = _papplDNSSDInit(NULL); if ((error = DNSServiceBrowse(&pdl_ref, kDNSServiceFlagsShareConnection, 0, "_pdl-datastream._tcp", NULL, (DNSServiceBrowseReply)pappl_dnssd_browse_cb, devices)) != kDNSServiceErr_NoError) { _papplDeviceError(err_cb, err_data, "Unable to create service browser: %s (%d).", _papplDNSSDStrError(error), error); cupsArrayDelete(devices); return (ret); } # else if ((pdl_ref = avahi_service_browser_new(_papplDNSSDInit(NULL), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_pdl-datastream._tcp", NULL, 0, pappl_dnssd_browse_cb, devices)) == NULL) { _papplDeviceError(err_cb, err_data, "Unable to create service browser."); cupsArrayDelete(devices); return (ret); } # endif // HAVE_DNSSD _papplDNSSDUnlock(); // Wait up to 10 seconds for us to find all available devices... for (timeout = 10000, last_count = 0; timeout > 0; timeout -= 250) { // 250000 microseconds == 250 milliseconds _PAPPL_DEBUG("pappl_dnssd_find: timeout=%d, last_count=%d\n", timeout, last_count); usleep(250000); if (last_count == cupsArrayCount(devices)) break; last_count = cupsArrayCount(devices); } _PAPPL_DEBUG("pappl_dnssd_find: timeout=%d, last_count=%d\n", timeout, last_count); // Do the callback for each of the devices... for (device = (_pappl_dns_sd_dev_t *)cupsArrayFirst(devices); device; device = (_pappl_dns_sd_dev_t *)cupsArrayNext(devices)) { snprintf(device_name, sizeof(device_name), "%s (DNS-SD Network Printer)", device->name); if (device->uuid) httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "dnssd", NULL, device->fullName, 0, "/?uuid=%s", device->uuid); else httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "dnssd", NULL, device->fullName, 0, "/"); if ((*cb)(device_name, device_uri, device->device_id, data)) { ret = true; break; } } // Stop browsing and free memory... _papplDNSSDLock(); # ifdef HAVE_DNSSD DNSServiceRefDeallocate(pdl_ref); # else avahi_service_browser_free(pdl_ref); # endif // HAVE_DNSSD _papplDNSSDUnlock(); cupsArrayDelete(devices); return (ret); } // // 'pappl_dnssd_query_cb()' - Query a DNS-SD service. // # ifdef HAVE_DNSSD static void pappl_dnssd_query_cb( DNSServiceRef sdRef, // I - Service reference DNSServiceFlags flags, // I - Data flags uint32_t interfaceIndex, // I - Interface (unused) DNSServiceErrorType errorCode, // I - Error, if any const char *fullName, // I - Full service name uint16_t rrtype, // I - Record type uint16_t rrclass, // I - Record class uint16_t rdlen, // I - Length of record data const void *rdata, // I - Record data uint32_t ttl, // I - Time-to-live void *context) // I - Device # else static void pappl_dnssd_query_cb( AvahiRecordBrowser *browser, // I - Record browser */ AvahiIfIndex interfaceIndex, // I - Interface index (unused) AvahiProtocol protocol, // I - Network protocol (unused) AvahiBrowserEvent event, // I - What happened? const char *fullName, // I - Service name uint16_t rrclass, // I - Record clasa uint16_t rrtype, // I - Record type const void *rdata, // I - TXT record size_t rdlen, // I - Length of TXT record AvahiLookupResultFlags flags, // I - Flags void *context) // I - Device # endif // HAVE_DNSSD { char *ptr; // Pointer into string _pappl_dns_sd_dev_t *device = (_pappl_dns_sd_dev_t *)context; // Device const uint8_t *data, // Pointer into data *datanext, // Next key/value pair *dataend; // End of entire TXT record uint8_t datalen; // Length of current key/value pair char key[256], // Key string value[256], // Value string cmd[256], // usb_CMD string mdl[256], // usb_MDL string mfg[256], // usb_MFG string pdl[256], // pdl string product[256], // product string ty[256], // ty string device_id[1024]; // 1284 device ID */ # ifdef HAVE_DNSSD // Only process "add" data... if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd)) return; (void)sdRef; (void)interfaceIndex; (void)fullName; (void)ttl; (void)rrtype; (void)rrclass; # else // Only process "add" data... if (event != AVAHI_BROWSER_NEW) return; (void)interfaceIndex; (void)protocol; (void)fullName; (void)rrclass; (void)rrtype; (void)flags; # endif /* HAVE_DNSSD */ // Pull out the make and model and device ID data from the TXT record... cmd[0] = '\0'; mfg[0] = '\0'; mdl[0] = '\0'; pdl[0] = '\0'; product[0] = '\0'; ty[0] = '\0'; for (data = rdata, dataend = data + rdlen; data < dataend; data = datanext) { // Read a key/value pair starting with an 8-bit length. Since the length is // 8 bits and the size of the key/value buffers is 256, we don't need to // check for overflow... datalen = *data++; if (!datalen || (data + datalen) > dataend) break; datanext = data + datalen; for (ptr = key; data < datanext && *data != '='; data ++) *ptr++ = (char)*data; *ptr = '\0'; if (data < datanext && *data == '=') { data ++; if (data < datanext) memcpy(value, data, (size_t)(datanext - data)); value[datanext - data] = '\0'; } else { continue; } if (!strcasecmp(key, "usb_CMD")) strlcpy(cmd, value, sizeof(cmd)); else if (!strcasecmp(key, "usb_MDL")) strlcpy(mdl, value, sizeof(mdl)); else if (!strcasecmp(key, "usb_MFG")) strlcpy(mfg, value, sizeof(mfg)); else if (!strcasecmp(key, "pdl")) strlcpy(pdl, value, sizeof(pdl)); else if (!strcasecmp(key, "product")) strlcpy(product, value, sizeof(product)); else if (!strcasecmp(key, "ty")) strlcpy(ty, value, sizeof(ty)); } // Synthesize values as needed... if (!cmd[0] && pdl[0]) { int i; // Looping var char *cmdptr, // Pointer into CMD value *pdlptr; // Pointer into pdl value static const char * const pdls[][2] = { // MIME media type to command set mapping { "application/postscript", "PS" }, { "application/vnd.canon-cpdl", "CPDL" }, { "application/vnd.canon-lips", "LIPS" }, { "application/vnd.hp-PCL", "PCL" }, { "application/vnd.hp-PCLXL", "PCLXL" }, { "application/vnd.ms-xpsdocument", "XPS" }, { "image/jpeg", "JPEG" }, { "image/tiff", "TIFF" } }; for (i = 0, cmdptr = cmd; i < (int)(sizeof(pdls) / sizeof(pdls[0])); i ++) { if ((pdlptr = strstr(pdl, pdls[i][0])) != NULL) { if ((pdlptr == pdl || pdlptr[-1] == ',') && pdlptr[strlen(pdls[i][0])] == ',') { if (cmdptr > cmd && cmdptr < (cmd + sizeof(cmd) - 1)) *cmdptr++ = ','; strlcpy(cmdptr, pdls[i][1], sizeof(cmd) - (size_t)(cmdptr - cmd)); cmdptr += strlen(cmdptr); } } } } if (!ty[0] && product[0]) { if (product[0] == '(') { strlcpy(ty, product + 1, sizeof(ty)); if ((ptr = product + strlen(product) - 1) >= product && *ptr == ')') *ptr = '\0'; } else strlcpy(ty, product, sizeof(ty)); } if (!ty[0] && mfg[0] && mdl[0]) snprintf(ty, sizeof(ty), "%s %s", mfg, mdl); if (!mfg[0] && ty[0]) { strlcpy(mfg, ty, sizeof(mfg)); if ((ptr = strchr(mfg, ' ')) != NULL) *ptr = '\0'; } if (!mdl[0] && ty[0]) { if ((ptr = strchr(ty, ' ')) != NULL) strlcpy(mdl, ptr + 1, sizeof(mdl)); else strlcpy(mdl, ty, sizeof(mdl)); } snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:%s;", mfg, mdl, cmd); // Save the make and model and IEEE-1284 device ID... device->device_id = strdup(device_id); device->make_and_model = strdup(ty); } // // 'pappl_dnssd_resolve_cb()' - Resolve a DNS-SD service. // # ifdef HAVE_DNSSD static void pappl_dnssd_resolve_cb( DNSServiceRef sdRef, // I - Service reference DNSServiceFlags flags, // I - Option flags uint32_t interfaceIndex, // I - Interface number DNSServiceErrorType errorCode, // I - Error, if any const char *fullname, // I - Full service domain name const char *host_name, // I - Host name uint16_t port, // I - Port number uint16_t txtLen, // I - TXT record len const unsigned char *txtRecord, // I - TXT record void *context) // I - Device { (void)sdRef; (void)interfaceIndex; (void)fullname; (void)txtLen; (void)txtRecord; if (errorCode == kDNSServiceErr_NoError && (flags & kDNSServiceFlagsAdd)) { _pappl_socket_t *sock = (_pappl_socket_t *)context; // Socket sock->host = strdup(host_name); sock->port = port; } } # else // // 'pappl_dnssd_resolve_cb()' - Resolve a DNS-SD service. // static void pappl_dnssd_resolve_cb( AvahiServiceResolver *resolver, // I - Service resolver AvahiIfIndex interface, // I - Interface number AvahiProtocol protocol, // I - Network protocol AvahiResolverEvent event, // I - What happened const char *name, // I - Service name const char *type, // I - Service type const char *domain, // I - Domain const char *host_name, // I - Host name const AvahiAddress *address, // I - Address uint16_t port, // I - Port number AvahiStringList *txt, // I - TXT record AvahiLookupResultFlags flags, // I - Flags void *context) // I - Device { if (!resolver) return; if (event == AVAHI_RESOLVER_FOUND) { _pappl_socket_t *sock = (_pappl_socket_t *)context; // Socket sock->host = strdup(host_name); sock->port = port; } } # endif // HAVE_DNSSD // // 'pappl_dnssd_unescape()' - Unescape a service name. // static void pappl_dnssd_unescape( char *dst, // I - Destination buffer const char *src, // I - Source string size_t dstsize) // I - Size of destination buffer { char *dstend = dst + dstsize - 1; // End of destination buffer while (*src && dst < dstend) { if (*src == '\\') { src ++; if (isdigit(src[0] & 255) && isdigit(src[1] & 255) && isdigit(src[2] & 255)) { *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0'; src += 3; } else *dst++ = *src++; } else *dst++ = *src ++; } *dst = '\0'; } #endif // HAVE_DNSSD || HAVE_AVAHI // // 'pappl_snmp_compare_devices()' - Compare two SNMP devices. // static int // O - Result of comparison pappl_snmp_compare_devices( _pappl_snmp_dev_t *a, // I - First device _pappl_snmp_dev_t *b) // I - Second device { int ret = strcmp(a->addrname, b->addrname); // Return value _PAPPL_DEBUG("pappl_snmp_compare_devices(a=%p(%s), b=%p(%s)) = %d\n", a, a->addrname, b, b->addrname, ret); return (ret); } // // 'pappl_snmp_find()' - Find an SNMP device. // static bool // O - `true` if found, `false` if not pappl_snmp_find( pappl_device_cb_t cb, // I - Callback function void *data, // I - User data pointer _pappl_socket_t *sock, // O - Device info pappl_deverror_cb_t err_cb, // I - Error callback void *err_data) // I - Error callback data { bool ret = false; // Return value cups_array_t *devices = NULL;// Device array int snmp_sock = -1, // SNMP socket last_count; // Last devices count fd_set input; // Input set for select() struct timeval timeout; // Timeout for select() time_t endtime; // End time for scan http_addrlist_t *addrs, // List of addresses *addr; // Current address _pappl_snmp_dev_t *cur_device; // Current device #ifdef DEBUG char temp[1024]; // Temporary address string #endif // DEBUG static const int DeviceTypeOID[] = { // Device Type OID 1,3,6,1,2,1,25,3,2,1,2,1,-1 }; // Create an array to track SNMP devices... devices = cupsArrayNew3((cups_array_func_t)pappl_snmp_compare_devices, NULL, NULL, 0, NULL, (cups_afree_func_t)pappl_snmp_free); // Open SNMP socket... if ((snmp_sock = _papplSNMPOpen(AF_INET)) < 0) { _papplDeviceError(err_cb, err_data, "Unable to open SNMP socket."); goto finished; } // Get the list of network interface broadcast addresses... if ((addrs = pappl_snmp_get_interface_addresses()) == NULL) { _papplDeviceError(err_cb, err_data, "Unable to get SNMP broadcast addresses."); goto finished; } // Send queries to every broadcast address... for (addr = addrs; addr; addr = addr->next) { _PAPPL_DEBUG("pappl_snmp_find: Sending SNMP device type get request to '%s'.\n", httpAddrString(&(addr->addr), temp, sizeof(temp))); _papplSNMPWrite(snmp_sock, &(addr->addr), _PAPPL_SNMP_VERSION_1, _PAPPL_SNMP_COMMUNITY, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_TYPE, DeviceTypeOID); } // Free broadcast addresses (all done with them...) httpAddrFreeList(addrs); // Wait up to 30 seconds to discover printers via SNMP... FD_ZERO(&input); for (endtime = time(NULL) + 30, last_count = 0; time(NULL) < endtime;) { // Wait up to 2 seconds for more data... timeout.tv_sec = 2; timeout.tv_usec = 0; FD_SET(snmp_sock, &input); _PAPPL_DEBUG("Running select() for %d.\n", snmp_sock); if (select(snmp_sock + 1, &input, NULL, NULL, &timeout) < 0) { _papplDeviceError(err_cb, err_data, "SNMP select() failed with error: %s", strerror(errno)); break; } if (FD_ISSET(snmp_sock, &input)) { _PAPPL_DEBUG("pappl_snmp_find: Reading SNMP response.\n"); pappl_snmp_read_response(devices, snmp_sock, err_cb, err_data); } else { if (last_count == cupsArrayCount(devices)) break; last_count = cupsArrayCount(devices); _PAPPL_DEBUG("pappl_snmp_find: timeout=%d, last_count = %d\n", (int)(endtime - time(NULL)), last_count); } } _PAPPL_DEBUG("pappl_snmp_find: timeout=%d, last_count = %d\n", (int)(endtime - time(NULL)), last_count); // Report all of the devices we found... for (cur_device = (_pappl_snmp_dev_t *)cupsArrayFirst(devices); cur_device; cur_device = (_pappl_snmp_dev_t *)cupsArrayNext(devices)) { char info[256]; // Device description int num_did; // Number of device ID keys/values cups_option_t *did; // Device ID keys/values const char *make, // Manufacturer *model; // Model name // Skip LPD (port 515) and IPP (port 631) since they can't be raw sockets... if (cur_device->port == 515 || cur_device->port == 631 || !cur_device->uri) continue; num_did = papplDeviceParseID(cur_device->device_id, &did); if ((make = cupsGetOption("MANUFACTURER", num_did, did)) == NULL) if ((make = cupsGetOption("MFG", num_did, did)) == NULL) if ((make = cupsGetOption("MFGR", num_did, did)) == NULL) make = "Unknown"; if ((model = cupsGetOption("MODEL", num_did, did)) == NULL) if ((model = cupsGetOption("MDL", num_did, did)) == NULL) model = "Printer"; if (!strcmp(make, "HP") && !strncmp(model, "HP ", 3)) snprintf(info, sizeof(info), "%s (Network Printer %s)", model, cur_device->uri + 7); else snprintf(info, sizeof(info), "%s %s (Network Printer %s)", make, model, cur_device->uri + 7); if ((*cb)(info, cur_device->uri, cur_device->device_id, data)) { // Save the address and port... char address_str[256]; // IP address as a string sock->host = strdup(httpAddrString(&cur_device->address, address_str, sizeof(address_str))); sock->port = cur_device->port; ret = true; break; } } // Clean up and return... finished: cupsArrayDelete(devices); _papplSNMPClose(snmp_sock); return (ret); } // // 'pappl_snmp_free()' - Free the memory used for SNMP device. // static void pappl_snmp_free(_pappl_snmp_dev_t *d) // I - SNMP device { // Free all memory... free(d->addrname); free(d->device_id); free(d->uri); free(d); } // // 'pappl_snmp_get_interface_addresses()' - Get interface broadcast addresses. // static http_addrlist_t * // O - List of addresses pappl_snmp_get_interface_addresses(void) { struct ifaddrs *addrs, // Interface address list *addr; // Current interface address http_addrlist_t *first, // First address in list *last, // Last address in list *current; // Current address // Get a list of network interfaces... if (getifaddrs(&addrs) < 0) { // Unable to get the list... return (NULL); } // Copy the broadcast addresses into a list of addresses... for (addr = addrs, first = NULL, last = NULL; addr; addr = addr->ifa_next) { if ((addr->ifa_flags & IFF_BROADCAST) && addr->ifa_broadaddr && addr->ifa_broadaddr->sa_family == AF_INET) { // Copy this IPv4 broadcast address... if ((current = calloc(1, sizeof(http_addrlist_t))) != NULL) { memcpy(&(current->addr), addr->ifa_broadaddr, sizeof(struct sockaddr_in)); if (!last) first = current; else last->next = current; last = current; } } } // Free the original interface addresses and return... freeifaddrs(addrs); return (first); } // // 'pappl_snmp_list()' - List SNMP printers. // static bool // O - `true` if found, `false` otherwise pappl_snmp_list( pappl_device_cb_t cb, // I - Device callback void *data, // I - Device callback data pappl_deverror_cb_t err_cb, // I - Error callback void *err_data) // I - Error callback data { _pappl_socket_t sock; // Socket data bool ret; // Return value memset(&sock, 0, sizeof(sock)); ret = pappl_snmp_find(cb, data, &sock, err_cb, err_data); free(sock.host); return (ret); } // // 'pappl_snmp_open_cb()' - Look for a matching device URI. // static bool // O - `true` on match, `false` otherwise pappl_snmp_open_cb( const char *device_info, // I - Device description const char *device_uri, // I - This device's URI const char *device_id, // I - IEEE-1284 Device ID void *data) // I - URI we are looking for { bool match = !strcmp(device_uri, (const char *)data); // Does this match? _PAPPL_DEBUG("pappl_snmp_open_cb(device_info=\"%s\", device_uri=\"%s\", device_id=\"%s\", user_data=\"%s\") = %s\n", device_info, device_uri, device_id, (char *)data, match ? "true" : "false"); return (match); } // // 'pappl_snmp_read_response()' - Read and parse a SNMP response. // static void pappl_snmp_read_response( cups_array_t *devices, // Devices array int fd, // I - SNMP socket file descriptor pappl_deverror_cb_t err_cb, // I - Error callback void *err_data) // I - Data for error callback { int i; // Looping variable _pappl_snmp_t packet; // Decoded packet _pappl_snmp_dev_t *device, // Matching device *temp; // New device entry char addrname[256]; // Source address name static const int DevicePrinterOID[] = { 1,3,6,1,2,1,25,3,1,5,-1 }; // Host MIB OID for "printer" type static const int SysNameOID[] = { 1,3,6,1,2,1,1,5,0,-1 }; // Host MIB sysName OID static const int HPDeviceIDOID[] = { 1,3,6,1,4,1,11,2,3,9,1,1,7,0,-1 }; // HP MIB IEEE-1284 Device ID OID static const int LexmarkDeviceIdOID[] = { 1,3,6,1,4,1,641,2,1,2,1,3,1,-1 }; // Lexmark MIB IEEE-1284 Device ID OID static const int LexmarkPortOID[] = { 1,3,6,1,4,1,641,1,5,7,11,0,-1 }; // Lexmark MIB raw socket port number OID static const int ZebraDeviceIDOID[] = { 1,3,6,1,4,1,10642,1,3,0,-1 }; // Zebra MIB IEEE-1284 Device ID OID static const int ZebraPortOID[] = { 1,3,6,1,4,1,10642,20,10,20,15,2,1,10,1,-1 }; // Zebra MIB raw socket port number OID static const int PWGPPMDeviceIdOID[] = { 1,3,6,1,4,1,2699,1,2,1,2,1,1,3,1,-1 }; // PWG Printer Port Monitor MIB IEEE-1284 Device ID OID static const int PWGPPMPortOID[] = { 1,3,6,1,4,1,2699,1,2,1,3,1,1,6,1,1,-1 }; // PWG Printer Port Monitor MIB raw socket port number OID static const int RawTCPPortOID[] = { 1,3,6,1,4,1,683,6,3,1,4,17,0,-1 }; // Extended Networks MIB (common) raw socket port number OID // Read the response data if (!_papplSNMPRead(fd, &packet, -1.0)) { _papplDeviceError(err_cb, err_data, "Unable to read SNMP response data: %s", strerror(errno)); return; } httpAddrString(&(packet.address), addrname, sizeof(addrname)); // Look for the response status code in the SNMP message header if (packet.error) { _papplDeviceError(err_cb, err_data, "Bad SNMP packet from '%s': %s", addrname, packet.error); return; } _PAPPL_DEBUG("pappl_snmp_read_response: community=\"%s\"\n", packet.community); _PAPPL_DEBUG("pappl_snmp_read_response: request-id=%u\n", packet.request_id); _PAPPL_DEBUG("pappl_snmp_read_response: error-status=%d\n", packet.error_status); if (packet.error_status && packet.request_id != _PAPPL_SNMP_QUERY_DEVICE_TYPE) return; // Find a matching device in the cache for (device = (_pappl_snmp_dev_t *)cupsArrayFirst(devices); device; device = (_pappl_snmp_dev_t *)cupsArrayNext(devices)) { if (!strcmp(device->addrname, addrname)) break; } // Process the message switch (packet.request_id) { case _PAPPL_SNMP_QUERY_DEVICE_TYPE: if (device) { _PAPPL_DEBUG("pappl_snmp_read_response: Discarding duplicate device type for \"%s\".\n", addrname); return; } for (i = 0; DevicePrinterOID[i] >= 0; i ++) { if (DevicePrinterOID[i] != packet.object_value.oid[i]) { _PAPPL_DEBUG("pappl_snmp_read_response: Discarding device (not printer).\n"); return; } } if (packet.object_value.oid[i] >= 0) { _PAPPL_DEBUG("pappl_snmp_read_response: Discarding device (not printer).\n"); return; } // Add the device and request the device data if ((temp = calloc(1, sizeof(_pappl_snmp_dev_t))) == NULL) { _PAPPL_DEBUG("pappl_snmp_read_response: Unable to allocate memory for device.\n"); return; } temp->address = packet.address; temp->addrname = strdup(addrname); temp->port = 9100; // Default port to use if (!temp->addrname) { _PAPPL_DEBUG("pappl_snmp_read_response: Unable to allocate memory for device name.\n"); free(temp); return; } cupsArrayAdd(devices, temp); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_SYSNAME, SysNameOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_ID, HPDeviceIDOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_ID, LexmarkDeviceIdOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_ID, PWGPPMDeviceIdOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_ID, ZebraDeviceIDOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_PORT, LexmarkPortOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_PORT, ZebraPortOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_PORT, PWGPPMPortOID); _papplSNMPWrite(fd, &(packet.address), _PAPPL_SNMP_VERSION_1, packet.community, _PAPPL_ASN1_GET_REQUEST, _PAPPL_SNMP_QUERY_DEVICE_PORT, RawTCPPortOID); break; case _PAPPL_SNMP_QUERY_DEVICE_ID: if (device && packet.object_type == _PAPPL_ASN1_OCTET_STRING && (!device->device_id || strlen(device->device_id) < packet.object_value.string.num_bytes)) { char *ptr; // Pointer into device ID for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++) { if (*ptr == '\n') // A lot of bad printers put a newline *ptr = ';'; } free(device->device_id); device->device_id = strdup((char *)packet.object_value.string.bytes); } break; case _PAPPL_SNMP_QUERY_DEVICE_SYSNAME: if (device && packet.object_type == _PAPPL_ASN1_OCTET_STRING && !device->uri) { char uri[2048]; // Device URI snprintf(uri, sizeof(uri), "snmp://%s", (char *)packet.object_value.string.bytes); device->uri = strdup(uri); } break; case _PAPPL_SNMP_QUERY_DEVICE_PORT: if (device) { if (packet.object_type == _PAPPL_ASN1_INTEGER) { device->port = packet.object_value.integer; } else if (packet.object_type == _PAPPL_ASN1_OCTET_STRING) { char *end; // End of string device->port = (int)strtol((char *)packet.object_value.string.bytes, &end, 10); if (errno == ERANGE || *end) device->port = 0; } } break; } } // // 'pappl_socket_close()' - Close a network socket. // static void pappl_socket_close( pappl_device_t *device) // I - Device { _pappl_socket_t *sock; // Socket device if ((sock = papplDeviceGetData(device)) == NULL) return; close(sock->fd); httpAddrFreeList(sock->list); free(sock); papplDeviceSetData(device, NULL); } // // 'pappl_socket_getid()' - Get the current IEEE-1284 device ID via SNMP. // static char * // O - Device ID or `NULL` on error pappl_socket_getid( pappl_device_t *device, // I - Device char *buffer, // I - Buffer size_t bufsize) // I - Size of buffer { // TODO: Implement network query of IEEE-1284 device ID (Issue #95) (void)device; (void)buffer; (void)bufsize; return (NULL); } // // 'pappl_socket_open()' - Open a network socket. // static bool // O - `true` on success, `false` on failure pappl_socket_open( pappl_device_t *device, // I - Device const char *device_uri, // I - Device URI const char *job_name) // I - Job name { _pappl_socket_t *sock; // Socket device char scheme[32], // URI scheme userpass[32], // Username/password (not used) host[256], // Host name or make resource[256], // Resource path, if any *options; // Pointer to options, if any int port; // Port number char port_str[32]; // String for port number (void)job_name; // Allocate memory for the socket... if ((sock = (_pappl_socket_t *)calloc(1, sizeof(_pappl_socket_t))) == NULL) { papplDeviceError(device, "Unable to allocate memory for socket device: %s", strerror(errno)); return (false); } // Split apart the URI... httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)); if ((options = strchr(resource, '?')) != NULL) *options++ = '\0'; if (!strcmp(scheme, "dnssd")) { // DNS-SD discovered device #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) int i; // Looping var char srvname[256], // Service name *type, // Service type *domain; // Domain _pappl_dns_sd_t master; // DNS-SD context # ifdef HAVE_DNSSD int error; // Error code, if any DNSServiceRef resolver; // Resolver # else AvahiServiceResolver *resolver; // Resolver # endif // HAVE_DNSSD if ((domain = strstr(host, "._tcp.")) != NULL) { // Truncate host at domain portion... domain += 5; *domain++ = '\0'; // Then separate the service type portion... type = strstr(host, "._"); *type ++ = '\0'; // Unescape the service name... pappl_dnssd_unescape(srvname, host, sizeof(srvname)); master = _papplDNSSDInit(NULL); # ifdef HAVE_DNSSD resolver = master; if ((error = DNSServiceResolve(&resolver, kDNSServiceFlagsShareConnection, 0, srvname, type, domain, (DNSServiceResolveReply)pappl_dnssd_resolve_cb, sock)) != kDNSServiceErr_NoError) { papplDeviceError(device, "Unable to resolve '%s': %s", device_uri, _papplDNSSDStrError(error)); goto error; } # else _papplDNSSDLock(); if ((resolver = avahi_service_resolver_new(master, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, srvname, type, domain, AVAHI_PROTO_UNSPEC, 0, (AvahiServiceResolverCallback)pappl_dnssd_resolve_cb, sock)) == NULL) { papplDeviceError(device, "Unable to resolve '%s'.", device_uri); _papplDNSSDUnlock(); goto error; } _papplDNSSDUnlock(); # endif // HAVE_DNSSD // Wait up to 30 seconds for the resolve to complete... for (i = 0; i < 30000 && !sock->host; i ++) usleep(1000); # ifdef HAVE_DNSSD DNSServiceRefDeallocate(resolver); # else _papplDNSSDLock(); avahi_service_resolver_free(resolver); _papplDNSSDUnlock(); # endif // HAVE_DNSSD if (!sock->host) { papplDeviceError(device, "Unable to resolve '%s'.", device_uri); goto error; } } #endif // HAVE_DNSSD || HAVE_AVAHI } else if (!strcmp(scheme, "snmp")) { // SNMP discovered device if (!pappl_snmp_find(pappl_snmp_open_cb, (void *)device_uri, sock, NULL, NULL)) goto error; } else if (!strcmp(scheme, "socket")) { // Raw socket (JetDirect or similar) sock->host = strdup(host); sock->port = port; } // Lookup the address of the printer... snprintf(port_str, sizeof(port_str), "%d", sock->port); if ((sock->list = httpAddrGetList(sock->host, AF_UNSPEC, port_str)) == NULL) { papplDeviceError(device, "Unable to lookup '%s:%d': %s", sock->host, sock->port, cupsLastErrorString()); goto error; } sock->fd = -1; httpAddrConnect2(sock->list, &sock->fd, 30000, NULL); if (sock->fd < 0) { papplDeviceError(device, "Unable to connect to '%s:%d': %s", sock->host, sock->port, cupsLastErrorString()); goto error; } papplDeviceSetData(device, sock); _PAPPL_DEBUG("Connection successful, device fd = %d\n", sock->fd); return (true); // If we get here there was an error... error: free(sock->host); httpAddrFreeList(sock->list); free(sock); return (false); } // // 'pappl_socket_read()' - Read from a network socket. // static ssize_t // O - Number of bytes read pappl_socket_read( pappl_device_t *device, // I - Device void *buffer, // I - Buffer to read into size_t bytes) // I - Bytes to read { _pappl_socket_t *sock; // Socket device ssize_t count; // Total bytes read struct pollfd data; // poll() data int nfds; // poll() return value if ((sock = papplDeviceGetData(device)) == NULL) return (-1); // Only read if we have data to read within 100ms... data.fd = sock->fd; data.events = POLLIN; data.revents = 0; while ((nfds = poll(&data, 1, 10000)) < 0) { if (errno != EINTR && errno != EAGAIN) break; } if (nfds < 1 || !(data.revents & POLLIN)) return (-1); // Read data from the socket, protecting against signals and busy kernels... while ((count = read(sock->fd, buffer, bytes)) < 0) { if (errno != EINTR && errno != EAGAIN) break; } return (count); } // // 'pappl_socket_status()' - Get the current network device status. // static pappl_preason_t // O - New "printer-state-reasons" values pappl_socket_status( pappl_device_t *device) // I - Device { (void)device; return (PAPPL_PREASON_NONE); } // // 'pappl_socket_write()' - Write to a network socket. // static ssize_t // O - Number of bytes written pappl_socket_write( pappl_device_t *device, // I - Device const void *buffer, // I - Write buffer size_t bytes) // I - Bytes to write { _pappl_socket_t *sock; // Socket device ssize_t count, // Total bytes written written; // Bytes written this time const char *ptr; // Pointer into buffer if ((sock = papplDeviceGetData(device)) == NULL) return (-1); for (count = 0, ptr = (const char *)buffer; count < (ssize_t)bytes; count += written, ptr += written) { if ((written = write(sock->fd, ptr, bytes - (size_t)count)) < 0) { if (errno == EINTR || errno == EAGAIN) { written = 0; continue; } count = -1; break; } } return (count); } pappl-1.0.3/pappl/device-private.h000066400000000000000000000034031403603036100170170ustar00rootroot00000000000000// // Private device communication functions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_DEVICE_PRIVATE_H_ # define _PAPPL_DEVICE_PRIVATE_H_ // // Include necessary headers... // # include "base-private.h" # include "device.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Constants... // #define PAPPL_DEVICE_BUFSIZE 8192 // Size of write buffer // // Types... // struct _pappl_device_s // Device connection data { pappl_devclose_cb_t close_cb; // Close callback pappl_deverror_cb_t error_cb; // Error callback pappl_devid_cb_t id_cb; // IEEE-1284 device ID callback pappl_devread_cb_t read_cb; // Read callback pappl_devstatus_cb_t status_cb; // Status callback pappl_devwrite_cb_t write_cb; // Write callback void *device_data, // Data pointer for device *error_data; // Data pointer for error callback char buffer[PAPPL_DEVICE_BUFSIZE]; // Write buffer size_t bufused; // Number of bytes in write buffer pappl_devmetrics_t metrics; // Device metrics }; typedef void (*_pappl_devscheme_cb_t)(const char *scheme, void *data); // // Functions... // extern void _papplDeviceAddFileScheme(void) _PAPPL_PRIVATE; extern void _papplDeviceAddNetworkSchemes(void) _PAPPL_PRIVATE; extern void _papplDeviceAddSupportedSchemes(ipp_t *attrs); extern void _papplDeviceAddUSBScheme(void) _PAPPL_PRIVATE; extern void _papplDeviceError(pappl_deverror_cb_t err_cb, void *err_data, const char *message, ...) _PAPPL_FORMAT(3,4) _PAPPL_PRIVATE; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_DEVICE_H_ pappl-1.0.3/pappl/device-usb.c000066400000000000000000000551331403603036100161400ustar00rootroot00000000000000// // USB device support code for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2007-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // //#define DEBUG 1 #include "device-private.h" #include "printer.h" #ifdef HAVE_LIBUSB # include #endif // HAVE_LIBUSB // // Local types... // #ifdef HAVE_LIBUSB typedef struct _pappl_usb_dev_s // USB device data { struct libusb_device *device; // Device info struct libusb_device_handle *handle; // Open handle to device int conf, // Configuration origconf, // Original configuration iface, // Interface ifacenum, // Interface number altset, // Alternate setting write_endp, // Write endpoint read_endp, // Read endpoint protocol; // Protocol: 1 = Uni-di, 2 = Bi-di. } _pappl_usb_dev_t; #endif // HAVE_LIBUSB // // Local functions... // #ifdef HAVE_LIBUSB static void pappl_usb_close(pappl_device_t *device); static bool pappl_usb_find(pappl_device_cb_t cb, void *data, _pappl_usb_dev_t *device, pappl_deverror_cb_t err_cb, void *err_data); static char *pappl_usb_getid(pappl_device_t *device, char *buffer, size_t bufsize); static bool pappl_usb_list(pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data); static bool pappl_usb_open(pappl_device_t *device, const char *device_uri, const char *name); static bool pappl_usb_open_cb(const char *device_info, const char *device_uri, const char *device_id, void *data); static ssize_t pappl_usb_read(pappl_device_t *device, void *buffer, size_t bytes); static pappl_preason_t pappl_usb_status(pappl_device_t *device); static ssize_t pappl_usb_write(pappl_device_t *device, const void *buffer, size_t bytes); #endif // HAVE_LIBUSB // // '_papplDeviceAddUSBScheme()' - Add the USB scheme. // void _papplDeviceAddUSBScheme(void) { #ifdef HAVE_LIBUSB papplDeviceAddScheme("usb", PAPPL_DEVTYPE_USB, pappl_usb_list, pappl_usb_open, pappl_usb_close, pappl_usb_read, pappl_usb_write, pappl_usb_status, pappl_usb_getid); #endif // HAVE_LIBUSB } #ifdef HAVE_LIBUSB // // 'pappl_usb_close()' - Close a USB device. // static void pappl_usb_close(pappl_device_t *device) // I - Device { _pappl_usb_dev_t *usb = (_pappl_usb_dev_t *)papplDeviceGetData(device); // USB device data libusb_close(usb->handle); libusb_unref_device(usb->device); free(usb); papplDeviceSetData(device, NULL); } // // 'pappl_usb_find()' - Find a USB printer. // static bool // O - `true` if found, `false` if not pappl_usb_find( pappl_device_cb_t cb, // I - Callback function void *data, // I - User data pointer _pappl_usb_dev_t *device, // O - USB device info pappl_deverror_cb_t err_cb, // I - Error callback void *err_data) // I - Error callback data { ssize_t err = 0, // Current error i, // Looping var num_udevs; // Number of USB devices libusb_device **udevs; // USB devices /* * Get the list of connected USB devices... */ device->device = NULL; device->handle = NULL; if ((err = libusb_init(NULL)) != 0) { _papplDeviceError(err_cb, err_data, "Unable to initialize USB access: %s", libusb_strerror((enum libusb_error)err)); return (false); } num_udevs = libusb_get_device_list(NULL, &udevs); _PAPPL_DEBUG("pappl_usb_find: num_udevs=%d\n", (int)num_udevs); // Find the printers and do the callback until we find a match. for (i = 0; i < num_udevs; i ++) { libusb_device *udevice = udevs[i]; // Current device char device_id[1024], // Current device ID device_info[256], // Current device description device_uri[1024]; // Current device URI struct libusb_device_descriptor devdesc; // Current device descriptor struct libusb_config_descriptor *confptr = NULL; // Pointer to current configuration const struct libusb_interface *ifaceptr = NULL; // Pointer to current interface const struct libusb_interface_descriptor *altptr = NULL; // Pointer to current alternate setting const struct libusb_endpoint_descriptor *endpptr = NULL; // Pointer to current endpoint uint8_t conf, // Current configuration iface, // Current interface altset, // Current alternate setting endp, // Current endpoint read_endp, // Current read endpoint write_endp; // Current write endpoint // Ignore devices with no configuration data and anything that is not // a printer... if (libusb_get_device_descriptor(udevice, &devdesc) < 0) { _PAPPL_DEBUG("pappl_usb_find: udev%d - no descriptor.\n", (int)i); continue; } _PAPPL_DEBUG("pappl_usb_find: udev%d -\n", (int)i); _PAPPL_DEBUG("pappl_usb_find: bLength=%d\n", devdesc.bLength); _PAPPL_DEBUG("pappl_usb_find: bDescriptorType=%d\n", devdesc.bDescriptorType); _PAPPL_DEBUG("pappl_usb_find: bcdUSB=%04x\n", devdesc.bcdUSB); _PAPPL_DEBUG("pappl_usb_find: bDeviceClass=%d\n", devdesc.bDeviceClass); _PAPPL_DEBUG("pappl_usb_find: bDeviceSubClass=%d\n", devdesc.bDeviceSubClass); _PAPPL_DEBUG("pappl_usb_find: bDeviceProtocol=%d\n", devdesc.bDeviceProtocol); _PAPPL_DEBUG("pappl_usb_find: bMaxPacketSize0=%d\n", devdesc.bMaxPacketSize0); _PAPPL_DEBUG("pappl_usb_find: idVendor=0x%04x\n", devdesc.idVendor); _PAPPL_DEBUG("pappl_usb_find: idProduct=0x%04x\n", devdesc.idProduct); _PAPPL_DEBUG("pappl_usb_find: bcdDevice=%04x\n", devdesc.bcdDevice); _PAPPL_DEBUG("pappl_usb_find: iManufacturer=%d\n", devdesc.iManufacturer); _PAPPL_DEBUG("pappl_usb_find: iProduct=%d\n", devdesc.iProduct); _PAPPL_DEBUG("pappl_usb_find: iSerialNumber=%d\n", devdesc.iSerialNumber); _PAPPL_DEBUG("pappl_usb_find: bNumConfigurations=%d\n", devdesc.bNumConfigurations); if (!devdesc.bNumConfigurations || !devdesc.idVendor || !devdesc.idProduct) continue; if (devdesc.idVendor == 0x05ac) continue; // Skip Apple devices... device->device = udevice; device->handle = NULL; device->conf = -1; device->origconf = -1; device->iface = -1; device->ifacenum = -1; device->altset = -1; device->write_endp = -1; device->read_endp = -1; device->protocol = 0; for (conf = 0; conf < devdesc.bNumConfigurations; conf ++) { if (libusb_get_config_descriptor(udevice, conf, &confptr) < 0) { _PAPPL_DEBUG("pappl_usb_find: conf%d - no descriptor\n", conf); continue; } _PAPPL_DEBUG("pappl_usb_find: conf%d -\n", conf); _PAPPL_DEBUG("pappl_usb_find: bLength=%d\n", confptr->bLength); _PAPPL_DEBUG("pappl_usb_find: bDescriptorType=%d\n", confptr->bDescriptorType); _PAPPL_DEBUG("pappl_usb_find: wTotalLength=%d\n", confptr->wTotalLength); _PAPPL_DEBUG("pappl_usb_find: bNumInterfaces=%d\n", confptr->bNumInterfaces); _PAPPL_DEBUG("pappl_usb_find: bConfigurationValue=%d\n", confptr->bConfigurationValue); _PAPPL_DEBUG("pappl_usb_find: iConfiguration=%d\n", confptr->iConfiguration); _PAPPL_DEBUG("pappl_usb_find: bmAttributes=%d\n", confptr->bmAttributes); _PAPPL_DEBUG("pappl_usb_find: MaxPower=%d\n", confptr->MaxPower); _PAPPL_DEBUG("pappl_usb_find: interface=%p\n", confptr->interface); _PAPPL_DEBUG("pappl_usb_find: extra=%p\n", confptr->extra); _PAPPL_DEBUG("pappl_usb_find: extra_length=%d\n", confptr->extra_length); // Some printers offer multiple interfaces... for (iface = 0, ifaceptr = confptr->interface; iface < confptr->bNumInterfaces; iface ++, ifaceptr ++) { if (!ifaceptr->altsetting) { _PAPPL_DEBUG("pappl_usb_find: iface%d - no alternate setting\n", iface); continue; } _PAPPL_DEBUG("pappl_usb_find: iface%d -\n", iface); _PAPPL_DEBUG("pappl_usb_find: num_altsetting=%d\n", ifaceptr->num_altsetting); _PAPPL_DEBUG("pappl_usb_find: altsetting=%p\n", ifaceptr->altsetting); device->protocol = 0; for (altset = 0, altptr = ifaceptr->altsetting; (int)altset < ifaceptr->num_altsetting; altset ++, altptr ++) { _PAPPL_DEBUG("pappl_usb_find: altset%d - bInterfaceClass=%d, bInterfaceSubClass=%d, bInterfaceProtocol=%d\n", altset, altptr->bInterfaceClass, altptr->bInterfaceSubClass, altptr->bInterfaceProtocol); if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER || altptr->bInterfaceSubClass != 1) continue; if (altptr->bInterfaceProtocol != 1 && altptr->bInterfaceProtocol != 2) continue; if (altptr->bInterfaceProtocol < device->protocol || altptr->bInterfaceProtocol > 2) continue; read_endp = 0xff; write_endp = 0xff; for (endp = 0, endpptr = altptr->endpoint; endp < altptr->bNumEndpoints; endp ++, endpptr ++) { if ((endpptr->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) { if (endpptr->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) read_endp = endp; else write_endp = endp; } } if (write_endp != 0xff) { // Save the best match so far... device->protocol = altptr->bInterfaceProtocol; device->altset = altptr->bAlternateSetting; device->ifacenum = altptr->bInterfaceNumber; device->write_endp = write_endp; if (device->protocol > 1) device->read_endp = read_endp; } } _PAPPL_DEBUG("pappl_usb_find: device->protocol=%d\n", device->protocol); if (device->protocol > 0) { device->conf = conf; device->iface = iface; if (!libusb_open(udevice, &device->handle)) { uint8_t current; // Current configuration // Opened the device, try to set the configuration... if (libusb_control_transfer(device->handle, LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE, 8, /* GET_CONFIGURATION */ 0, 0, (unsigned char *)¤t, 1, 5000) < 0) current = 0; if (confptr->bConfigurationValue != current) { // Select the configuration we want... if (libusb_set_configuration(device->handle, confptr->bConfigurationValue) < 0) { libusb_close(device->handle); device->handle = NULL; } } #ifdef __linux if (device->handle) { // Make sure the old, busted usblp kernel driver is not loaded... if (libusb_kernel_driver_active(device->handle, device->iface) == 1) { if ((err = libusb_detach_kernel_driver(device->handle, device->iface)) < 0 && err != LIBUSB_ERROR_NOT_FOUND) { _papplDeviceError(err_cb, err_data, "Unable to detach usblp kernel driver for USB printer %04x:%04x: %s", devdesc.idVendor, devdesc.idProduct, libusb_strerror((enum libusb_error)err)); libusb_close(device->handle); device->handle = NULL; } } } #endif // __linux if (device->handle) { // Claim the interface... if ((err = libusb_claim_interface(device->handle, device->ifacenum)) < 0) { _papplDeviceError(err_cb, err_data, "Unable to claim USB interface: %s", libusb_strerror((enum libusb_error)err)); libusb_close(device->handle); device->handle = NULL; } } if (device->handle && ifaceptr->num_altsetting > 1) { // Set the alternate setting as needed... if ((err = libusb_set_interface_alt_setting(device->handle, device->ifacenum, device->altset)) < 0) { _papplDeviceError(err_cb, err_data, "Unable to set alternate USB interface: %s", libusb_strerror((enum libusb_error)err)); libusb_close(device->handle); device->handle = NULL; } } if (device->handle) { // Get the 1284 Device ID... if ((err = libusb_control_transfer(device->handle, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE, 0, (uint16_t)device->conf, (uint16_t)((device->iface << 8) | device->altset), (unsigned char *)device_id, sizeof(device_id), 5000)) < 0) { _papplDeviceError(err_cb, err_data, "Unable to get IEEE-1284 device ID: %s", libusb_strerror((enum libusb_error)err)); device_id[0] = '\0'; libusb_close(device->handle); device->handle = NULL; } else { int length = ((device_id[0] & 255) << 8) | (device_id[1] & 255); if (length < 14 || length > (int)sizeof(device_id)) length = ((device_id[1] & 255) << 8) | (device_id[0] & 255); if (length > (int)sizeof(device_id)) length = (int)sizeof(device_id); length -= 2; memmove(device_id, device_id + 2, (size_t)length); device_id[length] = '\0'; _PAPPL_DEBUG("pappl_usb_find: device_id=\"%s\"\n", device_id); } } if (device->handle) { // Build the device URI... const char *make, // Pointer to make *model, // Pointer to model *serial = NULL; // Pointer to serial number char *ptr, // Pointer into device ID copy_did[1024], // Copy of device ID temp[256]; // Temporary string for serial # strlcpy(copy_did, device_id, sizeof(copy_did)); if ((make = strstr(copy_did, "MANUFACTURER:")) != NULL) make += 13; else if ((make = strstr(copy_did, "MFG:")) != NULL) make += 4; if ((model = strstr(copy_did, "MODEL:")) != NULL) model += 6; else if ((model = strstr(copy_did, "MDL:")) != NULL) model += 4; if ((serial = strstr(copy_did, "SERIALNUMBER:")) != NULL) serial += 12; else if ((serial = strstr(copy_did, "SERN:")) != NULL) serial += 5; else if ((serial = strstr(copy_did, "SN:")) != NULL) serial += 3; if (serial) { if ((ptr = strchr(serial, ';')) != NULL) *ptr = '\0'; } else { int length = libusb_get_string_descriptor_ascii(device->handle, devdesc.iSerialNumber, (unsigned char *)temp, sizeof(temp) - 1); if (length > 0) { temp[length] = '\0'; serial = temp; } } if (make) { if ((ptr = strchr(make, ';')) != NULL) *ptr = '\0'; } else make = "Unknown"; if (model) { if ((ptr = strchr(model, ';')) != NULL) *ptr = '\0'; } else model = "Unknown"; if (serial) httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "usb", NULL, make, 0, "/%s?serial=%s", model, serial); else httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "usb", NULL, make, 0, "/%s", model); if (!strcmp(make, "HP") && !strncmp(model, "HP ", 3)) snprintf(device_info, sizeof(device_info), "%s (USB)", model); else snprintf(device_info, sizeof(device_info), "%s %s (USB)", make, model); if ((*cb)(device_info, device_uri, device_id, data)) { _PAPPL_DEBUG("pappl_usb_find: Found a match.\n"); libusb_ref_device(device->device); if (device->read_endp != -1) device->read_endp = confptr->interface[device->iface].altsetting[device->altset].endpoint[device->read_endp].bEndpointAddress; if (device->write_endp != -1) device->write_endp = confptr->interface[device->iface].altsetting[device->altset].endpoint[device->write_endp].bEndpointAddress; goto match_found; } libusb_close(device->handle); device->handle = NULL; } } } } // iface loop libusb_free_config_descriptor(confptr); } // conf loop } match_found: _PAPPL_DEBUG("pappl_usb_find: device->handle=%p\n", device->handle); // Clean up .... if (num_udevs >= 0) libusb_free_device_list(udevs, 1); return (device->handle != NULL); } // // 'pappl_usb_getid()' - Get the current IEEE-1284 device ID. // static char * // O - Device ID or `NULL` on error pappl_usb_getid( pappl_device_t *device, // I - Device char *buffer, // I - Buffer size_t bufsize) // I - Size of buffer { _pappl_usb_dev_t *usb = (_pappl_usb_dev_t *)papplDeviceGetData(device); // USB device data size_t length; // Length of device ID int error; // USB transfer error _PAPPL_DEBUG("pappl_usb_getid(device=%p, buffer=%p, bufsize=%ld) usb->conf=%d, ->iface=%d, ->altset=%d\n", device, buffer, (long)bufsize, usb->conf, usb->iface, usb->altset); // Get the 1284 Device ID... if ((error = libusb_control_transfer(usb->handle, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE, 0, (uint16_t)usb->conf, (uint16_t)((usb->iface << 8) | usb->altset), (unsigned char *)buffer, (uint16_t)bufsize, 5000)) < 0) { papplDeviceError(device, "Unable to get IEEE-1284 device ID from USB port: %s", libusb_strerror((enum libusb_error)error)); buffer[0] = '\0'; return (NULL); } // Nul-terminate length = (size_t)(((buffer[0] & 255) << 8) | (buffer[1] & 255)); if (length < 14 || length > bufsize) // Some printers do it wrong (LSB)... length = (size_t)(((buffer[1] & 255) << 8) | (buffer[0] & 255)); if (length > bufsize) length = bufsize; length -= 2; memmove(buffer, buffer + 2, length); buffer[length] = '\0'; return (buffer); } // // 'pappl_usb_list()' - List USB devices. // static bool // O - `true` if found, `false` if not pappl_usb_list( pappl_device_cb_t cb, // I - Callback function void *data, // I - User data pointer pappl_deverror_cb_t err_cb, // I - Error callback void *err_data) // I - Error callback data { _pappl_usb_dev_t usb; // USB device bool ret; // Return value ret = pappl_usb_find(cb, data, &usb, err_cb, err_data); if (usb.handle) { libusb_close(usb.handle); libusb_unref_device(usb.device); } return (ret); } // // 'pappl_usb_open()' - Open a USB device. // static bool // `true` on success, `false` on error pappl_usb_open( pappl_device_t *device, // I - Device const char *device_uri, // I - Device URI const char *job_name) // I - Job name (unused) { _pappl_usb_dev_t *usb; // USB device (void)job_name; if ((usb = (_pappl_usb_dev_t *)calloc(1, sizeof(_pappl_usb_dev_t))) == NULL) { papplDeviceError(device, "Unable to allocate memory for USB device: %s", strerror(errno)); return (false); } if (!pappl_usb_find(pappl_usb_open_cb, (void *)device_uri, usb, device->error_cb, device->error_data)) { free(usb); return (false); } papplDeviceSetData(device, usb); return (true); } // // 'pappl_usb_open_cb()' - Look for a matching device URI. // static bool // O - `true` on match, `false` otherwise pappl_usb_open_cb( const char *device_info, // I - Description of device const char *device_uri, // I - This device's URI const char *device_id, // I - IEEE-1284 Device ID void *data) // I - URI we are looking for { bool match = !strcmp(device_uri, (const char *)data); // Does this match? (void)device_info; (void)device_id; _PAPPL_DEBUG("pappl_usb_open_cb(device_info=\"%s\", device_uri=\"%s\", device_id=\"%s\", user_data=\"%s\") returning %s.\n", device_info, device_uri, device_id, (char *)data, match ? "true" : "false"); return (match); } #endif // HAVE_LIBUSB // // 'pappl_usb_read()' - Read data from a USB device. // static ssize_t // O - Bytes read pappl_usb_read(pappl_device_t *device, // I - Device void *buffer, // I - Read buffer size_t bytes) // I - Bytes to read { _pappl_usb_dev_t *usb = (_pappl_usb_dev_t *)papplDeviceGetData(device); // USB device data int icount; // Bytes that were read int error; // USB transfer error if (usb->read_endp < 0) return (-1); // No read endpoint! if ((error = libusb_bulk_transfer(usb->handle, (unsigned char)usb->read_endp, buffer, (int)bytes, &icount, 10000)) < 0) { papplDeviceError(device, "Unable to read from USB port: %s", libusb_strerror((enum libusb_error)error)); return (-1); } else return ((ssize_t)icount); } // // 'pappl_usb_status()' - Get the USB printer status. // static pappl_preason_t // O - IPP "printer-state-reasons" values pappl_usb_status(pappl_device_t *device)// I - Device { _pappl_usb_dev_t *usb = (_pappl_usb_dev_t *)papplDeviceGetData(device); // USB device data pappl_preason_t status = PAPPL_PREASON_NONE; // IPP "printer-state-reasons" values unsigned char port_status = 0x08; // Centronics port status byte int error; // USB transfer error if ((error = libusb_control_transfer(usb->handle, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE, 1, 0, (uint16_t)(usb->iface << 8), &port_status, 1, 0)) < 0) { papplDeviceError(device, "Unable to get USB port status: %s", libusb_strerror((enum libusb_error)error)); } else { if (!(port_status & 0x08)) status |= PAPPL_PREASON_OTHER; if (port_status & 0x20) status |= PAPPL_PREASON_MEDIA_EMPTY; if (port_status & 0x40) // Vendor extension status |= PAPPL_PREASON_MEDIA_JAM; if (port_status & 0x80) // Vendor extension status |= PAPPL_PREASON_COVER_OPEN; } return (status); } // // 'pappl_usb_write()' - Write data to a USB device. // static ssize_t // O - Bytes written pappl_usb_write(pappl_device_t *device, // I - Device const void *buffer, // I - Write buffer size_t bytes) // I - Bytes to write { _pappl_usb_dev_t *usb = (_pappl_usb_dev_t *)papplDeviceGetData(device); // USB device data int icount; // Bytes that were written int error; // USB transfer error if ((error = libusb_bulk_transfer(usb->handle, (unsigned char)usb->write_endp, (unsigned char *)buffer, (int)bytes, &icount, 0)) < 0) { papplDeviceError(device, "Unable to write %d bytes to USB port: %s", (int)bytes, libusb_strerror((enum libusb_error)error)); return (-1); } else return ((ssize_t)icount); } pappl-1.0.3/pappl/device.c000066400000000000000000000575771403603036100153670ustar00rootroot00000000000000// // Common device support code for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2007-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "device-private.h" #include "printer.h" #include // // Types... // typedef struct _pappl_devscheme_s // Device scheme data { char *scheme; // URI scheme pappl_devtype_t dtype; // Device type pappl_devlist_cb_t list_cb; // List devices callback, if any pappl_devopen_cb_t open_cb; // Open callback pappl_devclose_cb_t close_cb; // Close callback pappl_devread_cb_t read_cb; // Read callback pappl_devwrite_cb_t write_cb; // Write callback pappl_devid_cb_t id_cb; // IEEE-1284 device ID callback, if any pappl_devstatus_cb_t status_cb; // Status callback, if any } _pappl_devscheme_t; // // Local globals... // static pthread_rwlock_t device_rwlock = PTHREAD_RWLOCK_INITIALIZER; // Reader/writer lock for device schemes static cups_array_t *device_schemes = NULL; // Array of device schemes // // Local functions... // static int pappl_compare_schemes(_pappl_devscheme_t *a, _pappl_devscheme_t *b); static void pappl_default_error_cb(const char *message, void *data); static ssize_t pappl_write(pappl_device_t *device, const void *buffer, size_t bytes); // // 'papplDeviceAddScheme()' - Add a device URI scheme. // // This function registers a device URI scheme with PAPPL, so that devices using // the named scheme can receive print data, report status information, and so // forth. PAPPL includes support for the following URI schemes: // // - `dnssd`: Network printers discovered using DNS-SD. // - `file`: Character device files, plain files, and directories. // - `snmp`: Network printers discovered using SNMPv1. // - `socket`: Network printers using a hostname or numeric IP address. // - `usb`: Class 1 (unidirectional) or 2 (bidirectional) USB printers. // // The "scheme" parameter specifies the URI scheme and must consist of lowercase // letters, digits, "-", "_", and/or ".", for example "x-foo" or // "com.example.bar". // // The "dtype" parameter specifies the device type and should be // `PAPPL_DTYPE_CUSTOM_LOCAL` for locally connected printers and // `PAPPL_DTYPE_CUSTOM_NETWORK` for network printers. // // Each of the callbacks corresponds to one of the `papplDevice` functions: // // - "list_cb": Implements discovery of devices (optional) // - "open_cb": Opens communication with a device and allocates any device- // specific data as needed // - "close_cb": Closes communication with a device and frees any device- // specific data as needed // - "read_cb": Reads data from a device // - "write_cb": Write data to a device // - "status_cb": Gets basic printer state information from a device (optional) // - "id_cb": Gets the current IEEE-1284 device ID from a device (optional) // // The "open_cb" callback typically calls @link papplDeviceSetData@ to store a // pointer to contextual information for the connection while the "close_cb", // "id_cb", "read_cb", "write_cb", and "status_cb" callbacks typically call // @link papplDeviceGetData@ to retrieve it. // void papplDeviceAddScheme( const char *scheme, // I - URI scheme pappl_devtype_t dtype, // I - Device type (`PAPPL_DEVTYPE_CUSTOM_LOCAL` or `PAPPL_DEVTYPE_CUSTOM_NETWORK`) pappl_devlist_cb_t list_cb, // I - List devices callback, if any pappl_devopen_cb_t open_cb, // I - Open callback pappl_devclose_cb_t close_cb, // I - Close callback pappl_devread_cb_t read_cb, // I - Read callback pappl_devwrite_cb_t write_cb, // I - Write callback pappl_devstatus_cb_t status_cb, // I - Status callback, if any pappl_devid_cb_t id_cb) // I - IEEE-1284 device ID callback, if any { _pappl_devscheme_t *ds, // Device URI scheme data dkey; // Search key pthread_rwlock_wrlock(&device_rwlock); // Create the schemes array as needed... if (!device_schemes) { if ((device_schemes = cupsArrayNew((cups_array_func_t)pappl_compare_schemes, NULL)) == NULL) { pthread_rwlock_unlock(&device_rwlock); return; } } dkey.scheme = (char *)scheme; if (cupsArrayFind(device_schemes, &dkey)) { pthread_rwlock_unlock(&device_rwlock); return; } // Add the scheme... if ((ds = (_pappl_devscheme_t *)calloc(1, sizeof(_pappl_devscheme_t))) != NULL) { if ((ds->scheme = strdup(scheme)) != NULL) { ds->dtype = dtype; ds->list_cb = list_cb; ds->open_cb = open_cb; ds->close_cb = close_cb; ds->read_cb = read_cb; ds->write_cb = write_cb; ds->status_cb = status_cb; ds->id_cb = id_cb; cupsArrayAdd(device_schemes, ds); } else { free(ds); } } pthread_rwlock_unlock(&device_rwlock); } // // '_papplDeviceAddSupportedSchemes()' - Add the available URI schemes. // void _papplDeviceAddSupportedSchemes( ipp_t *attrs) // I - Attributes { int i; // Looping var ipp_attribute_t *attr; // IPP attribute _pappl_devscheme_t *devscheme; // Current device scheme pthread_rwlock_rdlock(&device_rwlock); attr = ippAddStrings(attrs, IPP_TAG_SYSTEM, IPP_TAG_URISCHEME, "smi2699-device-uri-schemes-supported", cupsArrayCount(device_schemes), NULL, NULL); for (i = 0, devscheme = (_pappl_devscheme_t *)cupsArrayFirst(device_schemes); devscheme; i ++, devscheme = (_pappl_devscheme_t *)cupsArrayNext(device_schemes)) ippSetString(attrs, &attr, i, devscheme->scheme); pthread_rwlock_unlock(&device_rwlock); } // // 'papplDeviceClose()' - Close a device connection. // // This function flushes any pending write data and closes the connection to a // device. // void papplDeviceClose( pappl_device_t *device) // I - Device to close { if (device) { if (device->bufused > 0) pappl_write(device, device->buffer, device->bufused); (device->close_cb)(device); free(device); } } // // '_papplDeviceError()' - Report an error. // void _papplDeviceError( pappl_deverror_cb_t err_cb, // I - Error callback void *err_data, // I - Error callback data const char *message, // I - Printf-style message ...) // I - Additional args as needed { va_list ap; // Pointer to additional args char buffer[8192]; // Formatted message if (!err_cb) return; va_start(ap, message); vsnprintf(buffer, sizeof(buffer), message, ap); va_end(ap); (*err_cb)(buffer, err_data); } // // 'papplDeviceError()' - Report an error on a device. // // This function reports an error on a device using the client-supplied callback // function. It is normally called from any custom device URI scheme callbacks // you implement. // void papplDeviceError( pappl_device_t *device, // I - Device const char *message, // I - Printf-style error message ...) // I - Additional arguments as needed { va_list ap; // Pointer to additional args char buffer[8192]; // Formatted message if (!device || !device->error_cb) return; va_start(ap, message); vsnprintf(buffer, sizeof(buffer), message, ap); va_end(ap); (device->error_cb)(buffer, device->error_data); } // // 'papplDeviceFlush()' - Flush any buffered data to the device. // // This function flushes any pending write data sent using the // @link papplDevicePrintf@, @link papplDevicePuts@, or @link papplDeviceWrite@ // functions to the device. // void papplDeviceFlush(pappl_device_t *device)// I - Device { if (device && device->bufused > 0) { pappl_write(device, device->buffer, device->bufused); device->bufused = 0; } } // // 'papplDeviceGetData()' - Get device-specific data. // // This function returns any device-specific data that has been set by the // device open callback. It is normally only called from any custom device URI // scheme callbacks you implement. // void * // O - Device data pointer papplDeviceGetData( pappl_device_t *device) // I - Device { return (device ? device->device_data : NULL); } // // 'papplDeviceGetID()' - Get the IEEE-1284 device ID. // // This function queries the IEEE-1284 device ID from the device and copies it // to the provided buffer. The buffer must be at least 64 bytes and should be // at least 1024 bytes in length. // // > *Note:* This function can block for up to several seconds depending on // > the type of connection. // char * // O - IEEE-1284 device ID or `NULL` on failure papplDeviceGetID( pappl_device_t *device, // I - Device char *buffer, // I - Buffer for IEEE-1284 device ID size_t bufsize) // I - Size of buffer { struct timeval starttime, // Start time endtime; // End time char *ret; // Return value // Range check input... if (buffer) *buffer = '\0'; if (!device || !device->id_cb || !buffer || bufsize < 64) return (NULL); // Get the device ID and collect timing metrics... gettimeofday(&starttime, NULL); ret = (device->id_cb)(device, buffer, bufsize); gettimeofday(&endtime, NULL); device->metrics.status_requests ++; device->metrics.status_msecs += (size_t)(1000 * (endtime.tv_sec - starttime.tv_sec) + (endtime.tv_usec - starttime.tv_usec) / 1000); // Return the device ID return (ret); } // // 'papplDeviceGetMetrics()' - Get the device metrics. // // This function returns a copy of the device metrics data, which includes the // number, length (in bytes), and duration (in milliseconds) of read, status, // and write requests for the current session. This information is normally // used for performance measurement and optimization during development of a // printer application. It can also be useful diagnostic information. // pappl_devmetrics_t * // O - Metrics data papplDeviceGetMetrics( pappl_device_t *device, // I - Device pappl_devmetrics_t *metrics) // I - Buffer for metrics data { if (device && metrics) memcpy(metrics, &device->metrics, sizeof(pappl_devmetrics_t)); else if (metrics) memset(metrics, 0, sizeof(pappl_devmetrics_t)); return (metrics); } // // 'papplDeviceGetDeviceStatus()' - Get the printer status bits. // // This function returns the current printer status bits, as applicable to the // current device. // // The status bits for USB devices come from the original Centronics parallel // printer "standard" which was later formally standardized in IEEE 1284-1984 // and the USB Device Class Definition for Printing Devices. Some vendor // extensions are also supported. // // The status bits for network devices come from the hrPrinterDetectedErrorState // property that is defined in the SNMP Printer MIB v2 (RFC 3805). // // This function returns a @link pappl_preason_t@ bitfield which can be // passed to the @link papplPrinterSetReasons@ function. Use the // @link PAPPL_PREASON_DEVICE_STATUS@ value as the value of the "remove" // argument. // // > Note: This function can block for several seconds while getting the status // > information. // pappl_preason_t // O - IPP "printer-state-reasons" values papplDeviceGetStatus( pappl_device_t *device) // I - Device { struct timeval starttime, // Start time endtime; // End time pappl_preason_t status = PAPPL_PREASON_NONE; // IPP "printer-state-reasons" values if (device) { gettimeofday(&starttime, NULL); if (device->status_cb) status = (device->status_cb)(device); gettimeofday(&endtime, NULL); device->metrics.status_requests ++; device->metrics.status_msecs += (size_t)(1000 * (endtime.tv_sec - starttime.tv_sec) + (endtime.tv_usec - starttime.tv_usec) / 1000); } return (status); } // // 'papplDeviceIsSupported()' - Determine whether a given URI is supported. // // This function determines whether a given URI or URI scheme is supported as // a device. // bool // O - `true` if supported, `false` otherwise papplDeviceIsSupported( const char *uri) // I - URI { char scheme[32], // Device scheme userpass[32], // Device user/pass host[256], // Device host resource[256]; // Device resource int port; // Device port _pappl_devscheme_t key, // Device search key *match; // Matching key // Separate out the components of the URI... if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) return (false); // Files are OK if the resource path is writable... if (!strcmp(scheme, "file")) { char *options = strchr(resource, '?'); // Device options, if any if (options) *options = '\0'; // Strip options before writability test return (!access(resource, W_OK)); } // Make sure schemes are added... if (!device_schemes) { _papplDeviceAddFileScheme(); _papplDeviceAddNetworkSchemes(); _papplDeviceAddUSBScheme(); } // Otherwise try to lookup the URI scheme... pthread_rwlock_rdlock(&device_rwlock); key.scheme = scheme; match = (_pappl_devscheme_t *)cupsArrayFind(device_schemes, &key); pthread_rwlock_unlock(&device_rwlock); return (match != NULL); } // // 'papplDeviceList()' - List available devices. // // This function lists the available devices, calling the "cb" function once per // device that is discovered/listed. The callback function receives the device // URI, IEEE-1284 device ID (if any), and "data" pointer, and returns `true` to // stop listing devices and `false` to continue. // // The "types" argument determines which devices are listed, for example // `PAPPL_DEVTYPE_ALL` will list all types of devices while `PAPPL_DEVTYPE_USB` only // lists USB printers. // // Any errors are reported using the supplied "err_cb" function. If you specify // `NULL` for this argument, errors are sent to `stderr`. // // > Note: This function will block (not return) until each of the device URI // > schemes has reported all of the devices *or* the supplied callback function // > returns `true`. // bool // O - `true` if the callback returned `true`, `false` otherwise papplDeviceList( pappl_devtype_t types, // I - Device types pappl_device_cb_t cb, // I - Callback function void *data, // I - User data for callback pappl_deverror_cb_t err_cb, // I - Error callback or `NULL` for default void *err_data) // I - Data for error callback { bool ret = false; // Return value _pappl_devscheme_t *ds; // Current device scheme if (!device_schemes) { _papplDeviceAddFileScheme(); _papplDeviceAddNetworkSchemes(); _papplDeviceAddUSBScheme(); } pthread_rwlock_rdlock(&device_rwlock); if (!err_cb) err_cb = pappl_default_error_cb; for (ds = (_pappl_devscheme_t *)cupsArrayFirst(device_schemes); ds && !ret; ds = (_pappl_devscheme_t *)cupsArrayNext(device_schemes)) { if ((types & ds->dtype) && ds->list_cb) ret = (ds->list_cb)(cb, data, err_cb, err_data); } pthread_rwlock_unlock(&device_rwlock); return (ret); } // // 'papplDeviceOpen()' - Open a connection to a device. // // This function opens a connection to the specified device URI. The "name" // argument provides textual context for the connection and is usually the name // (title) of the print job. // // Any errors are reported using the supplied "err_cb" function. If you specify // `NULL` for this argument, errors are sent to `stderr`. // pappl_device_t * // O - Device connection or `NULL` on error papplDeviceOpen( const char *device_uri, // I - Device URI const char *name, // I - Job name pappl_deverror_cb_t err_cb, // I - Error callback or `NULL` for default void *err_data) // I - Data for error callback { _pappl_devscheme_t *ds, // Scheme dkey; // Search key pappl_device_t *device; // Device structure char scheme[32], // URI scheme userpass[32], // Username/password (not used) host[256], // Host name or make resource[256], // Resource path, if any *options; // Pointer to options, if any int port; // Port number http_uri_status_t status; // URI status if (!device_uri) { _papplDeviceError(err_cb, err_data, "Bad NULL device URI."); return (NULL); } if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource))) < HTTP_URI_STATUS_OK) { _papplDeviceError(err_cb, err_data, "Bad device URI '%s': %s", device_uri, httpURIStatusString(status)); return (NULL); } if ((options = strchr(resource, '?')) != NULL) *options++ = '\0'; if (!device_schemes) { _papplDeviceAddFileScheme(); _papplDeviceAddNetworkSchemes(); _papplDeviceAddUSBScheme(); } pthread_rwlock_rdlock(&device_rwlock); dkey.scheme = scheme; ds = (_pappl_devscheme_t *)cupsArrayFind(device_schemes, &dkey); pthread_rwlock_unlock(&device_rwlock); if (!ds) { _papplDeviceError(err_cb, err_data, "Unsupported device URI scheme '%s'.", scheme); return (NULL); } if ((device = calloc(1, sizeof(pappl_device_t))) == NULL) { _papplDeviceError(err_cb, err_data, "Unable to allocate memory for device: %s", strerror(errno)); return (NULL); } device->close_cb = ds->close_cb; device->error_cb = err_cb ? err_cb : pappl_default_error_cb; device->error_data = err_data; device->id_cb = ds->id_cb; device->read_cb = ds->read_cb; device->status_cb = ds->status_cb; device->write_cb = ds->write_cb; if (!(ds->open_cb)(device, device_uri, name)) { free(device); return (NULL); } return (device); } // // 'papplDeviceParseID()' - Parse an IEEE-1284 device ID string. // // This function parses an IEEE-1284 device ID string and returns an array of // key/value pairs as a `cups_option_t` array. The returned array must be // freed using the `cupsFreeOptions` function. // int // O - Number of key/value pairs papplDeviceParseID( const char *device_id, // I - IEEE-1284 device ID string cups_option_t **pairs) // O - Key/value pairs { int num_pairs = 0; // Number of key/value pairs char name[256], // Key name value[256], // Value *ptr; // Pointer into key/value // Range check input... if (pairs) *pairs = NULL; if (!device_id || !pairs) return (0); // Scan the IEEE-1284 device ID string... while (*device_id) { // Skip leading whitespace... while (*device_id && isspace(*device_id)) device_id ++; if (!*device_id) break; // Get the key name... for (ptr = name; *device_id && *device_id != ':'; device_id ++) { if (ptr < (name + sizeof(name) - 1)) *ptr++ = *device_id; } *ptr = '\0'; if (*device_id != ':') break; device_id ++; // Skip leading whitespace in value... while (*device_id && isspace(*device_id)) device_id ++; for (ptr = value; *device_id && *device_id != ';'; device_id ++) { if (ptr < (value + sizeof(value) - 1)) *ptr++ = *device_id; } *ptr = '\0'; if (*device_id == ';') device_id ++; num_pairs = cupsAddOption(name, value, num_pairs, pairs); } return (num_pairs); } // // 'papplDevicePrintf()' - Write a formatted string. // // This function buffers a formatted string that will be sent to the device. // The "format" argument accepts all `printf` format specifiers and behaves // identically to that function. // // Call the @link papplDeviceFlush@ function to ensure that the formatted string // is immediately sent to the device. // ssize_t // O - Number of characters or -1 on error papplDevicePrintf( pappl_device_t *device, // I - Device const char *format, // I - Printf-style format string ...) // I - Additional args as needed { va_list ap; // Pointer to additional args char buffer[8192]; // Output buffer va_start(ap, format); vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); return (papplDeviceWrite(device, buffer, strlen(buffer))); } // // 'papplDevicePuts()' - Write a literal string. // // This function buffers a literal string that will be sent to the device. // Call the @link papplDeviceFlush@ function to ensure that the literal string // is immediately sent to the device. // ssize_t // O - Number of characters or -1 on error papplDevicePuts( pappl_device_t *device, // I - Device const char *s) // I - Literal string { return (papplDeviceWrite(device, s, strlen(s))); } // // 'papplDeviceRead()' - Read from a device. // // This function reads data from the device. Depending on the device, this // function may block indefinitely. // ssize_t // O - Number of bytes read or -1 on error papplDeviceRead( pappl_device_t *device, // I - Device void *buffer, // I - Read buffer size_t bytes) // I - Max bytes to read { struct timeval starttime, // Start time endtime; // End time ssize_t count; // Bytes read this time if (!device || !device->read_cb) return (-1); // Make sure any pending IO is flushed... if (device->bufused > 0) papplDeviceFlush(device); gettimeofday(&starttime, NULL); count = (device->read_cb)(device, buffer, bytes); gettimeofday(&endtime, NULL); device->metrics.read_requests ++; device->metrics.read_msecs += (size_t)(1000 * (endtime.tv_sec - starttime.tv_sec) + (endtime.tv_usec - starttime.tv_usec) / 1000); if (count > 0) device->metrics.read_bytes += (size_t)count; return (count); } // // 'papplDeviceSetData()' - Set device-specific data. // // This function sets any device-specific data needed to communicate with the // device. It is normally only called from the open callback that was // registered for the device URI scheme. // void papplDeviceSetData( pappl_device_t *device, // I - Device void *data) // I - Device data pointer { if (device) device->device_data = data; } // // 'papplDeviceWrite()' - Write to a device. // // This function buffers data that will be sent to the device. Call the // @link papplDeviceFlush@ function to ensure that the data is immediately sent // to the device. // ssize_t // O - Number of bytes written or -1 on error papplDeviceWrite( pappl_device_t *device, // I - Device const void *buffer, // I - Write buffer size_t bytes) // I - Number of bytes to write { if (!device) return (-1); if ((device->bufused + bytes) > sizeof(device->buffer)) { // Flush the write buffer... if (pappl_write(device, device->buffer, device->bufused) < 0) return (-1); device->bufused = 0; } if (bytes < sizeof(device->buffer)) { memcpy(device->buffer + device->bufused, buffer, bytes); device->bufused += bytes; return ((ssize_t)bytes); } return (pappl_write(device, buffer, bytes)); } // // 'pappl_compare_schemes()' - Compare two device URI schemes. // static int // O - Result of comparison pappl_compare_schemes( _pappl_devscheme_t *a, // I - First URI scheme _pappl_devscheme_t *b) // I - Second URI scheme { return (strcmp(a->scheme, b->scheme)); } // // 'pappl_default_error_cb()' - Send device errors to stderr. // static void pappl_default_error_cb( const char *message, // I - Error message void *data) // I - Callback data (unused) { (void)data; fprintf(stderr, "%s\n", message); } // // 'pappl_write()' - Write data to the device. // static ssize_t // O - Number of bytes written or `-1` on error pappl_write(pappl_device_t *device, // I - Device const void *buffer, // I - Buffer size_t bytes) // I - Bytes to write { struct timeval starttime, // Start time endtime; // End time ssize_t count; // Total bytes written gettimeofday(&starttime, NULL); count = (device->write_cb)(device, buffer, bytes); gettimeofday(&endtime, NULL); device->metrics.write_requests ++; device->metrics.write_msecs += (size_t)(1000 * (endtime.tv_sec - starttime.tv_sec) + (endtime.tv_usec - starttime.tv_usec) / 1000); if (count > 0) device->metrics.write_bytes += (size_t)count; return (count); } pappl-1.0.3/pappl/device.h000066400000000000000000000116061403603036100153530ustar00rootroot00000000000000// // Device communication functions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_DEVICE_H_ # define _PAPPL_DEVICE_H_ // // Include necessary headers... // # include "base.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Types... // typedef struct pappl_devmetrics_s // Device metrics { size_t read_bytes; // Total number of bytes read size_t read_requests; // Total number of read requests size_t read_msecs; // Total number of milliseconds spent reading size_t status_requests; // Total number of status requests size_t status_msecs; // Total number of milliseconds spent getting status size_t write_bytes; // Total number of bytes written size_t write_requests; // Total number of write requests size_t write_msecs; // Total number of milliseconds spent writing } pappl_devmetrics_t; enum pappl_devtype_e // Device type bit values { PAPPL_DEVTYPE_FILE = 0x01, // Local file/directory PAPPL_DEVTYPE_USB = 0x02, // USB printers PAPPL_DEVTYPE_SERIAL = 0x04, // Serial printers (not currently implemented) @private@ PAPPL_DEVTYPE_CUSTOM_LOCAL = 0x08, // Local printer using a custom interface or protocol PAPPL_DEVTYPE_SOCKET = 0x10, // Network printers using raw socket PAPPL_DEVTYPE_DNS_SD = 0x20, // Network printers discovered via DNS-SD/mDNS PAPPL_DEVTYPE_SNMP = 0x40, // Network printers discovered via SNMP PAPPL_DEVTYPE_CUSTOM_NETWORK = 0x80, // Network printer using a custom interface or protocol PAPPL_DEVTYPE_LOCAL = 0x0f, // All local printers PAPPL_DEVTYPE_NETWORK = 0xf0, // All network printers PAPPL_DEVTYPE_ALL = 0xff // All printers }; typedef unsigned pappl_devtype_t; // Device type bitfield typedef bool (*pappl_device_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data); // Device callback - return `true` to stop, `false` to continue typedef void (*pappl_devclose_cb_t)(pappl_device_t *device); // Device close callback typedef void (*pappl_deverror_cb_t)(const char *message, void *err_data); // Device error callback typedef char *(*pappl_devid_cb_t)(pappl_device_t *device, char *buffer, size_t bufsize); // Device ID callback typedef bool (*pappl_devlist_cb_t)(pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data); // Device list callback typedef bool (*pappl_devopen_cb_t)(pappl_device_t *device, const char *device_uri, const char *name); // Device open callback typedef ssize_t (*pappl_devread_cb_t)(pappl_device_t *device, void *buffer, size_t bytes); // Device read callback typedef pappl_preason_t (*pappl_devstatus_cb_t)(pappl_device_t *device); // Device status callback typedef ssize_t (*pappl_devwrite_cb_t)(pappl_device_t *device, const void *buffer, size_t bytes); // Device write callback // // Functions... // extern void papplDeviceAddScheme(const char *scheme, pappl_devtype_t dtype, pappl_devlist_cb_t list_cb, pappl_devopen_cb_t open_cb, pappl_devclose_cb_t close_cb, pappl_devread_cb_t read_cb, pappl_devwrite_cb_t write_cb, pappl_devstatus_cb_t status_cb, pappl_devid_cb_t id_cb) _PAPPL_PUBLIC; extern void papplDeviceClose(pappl_device_t *device) _PAPPL_PUBLIC; extern void papplDeviceError(pappl_device_t *device, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(2,3); extern void papplDeviceFlush(pappl_device_t *device) _PAPPL_PUBLIC; extern void *papplDeviceGetData(pappl_device_t *device) _PAPPL_PUBLIC; extern char *papplDeviceGetID(pappl_device_t *device, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern pappl_devmetrics_t *papplDeviceGetMetrics(pappl_device_t *device, pappl_devmetrics_t *metrics) _PAPPL_PUBLIC; extern pappl_preason_t papplDeviceGetStatus(pappl_device_t *device) _PAPPL_PUBLIC; extern bool papplDeviceIsSupported(const char *uri) _PAPPL_PUBLIC; extern bool papplDeviceList(pappl_devtype_t types, pappl_device_cb_t cb, void *data, pappl_deverror_cb_t err_cb, void *err_data) _PAPPL_PUBLIC; extern pappl_device_t *papplDeviceOpen(const char *device_uri, const char *name, pappl_deverror_cb_t err_cb, void *err_data) _PAPPL_PUBLIC; extern int papplDeviceParseID(const char *device_id, cups_option_t **pairs) _PAPPL_PUBLIC; extern ssize_t papplDevicePrintf(pappl_device_t *device, const char *format, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(2, 3); extern ssize_t papplDevicePuts(pappl_device_t *device, const char *s) _PAPPL_PUBLIC; extern ssize_t papplDeviceRead(pappl_device_t *device, void *buffer, size_t bytes) _PAPPL_PUBLIC; extern void papplDeviceSetData(pappl_device_t *device, void *data) _PAPPL_PUBLIC; extern ssize_t papplDeviceWrite(pappl_device_t *device, const void *buffer, size_t bytes) _PAPPL_PUBLIC; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_DEVICE_H_ pappl-1.0.3/pappl/dnssd-private.h000066400000000000000000000033131403603036100166730ustar00rootroot00000000000000// // Private DNS-SD header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_DNSSD_PRIVATE_H_ # define _PAPPL_DNSSD_PRIVATE_H_ // // Include necessary headers... // # include "base-private.h" # ifdef HAVE_DNSSD # include # elif defined(HAVE_AVAHI) # include # include # include # include # include # include # endif // HAVE_DNSSD // // Types and structures... // # ifdef HAVE_DNSSD typedef DNSServiceRef _pappl_srv_t; // DNS-SD service reference typedef TXTRecordRef _pappl_txt_t; // DNS-SD TXT record typedef DNSServiceRef _pappl_dns_sd_t; // DNS-SD master reference #elif defined(HAVE_AVAHI) typedef AvahiEntryGroup *_pappl_srv_t; // DNS-SD service reference typedef AvahiStringList *_pappl_txt_t; // DNS-SD TXT record typedef AvahiClient *_pappl_dns_sd_t; // DNS-SD master reference #else typedef void *_pappl_srv_t; // DNS-SD service reference typedef void *_pappl_txt_t; // DNS-SD TXT record typedef void *_pappl_dns_sd_t; // DNS-SD master reference #endif // HAVE_DNSSD // // Functions... // extern int _papplDNSSDGetHostChanges(void) _PAPPL_PRIVATE; extern _pappl_dns_sd_t _papplDNSSDInit(pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplDNSSDLock(void) _PAPPL_PRIVATE; extern const char *_papplDNSSDStrError(int error) _PAPPL_PRIVATE; extern void _papplDNSSDUnlock(void) _PAPPL_PRIVATE; #endif // !_PAPPL_DNSSD_PRIVATE_H_ pappl-1.0.3/pappl/dnssd.c000066400000000000000000001317731403603036100152320ustar00rootroot00000000000000// // DNS-SD support for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Constants... // #ifdef HAVE_AVAHI # define AVAHI_DNS_TYPE_LOC 29 // Per RFC 1876 #endif // HAVE_AVAHI // // Local globals... // static int pappl_dns_sd_host_name_changes = 0; // Number of host name changes/collisions static _pappl_dns_sd_t pappl_dns_sd_master = NULL; // DNS-SD master reference static pthread_mutex_t pappl_dns_sd_mutex = PTHREAD_MUTEX_INITIALIZER; // DNS-SD master mutex #ifdef HAVE_AVAHI static AvahiThreadedPoll *pappl_dns_sd_poll = NULL; // Avahi background thread #endif // HAVE_AVAHI // // Local functions... // static void dns_sd_geo_to_loc(const char *geo, unsigned char loc[16]); #ifdef HAVE_DNSSD static void DNSSD_API dns_sd_printer_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, pappl_printer_t *printer); static void *dns_sd_run(void *data); static void DNSSD_API dns_sd_system_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, pappl_system_t *system); #elif defined(HAVE_AVAHI) static void dns_sd_client_cb(AvahiClient *c, AvahiClientState state, void *data); static void dns_sd_printer_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, pappl_printer_t *printer); static void dns_sd_system_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, pappl_system_t *system); #endif // HAVE_DNSSD // // '_papplDNSSDGetHostChanges()' - Get the number of host name changes/collisions so far. // int // O - Number of host name changes/collisions _papplDNSSDGetHostChanges(void) { return (pappl_dns_sd_host_name_changes); } // // '_papplDNSSDInit()' - Initialize DNS-SD services. // _pappl_dns_sd_t // O - DNS-SD master reference _papplDNSSDInit( pappl_system_t *system) // I - System { #ifdef HAVE_DNSSD int error; // Error code, if any pthread_t tid; // Thread ID pthread_mutex_lock(&pappl_dns_sd_mutex); if (pappl_dns_sd_master) { pthread_mutex_unlock(&pappl_dns_sd_mutex); return (pappl_dns_sd_master); } if ((error = DNSServiceCreateConnection(&pappl_dns_sd_master)) == kDNSServiceErr_NoError) { if (pthread_create(&tid, NULL, dns_sd_run, system)) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create DNS-SD thread: %s", strerror(errno)); DNSServiceRefDeallocate(pappl_dns_sd_master); pappl_dns_sd_master = NULL; } else pthread_detach(tid); } else { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to initialize DNS-SD: %s", _papplDNSSDStrError(error)); DNSServiceRefDeallocate(pappl_dns_sd_master); pappl_dns_sd_master = NULL; } pthread_mutex_unlock(&pappl_dns_sd_mutex); #elif defined(HAVE_AVAHI) int error; // Error code, if any pthread_mutex_lock(&pappl_dns_sd_mutex); if (pappl_dns_sd_master) { pthread_mutex_unlock(&pappl_dns_sd_mutex); return (pappl_dns_sd_master); } if ((pappl_dns_sd_poll = avahi_threaded_poll_new()) == NULL) { // Unable to create the background thread... papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to initialize DNS-SD thread: %s", strerror(errno)); } else if ((pappl_dns_sd_master = avahi_client_new(avahi_threaded_poll_get(pappl_dns_sd_poll), AVAHI_CLIENT_NO_FAIL, (AvahiClientCallback)dns_sd_client_cb, system, &error)) == NULL) { // Unable to create the client... papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to initialize DNS-SD: %s", _papplDNSSDStrError(error)); avahi_threaded_poll_free(pappl_dns_sd_poll); pappl_dns_sd_poll = NULL; } else { // Start the background thread... avahi_threaded_poll_start(pappl_dns_sd_poll); } pthread_mutex_unlock(&pappl_dns_sd_mutex); #endif // HAVE_DNSSD return (pappl_dns_sd_master); } // // '_papplDNSSDLock()' - Grab a lock to make DNS-SD changes. // void _papplDNSSDLock(void) { #ifdef HAVE_AVAHI if (pappl_dns_sd_poll) avahi_threaded_poll_lock(pappl_dns_sd_poll); #endif // HAVE_AVAHI } // // '_papplDNSSDStrError()' - Return a string for the given DNS-SD error code. // const char * // O - Error message _papplDNSSDStrError(int error) // I - Error code { #ifdef HAVE_DNSSD switch (error) { case kDNSServiceErr_NoError : return ("No error"); case kDNSServiceErr_Unknown : default : return ("Unknown error"); case kDNSServiceErr_NoSuchName : return ("Name not found"); case kDNSServiceErr_NoMemory : return ("Out of memory"); case kDNSServiceErr_BadParam : return ("Bad parameter"); case kDNSServiceErr_BadReference : return ("Bad service reference"); case kDNSServiceErr_BadState : return ("Bad state"); case kDNSServiceErr_BadFlags : return ("Bad flags argument"); case kDNSServiceErr_Unsupported : return ("Unsupported feature"); case kDNSServiceErr_NotInitialized : return ("Not initialized"); case kDNSServiceErr_AlreadyRegistered : return ("Name already registered"); case kDNSServiceErr_NameConflict : return ("Name conflicts"); case kDNSServiceErr_Invalid : return ("Invalid argument"); case kDNSServiceErr_Firewall : return ("Firewall prevents access"); case kDNSServiceErr_Incompatible : return ("Client library incompatible with background daemon"); case kDNSServiceErr_BadInterfaceIndex : return ("Bad interface index"); case kDNSServiceErr_Refused : return ("Connection refused"); case kDNSServiceErr_NoSuchRecord : return ("DNS record not found"); case kDNSServiceErr_NoAuth : return ("No authoritative answer"); case kDNSServiceErr_NoSuchKey : return ("TXT record key not found"); case kDNSServiceErr_NATTraversal : return ("Unable to traverse via NAT"); case kDNSServiceErr_DoubleNAT : return ("Double NAT is in use"); case kDNSServiceErr_BadTime : return ("Bad time value"); case kDNSServiceErr_BadSig : return ("Bad signal"); case kDNSServiceErr_BadKey : return ("Bad TXT record key"); case kDNSServiceErr_Transient : return ("Transient error"); case kDNSServiceErr_ServiceNotRunning : return ("Background daemon not running"); case kDNSServiceErr_NATPortMappingUnsupported : return ("NAT doesn't support PCP, NAT-PMP or UPnP"); case kDNSServiceErr_NATPortMappingDisabled : return ("NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator"); case kDNSServiceErr_NoRouter : return ("No router configured, probably no network connectivity"); case kDNSServiceErr_PollingMode : return ("Polling error"); case kDNSServiceErr_Timeout : return ("Timeout"); case kDNSServiceErr_DefunctConnection : return ("Connection lost"); } #elif defined(HAVE_AVAHI) return (avahi_strerror(error)); #else return (""); #endif // HAVE_DNSSD } // // '_papplDNSSDUnlock()' - Release a lock after making DNS-SD changes. // void _papplDNSSDUnlock(void) { #ifdef HAVE_AVAHI if (pappl_dns_sd_poll) avahi_threaded_poll_unlock(pappl_dns_sd_poll); #endif // HAVE_AVAHI } // // '_papplPrinterRegisterDNSSDNoLock()' - Register a printer's DNS-SD service. // bool // O - `true` on success, `false` on failure _papplPrinterRegisterDNSSDNoLock( pappl_printer_t *printer) // I - Printer { #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) bool ret = true; // Return value pappl_system_t *system = printer->system; // System _pappl_txt_t txt; // DNS-SD TXT record int i, // Looping var count; // Number of values ipp_attribute_t *color_supported, *document_format_supported, *printer_kind, *printer_uuid, *urf_supported; // Printer attributes const char *value; // Value string char adminurl[246], // Admin URL formats[252], // List of supported formats kind[251], // List of printer-kind values urf[252], // List of supported URF values *ptr; // Pointer into string char regtype[256]; // DNS-SD service type char product[248]; // Make and model (legacy) int max_width; // Maximum media width (legacy) const char *papermax; // PaperMax string value (legacy) # ifdef HAVE_DNSSD DNSServiceErrorType error; // Error from mDNSResponder # else int error; // Error from Avahi char fullname[256]; // Full service name # endif // HAVE_DNSSD _pappl_dns_sd_t master; // DNS-SD master reference if (!printer->dns_sd_name || !printer->system->is_running) return (false); papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Registering DNS-SD name '%s' on '%s'", printer->dns_sd_name, printer->system->hostname); // Get attributes and values for the TXT record... color_supported = ippFindAttribute(printer->driver_attrs, "color-supported", IPP_TAG_BOOLEAN); document_format_supported = ippFindAttribute(printer->driver_attrs, "document-format-supported", IPP_TAG_MIMETYPE); printer_kind = ippFindAttribute(printer->driver_attrs, "printer-kind", IPP_TAG_KEYWORD); printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI); urf_supported = ippFindAttribute(printer->driver_attrs, "urf-supported", IPP_TAG_KEYWORD); for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++) { value = ippGetString(document_format_supported, i, NULL); if (!strcasecmp(value, "application/octet-stream")) continue; if (ptr > formats && ptr < (formats + sizeof(formats) - 1)) *ptr++ = ','; strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats)); ptr += strlen(ptr); if (ptr >= (formats + sizeof(formats) - 1)) break; } kind[0] = '\0'; for (i = 0, count = ippGetCount(printer_kind), ptr = kind; i < count; i ++) { value = ippGetString(printer_kind, i, NULL); if (ptr > kind && ptr < (kind + sizeof(kind) - 1)) *ptr++ = ','; strlcpy(ptr, value, sizeof(kind) - (size_t)(ptr - kind)); ptr += strlen(ptr); if (ptr >= (kind + sizeof(kind) - 1)) break; } snprintf(product, sizeof(product), "(%s)", printer->driver_data.make_and_model); for (i = 0, max_width = 0; i < printer->driver_data.num_media; i ++) { pwg_media_t *media = pwgMediaForPWG(printer->driver_data.media[i]); // Current media size if (media && media->width > max_width) max_width = media->width; } if (max_width < 21000) papermax = " urf && ptr < (urf + sizeof(urf) - 1)) *ptr++ = ','; strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf)); ptr += strlen(ptr); if (ptr >= (urf + sizeof(urf) - 1)) break; } httpAssembleURIf(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL, printer->system->hostname, printer->system->port, "%s/", printer->uriname); if (printer->geo_location) dns_sd_geo_to_loc(printer->geo_location, printer->dns_sd_loc); // Rename the service as needed... if (printer->dns_sd_collision) { char new_dns_sd_name[256]; // New DNS-SD name const char *serial = strstr(printer->device_uri, "?serial="); // Serial number const char *uuid = ippGetString(printer_uuid, 0, NULL); // "printer-uuid" value printer->dns_sd_serial ++; if (printer->dns_sd_serial == 1) { if (printer->system->options & PAPPL_SOPTIONS_DNSSD_HOST) snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", printer->dns_sd_name, printer->system->hostname); else if (serial) snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", printer->dns_sd_name, serial + 8); else snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%c%c%c%c%c%c)", printer->dns_sd_name, toupper(uuid[39]), toupper(uuid[40]), toupper(uuid[41]), toupper(uuid[42]), toupper(uuid[43]), toupper(uuid[44])); } else { char base_dns_sd_name[256]; // Base DNS-SD name strlcpy(base_dns_sd_name, printer->dns_sd_name, sizeof(base_dns_sd_name)); if ((ptr = strrchr(base_dns_sd_name, '(')) != NULL) *ptr = '\0'; snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s(%d)", base_dns_sd_name, printer->dns_sd_serial); } free(printer->dns_sd_name); if ((printer->dns_sd_name = strdup(new_dns_sd_name)) != NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "DNS-SD name collision, trying new DNS-SD service name '%s'.", printer->dns_sd_name); printer->dns_sd_collision = false; } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "DNS-SD name collision, failed to allocate new DNS-SD service name."); return (false); } } if ((master = _papplDNSSDInit(printer->system)) == NULL) return (false); #endif // HAVE_DNSSD || HAVE_AVAHI #ifdef HAVE_DNSSD // Build the TXT record for IPP... TXTRecordCreate(&txt, 1024, NULL); TXTRecordSetValue(&txt, "rp", (uint8_t)strlen(printer->resource) - 1, printer->resource + 1); if (printer->driver_data.make_and_model[0]) TXTRecordSetValue(&txt, "ty", (uint8_t)strlen(printer->driver_data.make_and_model), printer->driver_data.make_and_model); TXTRecordSetValue(&txt, "adminurl", (uint8_t)strlen(adminurl), adminurl); if (printer->location) TXTRecordSetValue(&txt, "note", (uint8_t)strlen(printer->location), printer->location); else TXTRecordSetValue(&txt, "note", 0, ""); TXTRecordSetValue(&txt, "pdl", (uint8_t)strlen(formats), formats); if (kind[0]) TXTRecordSetValue(&txt, "kind", (uint8_t)strlen(kind), kind); if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL) TXTRecordSetValue(&txt, "UUID", (uint8_t)strlen(value) - 9, value + 9); if (urf[0]) TXTRecordSetValue(&txt, "URF", (uint8_t)strlen(urf), urf); TXTRecordSetValue(&txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F"); TXTRecordSetValue(&txt, "Duplex", 1, (printer->driver_data.sides_supported & PAPPL_SIDES_TWO_SIDED_LONG_EDGE) ? "T" : "F"); TXTRecordSetValue(&txt, "TLS", 3, "1.2"); TXTRecordSetValue(&txt, "txtvers", 1, "1"); TXTRecordSetValue(&txt, "qtotal", 1, "1"); TXTRecordSetValue(&txt, "priority", 1, "0"); TXTRecordSetValue(&txt, "mopria-certified", 3, "1.3"); // Legacy keys... TXTRecordSetValue(&txt, "product", (uint8_t)strlen(product), product); TXTRecordSetValue(&txt, "Fax", 1, "F"); TXTRecordSetValue(&txt, "PaperMax", (uint8_t)strlen(papermax), papermax); TXTRecordSetValue(&txt, "Scan", 1, "F"); // Register the _printer._tcp (LPD) service type with a port number of 0 to // defend our service name but not actually support LPD... if (printer->dns_sd_printer_ref) DNSServiceRefDeallocate(printer->dns_sd_printer_ref); printer->dns_sd_printer_ref = master; if ((error = DNSServiceRegister(&printer->dns_sd_printer_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, 0 /* interfaceIndex */, printer->dns_sd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._printer._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } // Then register the corresponding IPP service types with the real port // number to advertise our printer... if (printer->dns_sd_ipp_ref) DNSServiceRefDeallocate(printer->dns_sd_ipp_ref); printer->dns_sd_ipp_ref = master; if (system->subtypes && *system->subtypes) snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", system->subtypes); else strlcpy(regtype, "_ipp._tcp", sizeof(regtype)); if ((error = DNSServiceRegister(&printer->dns_sd_ipp_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, 0 /* interfaceIndex */, printer->dns_sd_name, regtype, NULL /* domain */, system->hostname, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error)); ret = false; } if (printer->geo_location && ret) { if ((error = DNSServiceAddRecord(printer->dns_sd_ipp_ref, &printer->dns_sd_ipp_loc_ref, 0, kDNSServiceType_LOC, sizeof(printer->dns_sd_loc), printer->dns_sd_loc, 0)) != kDNSServiceErr_NoError) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error)); ret = false; } } if (printer->dns_sd_ipps_ref) DNSServiceRefDeallocate(printer->dns_sd_ipps_ref); printer->dns_sd_ipps_ref = master; if (system->subtypes && *system->subtypes) snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", system->subtypes); else strlcpy(regtype, "_ipps._tcp", sizeof(regtype)); if ((error = DNSServiceRegister(&printer->dns_sd_ipps_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, 0 /* interfaceIndex */, printer->dns_sd_name, regtype, NULL /* domain */, system->hostname, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error)); ret = false; } if (printer->geo_location && ret) { if ((error = DNSServiceAddRecord(printer->dns_sd_ipps_ref, &printer->dns_sd_ipps_loc_ref, 0, kDNSServiceType_LOC, sizeof(printer->dns_sd_loc), printer->dns_sd_loc, 0)) != kDNSServiceErr_NoError) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error)); ret = false; } } TXTRecordDeallocate(&txt); if ((system->options & PAPPL_SOPTIONS_RAW_SOCKET) && printer->num_raw_listeners > 0) { // Register a PDL datastream (raw socket) service... TXTRecordCreate(&txt, 1024, NULL); if (printer->driver_data.make_and_model[0]) TXTRecordSetValue(&txt, "ty", (uint8_t)strlen(printer->driver_data.make_and_model), printer->driver_data.make_and_model); TXTRecordSetValue(&txt, "adminurl", (uint8_t)strlen(adminurl), adminurl); if (printer->location) TXTRecordSetValue(&txt, "note", (uint8_t)strlen(printer->location), printer->location); else TXTRecordSetValue(&txt, "note", 0, ""); TXTRecordSetValue(&txt, "pdl", (uint8_t)strlen(formats), formats); if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL) TXTRecordSetValue(&txt, "UUID", (uint8_t)strlen(value) - 9, value + 9); TXTRecordSetValue(&txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F"); TXTRecordSetValue(&txt, "Duplex", 1, (printer->driver_data.sides_supported & PAPPL_SIDES_TWO_SIDED_LONG_EDGE) ? "T" : "F"); TXTRecordSetValue(&txt, "txtvers", 1, "1"); TXTRecordSetValue(&txt, "qtotal", 1, "1"); TXTRecordSetValue(&txt, "priority", 3, "100"); // Legacy keys... TXTRecordSetValue(&txt, "product", (uint8_t)strlen(product), product); TXTRecordSetValue(&txt, "Fax", 1, "F"); TXTRecordSetValue(&txt, "PaperMax", (uint8_t)strlen(papermax), papermax); TXTRecordSetValue(&txt, "Scan", 1, "F"); if (printer->dns_sd_pdl_ref) DNSServiceRefDeallocate(printer->dns_sd_pdl_ref); printer->dns_sd_pdl_ref = master; if ((error = DNSServiceRegister(&printer->dns_sd_pdl_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, 0 /* interfaceIndex */, printer->dns_sd_name, "_pdl-datastream._tcp", NULL /* domain */, system->hostname, htons(9099 + printer->printer_id), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, "_pdl-datastream._tcp", _papplDNSSDStrError(error)); ret = false; } TXTRecordDeallocate(&txt); } // Register the _http._tcp,_printer (HTTP) service type with the real port // number to advertise our web interface... if (printer->dns_sd_http_ref) DNSServiceRefDeallocate(printer->dns_sd_http_ref); snprintf(adminurl, sizeof(adminurl), "%s/", printer->uriname); TXTRecordCreate(&txt, 1024, NULL); TXTRecordSetValue(&txt, "path", (uint8_t)strlen(adminurl), adminurl); printer->dns_sd_http_ref = master; if ((error = DNSServiceRegister(&printer->dns_sd_http_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, 0 /* interfaceIndex */, printer->dns_sd_name, "_http._tcp,_printer", NULL /* domain */, system->hostname, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, "_http._tcp,_printer", _papplDNSSDStrError(error)); ret = false; } TXTRecordDeallocate(&txt); #elif defined(HAVE_AVAHI) // Create the TXT record... txt = NULL; txt = avahi_string_list_add_printf(txt, "rp=%s", printer->resource + 1); if (printer->driver_data.make_and_model[0]) txt = avahi_string_list_add_printf(txt, "ty=%s", printer->driver_data.make_and_model); txt = avahi_string_list_add_printf(txt, "adminurl=%s", adminurl); txt = avahi_string_list_add_printf(txt, "note=%s", printer->location ? printer->location : ""); txt = avahi_string_list_add_printf(txt, "pdl=%s", formats); if (kind[0]) txt = avahi_string_list_add_printf(txt, "kind=%s", kind); if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL) txt = avahi_string_list_add_printf(txt, "UUID=%s", value + 9); if (urf[0]) txt = avahi_string_list_add_printf(txt, "URF=%s", urf); txt = avahi_string_list_add_printf(txt, "TLS=1.2"); txt = avahi_string_list_add_printf(txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F"); txt = avahi_string_list_add_printf(txt, "Duplex=%s", (printer->driver_data.sides_supported & PAPPL_SIDES_TWO_SIDED_LONG_EDGE) ? "T" : "F"); txt = avahi_string_list_add_printf(txt, "txtvers=1"); txt = avahi_string_list_add_printf(txt, "qtotal=1"); txt = avahi_string_list_add_printf(txt, "priority=0"); txt = avahi_string_list_add_printf(txt, "mopria-certified=1.3"); // Legacy keys... txt = avahi_string_list_add_printf(txt, "product=%s", product); txt = avahi_string_list_add_printf(txt, "Fax=F"); txt = avahi_string_list_add_printf(txt, "PaperMax=%s", papermax); txt = avahi_string_list_add_printf(txt, "Scan=F"); // Register _printer._tcp (LPD) with port 0 to reserve the service name... _papplDNSSDLock(); if (printer->dns_sd_ref) avahi_entry_group_free(printer->dns_sd_ref); if ((printer->dns_sd_ref = avahi_entry_group_new(master, (AvahiEntryGroupCallback)dns_sd_printer_callback, printer)) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register printer, is the Avahi daemon running?"); _papplDNSSDUnlock(); avahi_string_list_free(txt); return (false); } if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_printer._tcp", NULL, NULL, 0, NULL)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._printer._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } // Then register the IPP/IPPS services... if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipp._tcp", NULL, system->hostname, system->port, txt)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipp._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } if (system->subtypes && *system->subtypes) { char *temptypes = strdup(system->subtypes), *start, *end; // Pointers into sub-types... for (start = temptypes; start && *start; start = end) { if ((end = strchr(start, ',')) != NULL) *end++ = '\0'; else end = start + strlen(start); snprintf(regtype, sizeof(regtype), "%s._sub._ipp._tcp", start); if ((error = avahi_entry_group_add_service_subtype(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipp._tcp", NULL, regtype)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error)); ret = false; } } free(temptypes); } if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipps._tcp", NULL, system->hostname, system->port, txt)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipps._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } if (system->subtypes && *system->subtypes) { char *temptypes = strdup(system->subtypes), *start, *end; // Pointers into sub-types... for (start = temptypes; start && *start; start = end) { if ((end = strchr(start, ',')) != NULL) *end++ = '\0'; else end = start + strlen(start); snprintf(regtype, sizeof(regtype), "%s._sub._ipps._tcp", start); if ((error = avahi_entry_group_add_service_subtype(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipps._tcp", NULL, regtype)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error)); ret = false; } } free(temptypes); } avahi_string_list_free(txt); if ((system->options & PAPPL_SOPTIONS_RAW_SOCKET) && printer->num_raw_listeners > 0) { // Register a PDL datastream (raw socket) service... txt = NULL; if (printer->driver_data.make_and_model[0]) txt = avahi_string_list_add_printf(txt, "ty=%s", printer->driver_data.make_and_model); txt = avahi_string_list_add_printf(txt, "adminurl=%s", adminurl); txt = avahi_string_list_add_printf(txt, "note=%s", printer->location ? printer->location : ""); txt = avahi_string_list_add_printf(txt, "pdl=%s", formats); if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL) txt = avahi_string_list_add_printf(txt, "UUID=%s", value + 9); txt = avahi_string_list_add_printf(txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F"); txt = avahi_string_list_add_printf(txt, "Duplex=%s", (printer->driver_data.sides_supported & PAPPL_SIDES_TWO_SIDED_LONG_EDGE) ? "T" : "F"); txt = avahi_string_list_add_printf(txt, "txtvers=1"); txt = avahi_string_list_add_printf(txt, "qtotal=1"); txt = avahi_string_list_add_printf(txt, "priority=100"); // Legacy keys... txt = avahi_string_list_add_printf(txt, "product=%s", product); txt = avahi_string_list_add_printf(txt, "Fax=F"); txt = avahi_string_list_add_printf(txt, "PaperMax=%s", papermax); txt = avahi_string_list_add_printf(txt, "Scan=F"); if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_pdl-datastream._tcp", NULL, system->hostname, 9099 + printer->printer_id, txt)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._pdl-datastream._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } avahi_string_list_free(txt); } // Register the geolocation of the service... if (printer->geo_location && ret) { snprintf(fullname, sizeof(fullname), "%s._ipp._tcp.local.", printer->dns_sd_name); if ((error = avahi_entry_group_add_record(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_LOC, 75 * 60, printer->dns_sd_loc, sizeof(printer->dns_sd_loc))) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s': %s", fullname, _papplDNSSDStrError(error)); ret = false; } snprintf(fullname, sizeof(fullname), "%s._ipps._tcp.local.", printer->dns_sd_name); if ((error = avahi_entry_group_add_record(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_LOC, 75 * 60, printer->dns_sd_loc, sizeof(printer->dns_sd_loc))) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s': %s", fullname, _papplDNSSDStrError(error)); ret = false; } } // Finally _http.tcp (HTTP) for the web interface... txt = NULL; txt = avahi_string_list_add_printf(txt, "path=%s/", printer->uriname); avahi_entry_group_add_service_strlst(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_http._tcp", NULL, system->hostname, system->port, txt); avahi_entry_group_add_service_subtype(printer->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_http._tcp", NULL, "_printer._sub._http._tcp"); avahi_string_list_free(txt); // Commit it... avahi_entry_group_commit(printer->dns_sd_ref); _papplDNSSDUnlock(); #endif // HAVE_DNSSD return (ret); } // // '_papplPrinterUnregisterDNSSDNoLock()' - Unregister a printer's DNS-SD service. // void _papplPrinterUnregisterDNSSDNoLock( pappl_printer_t *printer) // I - Printer { #if HAVE_DNSSD if (printer->dns_sd_printer_ref) { DNSServiceRefDeallocate(printer->dns_sd_printer_ref); printer->dns_sd_printer_ref = NULL; } if (printer->dns_sd_ipp_ref) { DNSServiceRefDeallocate(printer->dns_sd_ipp_ref); printer->dns_sd_ipp_ref = NULL; printer->dns_sd_ipp_loc_ref = NULL; } if (printer->dns_sd_ipps_ref) { DNSServiceRefDeallocate(printer->dns_sd_ipps_ref); printer->dns_sd_ipps_ref = NULL; printer->dns_sd_ipps_loc_ref = NULL; } if (printer->dns_sd_http_ref) { DNSServiceRefDeallocate(printer->dns_sd_http_ref); printer->dns_sd_http_ref = NULL; } #elif defined(HAVE_AVAHI) _papplDNSSDLock(); if (printer->dns_sd_ref) { avahi_entry_group_free(printer->dns_sd_ref); printer->dns_sd_ref = NULL; } _papplDNSSDUnlock(); #else (void)printer; #endif /* HAVE_DNSSD */ } // // '_papplSystemRegisterDNSSDNoLock()' - Register a system's DNS-SD service. // bool // O - `true` on success, `false` on failure _papplSystemRegisterDNSSDNoLock( pappl_system_t *system) // I - System { #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) bool ret = true; // Return value _pappl_dns_sd_t master; // DNS-SD master reference _pappl_txt_t txt; // DNS-SD TXT record # ifdef HAVE_DNSSD DNSServiceErrorType error; // Error from mDNSResponder # else int error; // Error from Avahi char fullname[256]; // Full name of services # endif // HAVE_DNSSD // Make sure we have all of the necessary information to register the system... if (!system->dns_sd_name || !system->hostname || !system->uuid || !system->is_running) return (false); papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Registering DNS-SD name '%s' on '%s'", system->dns_sd_name, system->hostname); if (system->geo_location) dns_sd_geo_to_loc(system->geo_location, system->dns_sd_loc); // Rename the service as needed... if (system->dns_sd_collision) { char new_dns_sd_name[256]; // New DNS-SD name system->dns_sd_serial ++; if (system->dns_sd_serial == 1) { if (system->options & PAPPL_SOPTIONS_DNSSD_HOST) snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", system->dns_sd_name, system->hostname); else snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%c%c%c%c%c%c)", system->dns_sd_name, toupper(system->uuid[39]), toupper(system->uuid[40]), toupper(system->uuid[41]), toupper(system->uuid[42]), toupper(system->uuid[43]), toupper(system->uuid[44])); } else { char base_dns_sd_name[256], // Base DNS-SD name *ptr; // Pointer into name strlcpy(base_dns_sd_name, system->dns_sd_name, sizeof(base_dns_sd_name)); if ((ptr = strrchr(base_dns_sd_name, '(')) != NULL) *ptr = '\0'; snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s(%d)", base_dns_sd_name, system->dns_sd_serial); } free(system->dns_sd_name); if ((system->dns_sd_name = strdup(new_dns_sd_name)) != NULL) { papplLog(system, PAPPL_LOGLEVEL_INFO, "DNS-SD name collision, trying new DNS-SD service name '%s'.", system->dns_sd_name); system->dns_sd_collision = false; } else { papplLog(system, PAPPL_LOGLEVEL_ERROR, "DNS-SD name collision, unable to allocate new DNS-SD service name."); return (false); } } if ((master = _papplDNSSDInit(system)) == NULL) return (false); #endif // HAVE_DNSSD || HAVE_AVAHI #ifdef HAVE_DNSSD // Build the TXT record... TXTRecordCreate(&txt, 1024, NULL); if (system->location != NULL) TXTRecordSetValue(&txt, "note", (uint8_t)strlen(system->location), system->location); TXTRecordSetValue(&txt, "UUID", (uint8_t)strlen(system->uuid) - 9, system->uuid + 9); // Then register the corresponding IPPS service type to advertise our system... if (system->dns_sd_ipps_ref) DNSServiceRefDeallocate(system->dns_sd_ipps_ref); system->dns_sd_ipps_ref = master; if ((error = DNSServiceRegister(&system->dns_sd_ipps_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, 0 /* interfaceIndex */, system->dns_sd_name, "_ipps-system._tcp", NULL /* domain */, system->hostname, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_system_callback, system)) != kDNSServiceErr_NoError) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipps-system._tcp': %s", system->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } if (system->geo_location && ret) { papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Registering LOC record for '%s._ipps-system._tcp' with data %02X %02X %02X %02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", system->dns_sd_name, system->dns_sd_loc[0], system->dns_sd_loc[1], system->dns_sd_loc[2], system->dns_sd_loc[3], system->dns_sd_loc[4], system->dns_sd_loc[5], system->dns_sd_loc[6], system->dns_sd_loc[7], system->dns_sd_loc[8], system->dns_sd_loc[9], system->dns_sd_loc[10], system->dns_sd_loc[11], system->dns_sd_loc[12], system->dns_sd_loc[13], system->dns_sd_loc[14], system->dns_sd_loc[15]); if ((error = DNSServiceAddRecord(system->dns_sd_ipps_ref, &system->dns_sd_loc_ref, 0, kDNSServiceType_LOC, sizeof(system->dns_sd_loc), system->dns_sd_loc, 0)) != kDNSServiceErr_NoError) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s._ipps-system._tcp': %s", system->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } } TXTRecordDeallocate(&txt); // Register the _http._tcp,_printer (HTTP) service type with the real port // number to advertise our web interface... if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE) { if (system->dns_sd_http_ref) DNSServiceRefDeallocate(system->dns_sd_http_ref); system->dns_sd_http_ref = master; if ((error = DNSServiceRegister(&system->dns_sd_http_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, 0 /* interfaceIndex */, system->dns_sd_name, "_http._tcp,_printer", NULL /* domain */, system->hostname, htons(system->port), 0 /* txtlen */, NULL /* txt */, (DNSServiceRegisterReply)dns_sd_system_callback, system)) != kDNSServiceErr_NoError) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", system->dns_sd_name, "_http._tcp,_printer", _papplDNSSDStrError(error)); ret = false; } } #elif defined(HAVE_AVAHI) // Create the TXT record... txt = NULL; if (system->location) txt = avahi_string_list_add_printf(txt, "note=%s", system->location); txt = avahi_string_list_add_printf(txt, "UUID=%s", system->uuid + 9); // Register _printer._tcp (LPD) with port 0 to reserve the service name... _papplDNSSDLock(); if (system->dns_sd_ref) avahi_entry_group_free(system->dns_sd_ref); if ((system->dns_sd_ref = avahi_entry_group_new(master, (AvahiEntryGroupCallback)dns_sd_system_callback, system)) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register system, is the Avahi daemon running?"); _papplDNSSDUnlock(); avahi_string_list_free(txt); return (false); } if ((error = avahi_entry_group_add_service_strlst(system->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_ipps-system._tcp", NULL, system->hostname, system->port, txt)) < 0) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipps-system._tcp': %s", system->dns_sd_name, _papplDNSSDStrError(error)); ret = false; } // Register the geolocation of the service... if (system->geo_location && ret) { snprintf(fullname, sizeof(fullname), "%s._ipps-system._tcp.local.", system->dns_sd_name); papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Registering LOC record for '%s' with data %02X %02X %02X %02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", fullname, system->dns_sd_loc[0], system->dns_sd_loc[1], system->dns_sd_loc[2], system->dns_sd_loc[3], system->dns_sd_loc[4], system->dns_sd_loc[5], system->dns_sd_loc[6], system->dns_sd_loc[7], system->dns_sd_loc[8], system->dns_sd_loc[9], system->dns_sd_loc[10], system->dns_sd_loc[11], system->dns_sd_loc[12], system->dns_sd_loc[13], system->dns_sd_loc[14], system->dns_sd_loc[15]); if ((error = avahi_entry_group_add_record(system->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_LOC, 75 * 60, system->dns_sd_loc, sizeof(system->dns_sd_loc))) < 0) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s': %s", fullname, _papplDNSSDStrError(error)); ret = false; } } // Finally _http.tcp (HTTP) for the web interface... if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE) { avahi_entry_group_add_service_strlst(system->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_http._tcp", NULL, system->hostname, system->port, NULL); avahi_entry_group_add_service_subtype(system->dns_sd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_http._tcp", NULL, "_printer._sub._http._tcp"); } // Commit it... avahi_entry_group_commit(system->dns_sd_ref); _papplDNSSDUnlock(); avahi_string_list_free(txt); #endif // HAVE_DNSSD return (ret); } // // '_papplSystemUnregisterDNSSDNoLock()' - Unregister a printer's DNS-SD service. // void _papplSystemUnregisterDNSSDNoLock( pappl_system_t *system) // I - System { #if HAVE_DNSSD if (system->dns_sd_ipps_ref) { DNSServiceRefDeallocate(system->dns_sd_ipps_ref); system->dns_sd_ipps_ref = NULL; } if (system->dns_sd_http_ref) { DNSServiceRefDeallocate(system->dns_sd_http_ref); system->dns_sd_http_ref = NULL; } #elif defined(HAVE_AVAHI) _papplDNSSDLock(); if (system->dns_sd_ref) { avahi_entry_group_free(system->dns_sd_ref); system->dns_sd_ref = NULL; } _papplDNSSDUnlock(); #else (void)printer; #endif /* HAVE_DNSSD */ } // // 'dns_sd_geo_to_loc()' - Convert a "geo:" URI to a DNS LOC record. // static void dns_sd_geo_to_loc(const char *geo, // I - "geo:" URI unsigned char loc[16])// O - DNS LOC record { double lat = 0.0, lon = 0.0; // Latitude and longitude in degrees double alt = 0.0; // Altitude in meters unsigned int lat_ksec, lon_ksec; // Latitude and longitude in thousandths of arc seconds, biased by 2^31 unsigned int alt_cm; // Altitude in centimeters, biased by 10,000,000cm // Pull apart the "geo:" URI and convert to the integer representation for // the LOC record... sscanf(geo, "geo:%lf,%lf,%lf", &lat, &lon, &alt); lat_ksec = (unsigned)((int)(lat * 3600000.0) + 0x40000000 + 0x40000000); lon_ksec = (unsigned)((int)(lon * 3600000.0) + 0x40000000 + 0x40000000); alt_cm = (unsigned)((int)(alt * 100.0) + 10000000); // Build the LOC record... loc[0] = 0x00; // Version loc[1] = 0x11; // Size (10cm) loc[2] = 0x11; // Horizontal precision (10cm) loc[3] = 0x11; // Vertical precision (10cm) loc[4] = (unsigned char)(lat_ksec >> 24); // Latitude (32-bit big-endian) loc[5] = (unsigned char)(lat_ksec >> 16); loc[6] = (unsigned char)(lat_ksec >> 8); loc[7] = (unsigned char)(lat_ksec); loc[8] = (unsigned char)(lon_ksec >> 24); // Latitude (32-bit big-endian) loc[9] = (unsigned char)(lon_ksec >> 16); loc[10] = (unsigned char)(lon_ksec >> 8); loc[11] = (unsigned char)(lon_ksec); loc[12] = (unsigned char)(alt_cm >> 24); // Altitude (32-bit big-endian) loc[13] = (unsigned char)(alt_cm >> 16); loc[14] = (unsigned char)(alt_cm >> 8); loc[15] = (unsigned char)(alt_cm); } #ifdef HAVE_DNSSD // // 'dns_sd_printer_callback()' - Handle DNS-SD printer registration events. // static void DNSSD_API dns_sd_printer_callback( DNSServiceRef sdRef, // I - Service reference DNSServiceFlags flags, // I - Status flags DNSServiceErrorType errorCode, // I - Error, if any const char *name, // I - Service name const char *regtype, // I - Service type const char *domain, // I - Domain for service pappl_printer_t *printer) // I - Printer { (void)name; (void)sdRef; (void)flags; (void)domain; if (errorCode == kDNSServiceErr_NameConflict) { printer->dns_sd_collision = true; printer->system->dns_sd_any_collision = true; } else if (errorCode) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "DNSServiceRegister for '%s' failed with error %d (%s).", regtype, (int)errorCode, _papplDNSSDStrError(errorCode)); return; } } // // 'dns_sd_run()' - Handle DNS-SD traffic. // static void * // O - Exit status dns_sd_run(void *data) // I - System object { int err; // Status pappl_system_t *system = (pappl_system_t *)data; // System object for (;;) { if ((err = DNSServiceProcessResult(pappl_dns_sd_master)) != kDNSServiceErr_NoError) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "DNSServiceProcessResult returned %d (%s).", err, _papplDNSSDStrError(err)); break; } } return (NULL); } // // 'dns_sd_system_callback()' - Handle DNS-SD system registration events. // static void DNSSD_API dns_sd_system_callback( DNSServiceRef sdRef, // I - Service reference DNSServiceFlags flags, // I - Status flags DNSServiceErrorType errorCode, // I - Error, if any const char *name, // I - Service name const char *regtype, // I - Service type const char *domain, // I - Domain for service pappl_system_t *system) // I - System { (void)sdRef; (void)flags; (void)name; (void)domain; if (errorCode == kDNSServiceErr_NameConflict) { system->dns_sd_collision = true; system->dns_sd_any_collision = true; } else if (errorCode) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "DNSServiceRegister for '%s' failed with error %d (%s).", regtype, (int)errorCode, _papplDNSSDStrError(errorCode)); return; } } #elif defined(HAVE_AVAHI) // //'dns_sd_client_cb()' - Client callback for Avahi. // // Called whenever the client or server state changes... // static void dns_sd_client_cb( AvahiClient *c, // I - Client AvahiClientState state, // I - Current state void *data) // I - Callback data (unused) { (void)data; if (!c) return; if (state == AVAHI_CLIENT_FAILURE) { if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) { fputs("Avahi server crashed.\n", stderr); } } else if (state == AVAHI_CLIENT_S_RUNNING) pappl_dns_sd_host_name_changes ++; } // // 'dns_sd_printer_callback()' - Handle DNS-SD printer registration events. // static void dns_sd_printer_callback( AvahiEntryGroup *srv, // I - Service AvahiEntryGroupState state, // I - Registration state pappl_printer_t *printer) // I - Printer { (void)srv; if (state == AVAHI_ENTRY_GROUP_COLLISION) { printer->dns_sd_collision = true; printer->system->dns_sd_any_collision = true; } } // // 'dns_sd_system_callback()' - Handle DNS-SD system registration events. // static void dns_sd_system_callback( AvahiEntryGroup *srv, // I - Service AvahiEntryGroupState state, // I - Registration state pappl_system_t *system) // I - System { (void)srv; if (state == AVAHI_ENTRY_GROUP_COLLISION) { system->dns_sd_collision = true; system->dns_sd_any_collision = true; } } #endif // HAVE_DNSSD pappl-1.0.3/pappl/epub.png000066400000000000000000003076101403603036100154070ustar00rootroot00000000000000PNG  IHDRxQ"UgiCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*ixQkeiTXtXML:com.adobe.xmp Michael Sweet Copyright 2020 Michael Sweet New Image New Image uIDATxy]W]t9i4M MII$M"-_FAUW/DIQ@qq"< Rd.PZ1[ ''{sν|=ɹg{묽V G30E7A g߾}a@ZaX|ސDԖ P[B@4h%DԖ @4@m a%D P[B -! @4hjK 0hD-! 0hd]a0& 1F1,^X `||<(C4A4A4A4A48M+uV, -Hyfa0׷°pBA4 1ha@Mlb;B0Uyi{K 0hD-! 0hjK-! @4h 0hjK-! @4hjK 0hhDԖ P[B@4h%DԖ 0=mZaȲLo0# n* tqV.\7   DZh]a0& 1F1⌏ 5 [aC48loc€0v40` @4@m a%D P[B@4@m aDԖ P[B@4h%A@4@m aDԖ P[B@4h%DԖ @4@m a%D D-! 0hjK-! @4hjK $a0@     DZw]={a@G#ή]€m ŋ@4{ǰ!5ahaT-A4hjK 0hD-! 0hjK0hD-! 0hjK-! @4hjKA P[B@4@m aDԖ P[B@4hr$֭k!ƨ7A<ƈvZa0իWPU /bɒ%`۶mrg~ѢEu'{ֿSO=]tQa9S}lR;w>MzeYݻw׿3hU_NM?I7ֿ~Ϟ=OsuDŽoO=ԿСC*;|p?Iε'0K/ D]lٲtgY۷gYv/':&{GaÆ^fMQc8P':y޵kWQ/\Qs9O~u:e:/?dD:?R:ڏ:]u;v[tuG}i~]'EO.]:N/h?t#'::zG~ͫ?ԍ"}?rʎ;QQ+jQw=ꎟҝ#;QI.'Qǟt9j?t?tOeqJ.]ۯ$?K.2N~?7uVףNKUԩ[ڏ:uZ("`Q>m?)W_)<qz)GN8;QQ)ݭ?Rz޴iSQF}}/8q{ҝӉqu=42u=ڏzŊG׎8uΥ;QQKt}E5Ni8TuuDGޭӇStQ8[Sxڿt8shر)NH"ʮoMqj?)ҽIQv''6 l0].Oi]=ҷYu캎ӗ۞4?tkk(gO4 D@4 D@4 D@4 D@4 D@4 D@4 D@4 D0c$hYv 81 CO&r0@AI0tQb8'a0>$ 0hD-! 0hjK-! @4haPN@m aDԖ P[B@4h%DԖ @4@m a%DhjK 0hD ,[a@G,#ҥK€/^ '   DZe]M6`a'N UqFa0uֵ` h%aGCj` ;B0Uyi{K 0hD-! 0hjK-! @4h 0hjK-! @4hjK 0hhDԖ P[B@4h%DԖ 0DW`0dY7A,ƈm6a0M60>>7   DZ`]a0& 1F1⌍ TU Ga{KÎԄx ! % P[B@4@m aDԖ P[B@4h%DԖ @4@m a P[B@4h%DԖ @4@m a%D P[B@4@m a@4HjK-! @4hjK 0hDFr]a0@ LD+ 1F1 g g g g 8ݓ ڵ,0:`7l߾] `˖-0,ZHo D-q ;BPLU^D-! 0hjK-! @4hjK 0-! @4hjK 0hD-! 0h $h%DԖ @4@m a%D .gL+qVbzCa\o8ׯ:Xvm+ ccczh @ @ @ @ p&Xre+ 00dY7F+V,[;U D-q ;BPLU^D-! 0hjK-! @4hjK 0-! @4hjK 0hD-! 0h $h%DԖ @4@m a%D .'袋Za\o0# ccczc9€v ҥK@4@4A4A4A4A48:ch€0X[60D:e G\7@4h@4hF4 @4>8=GurȌD DiDр$bɒ%`۶mrg~ѢEucc{ֿOoZ.hqTt[lΝ;__],{ןqqWUVտ~G7n={?U ]c`_:t(ry>|柬[=?^z5Oo'ҿ5<9=6ٙʅCSsϭ? 65k:D{?͛7ovj|{…]ӱ?q9ڏ:}NLQ_|Gz?NNNG.iǎb֭"DG>@Q_pG}iuDtou=C{G:묳:Ӎ8tw?׷ڵk:ucǟHO\NT~];QGJwc$JOtSEXE:plqJ3LXA]{WtotscٲeT_:ҝ}x<n!/~il5 /^>=&jn4/(` Ґ# ?i]/~gINz89oi`S~'JBײ#Ki۬y} [ͮ89}MNùAi}VD@4L4\}_ڱNG{z$h Mӈh a$D\]__voKh h4h $݋"ptj:" 81 ⡇h4k4 nIQb48-cْ s(D  CX|jaY9ebzC hXƒB = b_bY %YI4h0Xٕ!{q?rXc,CDѠ08+ T~?0d/͵\xW,!DDAm apV @4̙\r򱈻N.C~LDѠ08+ 憥3~eⱈ#s'w1c==@DAm apV @4+x_-b!I4h@4-! JD޳8c9 w=>DAm apV @4E!z1Ϝʅ;cFq|;hhP[BD 3'Ui4e_t\HOgg4i @4 jKhрac$}z!\c9 w*?p SO=Sc,ˈ/iV^=aY9\,@4$v1לnEtڮ"mZqeM44рA!M;bϱkNbҘ?(%iD(i8+>c! 19 鉌e1-WH4 0T!\sws*>˗!M.  3OR/C,;p̅! |DúuZa'!s%͈4k׮0`@Lʠ0TU7c-ӢB|V9rCE<8dmoEpblo%6 A./mo S$̐},3wrH>r#|cKG %YI4h08_̅bʅWc~հ@4h@4-! JDFH.\3Bwmf.cX<,d.ED D$@4`>pA?9 ![[!@4h Ԗg%K.υoc9 ߈![[!@4h Ԗg%K.BױeNU&d|+D D$@4`wφ湓 }2#BvohhP[BD/pn?ŷT.|*GlohhP[BD DbO?Ms*>Q!;W2aϞ={'ܷ3a ahf׮] HɶmZaXx Agw׆c9 ![+PJDP+ħb9 xLN QJDbgO ٟs*>ߊBWB4yLWYS>K!4m #͎2doōs*c#!;WB4!$ Rs&֣MӈD9 7c߉SCv q_@4Dh aIE!d6 wC|}, C @4NC @4 %ΚWC4y晭0Z>陂KB6i.'byƿձH3,X 0j[ 'e˖ * Dü`Æ 0U!@4 D1{G!5 ~bu[bXZsB碻lo`{KE-1a Qyi{K D@4Dmn|@~!ou:2Z!#fڽe1tq D D@4zmyZWY1XlwE1Dȶ0 D D@4\m1y1X~e—cXD@4hh !-Rg$Ml곱X@C@4h  @4 D0le^aW +bX~{78Ob_W DDh _mYpnO o⽱}=X^,w. qԖDEDh L B6lGcXT8>%AC4hh -/JJSŞ,|'V}$:oEz6 =g7鞇CQNDÉlٲe4À 0/ذaC+ .D@4h +C<Ĵ&bZ3:2[N+b֛b.`NC @4 Dh85 Y1x`obv|dȶM D@4  s!<8dω_⋱::f᫱z[,^kB)(dh@4 hsѐ cX~u—bױ!hNg h \4,W3!gZłofcln_Y!2SWbbh8Ef0Wa i VWD@4Lb{ i!>&d/?p4V˷B~Ef%N -9,hG-A4 DȉT)GBX;i;i!,eRB4h  AD̹hH1BX/Y;Vڐ]" @4 Dh @4h!\CX|(wYHñ|C,ҧMy %h @4h%!ڐv,>{,iVkbc!;/O4h Ԗhs.ҕP??'cyDZ 1BTDCsV2P% @4 Dq,Ցr[X=,Ni?WK4hgX}:" AD\4 lz>/#BubL NWⶉLz!_A4 D hH /LJ˴-V y[j6T[qwO{˜?qbˈh8sb:C{>UW !{NwRyh6_E1On]boDѐF Y͞{4M`,-%n-C!~j;b~~./<)d:4mXZdo!Kӣ9h8f.m, 4M.wod{̼ [CԷ4d. [b-rAF!?hX07 qxs,1woۣ ڐ-:?&T:4mTßA!DPOOJ?K7MӴVX,4dV/l Y-@Olǹ iZ6-uDb Šiv_X#+7WbZ ![8{eR H9)4M;}.Oe#h>A4[Kzb_zFfcgwXD4mX@4@4hXBz4MӚt|C,ֺ $ŭ3ZA4#bq{iniM i 4MӦ!+݇ɹ{U'qSKb愷}`>{^Ӵo*m`4?M:mbb D< i'^ MnoҙNgho&={bmQ4mwbƥ-A4KѰ%YA4#5 ޾ =fX4VJh֋U. p @4 hH{sQ[\^º4{?3C{HY/!Dέh^Lgho8O-x~ӾN\21Q}oz afi! a!LKm" V4<2d2SXb_!>1С֥W32`kʉ+^J; XU!{e,>#3 ݱz%N4to_o"-ᱎ%0aHCX|&VTXh0ڐ=.-ʝ:d+#4;VwsBX\B_3р! CM5qFf3&%oD0|ƃ&6]0'0Ri' HZ!ҌƑ/|vECRn_VV<0KBI 9U)V#02aX23WcWN4hѐ.3<}3Vi{5Չ̸T à&>5_~&ɯY `c/j7;bR DF0 iҏ|s3k @4 hH2υ| 8Ri' ƪ~3Z 쨇zfQ4uo0A!#?buɭUf#@+mk @4kѐ{ڗ=eFh@OjK0 Ch,Lg,V:+LgxtvJ13 B4h0#m+X=!c D M2\˃'1h@kK0 Cxc,(gѰtT3vӝױ8ójK '^ۦ偉=5A4haZͱX|rg$vi08N4q7?ElzM(wKnXLgxA&Ҽ+e0#Ҽj ś<@ C#n\kn\Ǘ(?_rHv?|$v!X CM XE1 '[_ Z ѣ7V^}a0 8i/4^C8 V\ a^{.Ãjl>(z=hP;c{˩H~X.mo5ttL{L[bDF(N[h{L$2h'}[iG4  7a}Cz!N 3 ~ǦxB_g ~#5\EKD ڟ5^ uvb< #b)bit43YNi톮WrԖ?7Xah0_ECzNA*jm> #<|hX§Lgz m0!jK 0/7M@4hhV/4` ~J;PīcqW4~,$ʉ_:_cfwXv$jKCyi,nv_h0/E硉kC3BޏvDJ7^dDÊ>ՠԓ.Jt7Їu@4`Ð6D.G|Faeֵt[>VXQiG4 O X Ìʉg4 h CI0]|W%@4?/־//};_.f_پˏ6vDbv}7uG6ˉȩ \`uYրT[BFZ.ewj_ L4 }*?7U^y*aC޵z( #k`5vDfdoŢôʉTtG>۱N7#t%,1 D@4 h'V{s݆ u(,$,Ӡ|; 'X+(MSl֎|j>tNgX้?r !ݶsھ}{+ K,qj Dàw>xk/ˠ9pr& Sx)08 gjFng Bnw^q įv"h) DÀ+Dv V!o )*{Yi!6(_s5mC~6v7ϊ-Xf6o􆘯S A iAϠnՇAieݦؤxnj@ 36- hPp4VgP~ #vˁ.`Dvh񯱼זSall닯m|WoXDu.a0 O ngϞV-[  7߄ ѠXoe'0:chѠ/S4<=sDì08 4~:K) +3w[qMm a0vu[X#@4 ?"р J;apH[jX-%4 3Ms:Ѡ0 D vD0$ ά[GL KBiNg ԖhDҎh D4<6dK7Īti%h @4hѠ#a ) i0i3=)>6 D@4 @4hP D0G7ΰdoe ԖhDҎh }/Xx:CN>` Cy%h @4hѠ#! wәNg ԖhD:M6]KNg-nnNg4MF4h D D wsBoJiDDA#F>?0MgXP9x:C{,iF4 >gqƎ zD@4 4hhcBv{t34ٽB4h hB!tg6ΐ/3hF4D DhxT _զoxMȾ.K DѠi#(tƲtglI7!c,%Mӈhhд h0+ j0 4h?- 2@4 DM#>x ii|P 7◻Bf(a D J; C?7Lgxc,Ng3 DyLg{CԳt;b hAia 3!y],n[1_a(h v 4psv4pV_U ýhAi7޽ ,#F!l0әXt5;w eY a0:șg âED@4 0%4?ĮRrG ]^:~رcGغukHN/>ڏ:]DȷҥlpYguzAQY?qvN'Gؕ+Wv܉:݉ڏz||QWU'ԕ>#ق36*tKAI+LgxuW]rI'k֬i? 6CtY8V4Sw׹vŖNK:N)uڏ:K&zc*;ߍSui:N~ԩӦEHqV嶟髯?tu$Qu:Ot'j?tub…)CQoڴ]>Nq^xa}=NNToSŋ;D]O^bEQ#N)]DG~8|Qk-[Tn~uJ'QQw8!"~)Ny} Owhر)NHc/Ht*>)N'EWu::4ۢ>N)?Mx7L(%թ#OlxNKfIq:NN_n0 l:)Y Dh ?s(ƻNgx ƚ4| 1kah h a6D 0 & D?]3?pP5 :6~k̻N=@4 D@4 C%`:U!V[0@Q#mU Cn 0X!-hhT&xςh-t_dy׮{W,O5@4DO>y[iwz.O^}-m%ȻcUDCOtc%4)m@:e(hX44M4}:bP~(XreM Dh^-[ CYZh ^4OO<7]?W5 ex covXuɘ#lo9f?f{˅!|4Vv{Dž8ݩI-! NS0?DC_bϐ5g(z ֆٿWa 2pEv|HrGxE.D;tלRh_!30jK@4h@ECj }n5];}' B֦jvB| ]ٷ!@4hxsWS{],ֆHot{Or@4 0h!Z;FOxM,jzb h{Lg+V;}|hHXu}3`(0 DW4Я}]Z]O,?eeW<2D;t7LO h0Xn(0 D hH3ur Ze"S )nXM DC-^ΰ+L;DC?ǪِjK@4h@ECka>}8tomPcko;mCREqpj76Yth-{cR!noah(0 D hH'3R_->ģC [a0!NOX.q.gYcW wPޱ03]3Bv {  ӉܹK:5a4-"kC~0JX==-\M ru$Vm6w^ Z!B|nִ!0,-}X#/Yӊ_iert0]v4pOvi̗K<+d'?/P %ѐbmV\fXm_ zo7ؽ ׺WcP _r  7 = zͦ3tn0!2+h3s lҺ _ml^  =Lgw?`:7b5d(h̶hh ٓ}(L_ o4 ?a fBz5 Wrw=w+&~` l\NsiD0Т鬼XbM[a(bo?kLs6Q ˟a{(0,sv\;C$m_׺2,X@ohi2|r@4 h"Ļ>˃'1#rMQ*B DC8tiZso^v@4 Kѐ^ȐMkGⴺ)D殶\kbq4sD @ DCyt5 ?{wSv^.d(0 DX4$ tWmHV.DfL{jȿxc!t:@4-!~k:ntk1mʫ-! D |#Ŵ3рז)fm}i!JbDs~3wJoOn9^iP^m a  h跩4X?ĮYLL:b<3E\hB|4b {1P^m a  h5}C/ձ:d+\mv*dbudۺ D0tC|6jK@4hDڇ▓/?ߏųC~M!,  h-e|]{B|xғ8_5O:)@47P^jK@4 D0DCbse_זTŮ> hCݐ׆jK@4h@EC"6XW~{KD0twbP^m a D -TyZabŊK #Ғ+ʏh]5a0:ҥK[a'@4 hHanZݠ vK}C,:A4 nþMg0@4Df[48?XAX 1wƒhRpom:< 07!&4?&dAjrhj'Xe(hpu -Jهm[?GCf}0 Ct}ުP KpKC|S,v6-Eͱx%7n`VM0]8P~$Y~}`7l\Ndڵ0؏h S4H>>d8h oŏlz!z`b{KT-A4 h8}tzcʘ?4dKD0o?P^m a  PNZ~!/]d,omWu<}`|=Vcv{a̟B\&Q醶?:@4 0DC M֖G[O[W ;bP^mI4hpu"@4 DԖ$#Bk ՖhjK0arP^m a  0 D@4 @4 P[BjKh 0 DԖhjK@4ʫ-! DC-ΙW;we P, G>{a ͛7°h"@4 P\h @4h\D D<:DИr_ @4 H @4U0D@4|[0 5a%lo apu%h@4 P[B\ D%h %h @4h@4 jK@4h%h -! NDh@4 P[B\ D%h  ՖhjK@4ʫ-! D@4 D@4@m a -! NDh@4 D@4 @4@m a Cy%h @4`D$h0 h/TU CѩA4 Dh0W'h D> h<W'hD`(N@44f$ر,0f0ei(?uY0l޼q@4 8-! DCMlo [B\*/mo h 0 DԖW'D@4@m;_[% ! UvM` CQ"A}d@"0".!# b>ǴtHu}o?רUu~:GhP[BD-! DV^m a  0 D%D4h -! D%D4h -! DV^m a  0 D[y%h h -! D%D4h -! D@4-! D+iáV[mC*Кa POV9r- 6, h VN@4nD p+ h  0 a|8Кa PϙA7'  DCӯ_|LK4 D_%ڱ%hH-ayKic-A4 DԖh`t"@4 DԖh ԖhjK@4W[BD-! D@4@m a0: jK@4@m a0: jK@4W[BD-! DV^m a h jK@4@m a0: jK@4 jK@4h@phnaHzCZ3 DCBzʭ|s^{? =zt> wj D  V^0:D V @4@4@4 <hhط a|: ѐ\.V3fLa QFЯ_?@4 6x0d2! !! ={t+ℵN%D@4 P[BjKщh@4 P[BY0 D P[B@4 @4@m a DD4h -! D%D4h -! DV^m a  0 D[y%h @4h 0 DԖh 0 DԖhрf `M7͇!c! !! Tʭ|&? 4hP> l֩A4 Dhp+F'h D[y} h[yF'hDVN@4͈6l0 -!! tڭ|3lذ@449a|z)@4 mXX@4$򖰼%贱 jK@4@m a0: jK@4 jK@4h%h ʫ-! DDԖh 0 D%h 0 D%h ʫ-! DDԖhp+0 D@4 D%h 0 D%h %h @4yEߴ`0i! !! \έ|3jԨ@449o}> }uj D  V^0:D V @4@4@4 ejá0@& [ya0 ]X4XX@4$򖰼%贱 jK@4@m a0: jK@4 jK@4h%h ʫ-! DDԖh 0 D%h 0 D%h ʫ-! DDԖhp+0 D@4 D%h 0 D%h %h @4h@4 nՖhр ph !! qoqzU0@&'Ȧ|RSh VN@4 @4N<h(`ẇ\>h0 afn[ۮa0 Mȑ#a08@4 h!! -! F@4 P[BjKщh@4 P[BhP[BD-! DV^m a  0 D%D4h -! D%D4h -! DV^m a  0 D[y%h hhbѠi5m#VZ D\ N;8DѠi DѠ0 @4h4M#5n{4DCs[? A# iDF'hhUѰ1WvaCi ?ߧOCK:cƌI[lQ6,On?1&/ ?$~ԨQl6K4hu0tЃ:(aktѕCI'.|WT*޽{r¯=zt&mV!C o"CWl{[C?zQtcÇ/I{쑿zM <''64ݿp4>vhwmĆ[xRC!wߢ{sn 4r'~aq,}-ܤw99N!o{=hРkQBzذa{=bĈ8pq/)Daw=9N!? gr< EI[rt*Lx8G;aHCAI¯u]G0V&yå-T'GVDѠi D@4 D@4 DѠi D@4 DѠ )FbM4 kGoA ڬ hhDiF4$cyKa ,o [jDA4M#Am a  DiF4 D@4hDр*ECvi/' '0Ԏ/%hت5Am arG|yS9h%h %h DԖh D@4h -! DѠP[B Dh@4 D@4 DjK@4 jK@4 D -! DѠP[B D҆CPNÐN0f0Q+дea ̇a^$M-D[y#Cׂh @4 DhPN02 V^9@4h  @40:h EÞm84C> V@ˆhHC.#Z=zta v)} D@4Cphˇ! ah0 aٳ'`y@4t20p@h jK@4('Ԗ#h 0 DrBm a h%h %h DԖh D@4h -! DѠP[B Dh@4 D@4 DjK@4 jK@4 DjK@4('Ԗ#h 0 DrBm a h@3am84:th> ql\ ѐt:M4x9f? 2dH> \h @4WN02tID @4 h #h n Ddh0&0 a0rd#Æ DXX@4$ [B +/-o h 0 DrBm a02 jK@4 jK@4 D-! D@4-! D@4 0 DѠP[B Dh@4 %@4h -! D@4-! D@4 0 DѠ0 D@4 D%h jKh@4 P[BhP[Bh мa6!t:75@4$! -^N=a0 MN;C߾}h D[y#Ch @4 DhPN02 V^9@4h  @40:h E!m84` l0Q+М e> "I4 hjph B9C&Кa гgOh DC(/$h 0 DrBm a02 jK@4('Ԗh  P[BhP[Bh @4@m a A9A4@ Dh@4 %@4h -! D@4-! D@4 0 DѠ0 D@4h 0 DrBm a02 jK@4('Ԗh 4h߆Ca0 aݱ I~'t:M4 D +'$D N  @4 Drh@4 FDѰ} v|R ѐz.AB44'[oua -"^z D@4[[B0XR9ayKac-A4 DԖhPN-! FD@4@m a Am a h%h %h DԖh jKh@4 P[BA90 D%h %h DԖh Ԗh h 0 DrBm a02 jK@4 jK@4 DW4| ;c> TJoCkhHC6%Zn92޽{ D@4 F. @4hDD DѠ`d @4 rh@44F4ná0@0q؈W^hhr#0yih@S[[B0XR9ayKac-A4 DԖhPN-! FD@4@m a Am a h@Do-! y2DCd[x,SN-! L$ Hh ECX^J4!04>Dr^<\J4u#Ѡ0fH~&qXxh exh؂hGѲ8ןh s&}(M4-! eh=8,F DѠ/#q6SShaȳ[8>?g u'qvUⱘ9Am a/D qv8@4 !huZD!aQ8ۧPrqvETM4-! eJz9qX89@4 !\gOs;iPjKGpFN.ngzpYN|3,O<DΞ/hP[BXHn3 DѠ/q:~\zD!wL-z9ο'Ԗ"J:K )8>J%K,%2&O@xjʶpYN̎R$00Am a/gW& E|Îh Oqnp'ՆCa@+aPX۲Ԫ@FqZ¨R3kQiD4 R)?O0 D>0*5H͎\(5d7MuV>֖zS R3As&h ȯJ-<؃PW.3>g:rKc.tDBΏ3o& ١h rI sN a}8έI/kə 9qzYy1΍ 2q[8&|7NSD@44) mEY^jX}!@4#=$\l-ю-њa qOˇ]gI<.oǹ]:Ξh[5/-=g6k x{?$ aXWYDXR(=hP[B:D7J ED&oOzw aS~ Јr_oOi+ D!ZCf cg>]E4 D:uޞhрVgx8[TN|/:Fxِh  QRAC4 D:AQ\R4j:{"@4e0$)yh04'_,uƹݣ(E4-! fpW/K hh~gK' h0|;ά,5/}Ŀْ83hP[BjqRCÔD2\̓hр ~QfrBIDÌ2 ʚH4 h0/qX@ *P{-0g-4qv04Z4kR8CM'c#r;yy#ϖ-DC3RC Ȉ-! 8 jF4 h0\VLg @44/!.㡆NDWDbNR1itBsp82QV DZ6 R1gg @445{F2^[v2 [lE> ql\ne aX>0 t#ct?WƁ[玊=DÆ 6aT >ϖ QLL(DѦnCu]|h Pořrjq@J,1ř^8?μYƱ[>QhAv'X2`PE/CMhc;8gl]ex9Ιܫ esq_sѕ*_!Vc(^R 3*rGN4h *oVȼk\t,SYЎ5d29J\A ek "<_Yiupiy/Pߡ?nlp&@*k a)ol'/M ᇗ["?7dM֡ش M6$A;!1wB{0r7T* ۓ[h򋕮CA4lȐ!C* ֘xlVD> l]$tmn3UeyKcyKt08uY-O C,b)n3[umjaC,on>QtyY^`)> $}dyiyK (^x=hрnCqZ4 U=} P7Xڲqvfwh5h@Q|_-_Fsh\h Rk;‡3  (vcGM٥DC`zzGGqfZ!t0. tp  ݀Q/^sG鎎Dt03QŎНogF霣DC(Zux?(yoR D_\'G;U-uDCq]C~AtDZ) a +wwM\~= D@4 NaD?q7hН5t53 upqey2 e!Z\ë8ƙQ܋h M.GqxLtLxh= 5)ߞsߊ3`DK E3OWTe!Z?_ Uw fp t0zwj 1<VW:2hЍ P,5%qq(='JMo~QqB4h@s!~ nQ<+J®3UaPan$Pgo3+*FaHgXQ Q:HL"۳MAS4M -]^z]E4+\4M#Ix8c4M]gtSѐV.։i@4 (Y\6\4M˷'Mqk}דuy3'GGܻbiF4h@ukMӴZ0g~ṕęiDϞQk6hjFqe% s"h P&9&J=N7h}T{2΋R9WVP_4M#@4LtGG im-{B, 3ϬIiF4h@8(3+iZ0g&xQg(:e4Mӈ (0QvAӴV wƙ׀NѣG٩~Q4'J0μ8h P>hJ$N*J:hֽZɁxzz/]giDO o8΄۩GlXR~e5Mkz0J{4qq&'F=hk(})N_g;5Mӈ Ch/'2a@$G# |dy8pސDԖ P[B@4h%DԖ @4@m a%D P[B -! @4hjK 0hD-! 0hDѠ6t!80JF3`a0~ hh5h5h5hئ ]*;Aa-R (bذa0Xh W,ovha@BAŠl% P[B@4@m aDԖ P[B@4h%DԖ @4@m a P[B@4h%DԖ @4@m a%D P[B@4@m a@4HjK-! @4hjK h^viCW`w·!J a!gwfmaݻ PP3P3P3|m a0Cr|8 рayKcEC`;X€jK-! @4hjK 0hD-! jK 0hD-! 0h@ _~`v84vک}9E3&[lE7l>OE'ƶnQF~> %:ѣG'~*!C?~uoI 9rdwm¿*{]t %|p:.'{!O^:蠄χ?XߖӀ 7iۙ}cT}-E[ne^o{N"ĩlM^.{ݫW{]^a :la^ShwtM "DɛN½Bɽj&_v~E8:l@^^{=lذ½CtWA+nҎ;{.d%h1yGQCMЍE_6)+\t%J0u޽KuXJIJԾa<'Q O(0E"D0,.v%-LlIpi+) Q*h h h h h h h h 9EH㼖aa&6aI rm a0C_a(zhjjjjрka| 50 aȇhf a La0x> E rh@KcyKcEC`;X€jK-! @4hjK 0hD-! jK 0hD-! 0hjK-! I @4@m a%D P[B@4@m am 92T*7AP #F:th> @4@4A4A4A4C nCW`РA0q7AP3"L)/ ѿ| J4 +D;V40 !  ! XyiyK 0hD-! 0hjK-! @4h 0hjK-! @4hjK 0hhDԖ P[B@4h%DԖ м߆C: ( C=F{ E0_o DDDDDD>{a3f0@PĨQa@4چC& ( CϞ=F a0#$ 0hD-! 0hjK-! @4haPN@m aDԖ P[B@4h%DԖ @4@m a%DhjK 0hDM2dH> q a!Jga@ ȇ! h5h5h5h5hز ]#F`?6 C:-Å€"6l|z7hNJ$"v!+/-o DԖ P[B@4h%DԖ @4@m a%D P[BDԖ @4@m a%D P[B@4@m aDԖ P[B@4 @4hjK 0hD=zt> TJo0( lVo8F~aӧ PP3P3P3|ta0@ TрayKcEC`;X€jK-! @4hjK 0hD-! jK 0hD-! 0hjK-! I Zui] 0 P[B@4hѠiF4 P[B@4hѠiF4hDMӈԎ^mq7ZPR E#T*7h4M#@4h4h@CqMvꩧuY)s>#Z`ãmtőhѠiF4t1viדf%C]`A g}g͚5a„CݚLJk>/չ=#˹X 4MӈD,oeԧ> O;vGqg !p5 4Mӈcy={9smhYN>o;#Fp&D.jr"L7!aXJ4 E1fe]ia7lDԖh(°egyt^Plּ$@4@m a wq?~pc pk$8{v&@4@m a L4s 7'| s1qp Rl&D-! DP?>? dxD Bš- ׈>|6jK@4&<+M& !&ȑ#] P[B42/LؐhlVD-! DPhA p&@4fm[O*А0 F)H@4 h  DDh@4h h  hCWQa 9 q Dt[a 9  @4h@4hD@4 h%h @4h @4h @4hDh  P[Bh D-! D@4 @4h @4hh @4h@8x=tZoCC@44szhՁh D @4Dh @4h@4h .D @4D D (ɘ1cZU'Ш0 \.G4 ƍ%'tҚÙg믇_q?O7W]uʕ+kve 7h@4(' pΣ>zN2:-dٲ7h@4(' [ ﮻ /4p0YO?>cdٲ7h@4(' aW_җԨGN?zSw0l?ydhl h @4 Dn[>֬Yկ~uҤI[ou՝w̙3e7h@4(' s~k^&w-a |=D6@4 D@4hʧ@3<3LP k_~yã"zh  Dzhh0r9۔OP?~|}?[o{FdCo DhH _'?Y N٩u͟?oOȆ DhЊS?i:'SW_})SdI7h@4 @4Pjժ:=]n;O̞=[6$zh  DDC O'3f輎:#^{zQXb…ƍfȆ DhZ{wiuެ7VXQ睺Jl h%%`yZ>x[!0Ac[駟>s̑fȆ DѠ @4hSpϛ7oFܟ|ɟ9/;xꩧ:iP̟p 1SC(.]ZqQw7WgqƄ dD@4h D u~u;SN ~V;\rɓkKW\qEŽ}oѢEofen>}l4<zh  Drhy˧vN=Gy$4^xc~WF.X`ĉ>lܱ믇eD@4h D ]| L4k]reMʧ(r˖-l{^x$ ~{ŏ/^8 hl6@4 DC(\s5ju]W_J+_o]zlzh  P[B ]| ۿ[M ^ ^7nܓO>W1~aYfk`(9hl6@4 DCW*gP}t}͜9VVxs կ~vԅ^\ʆ  @4hm@M^A7j?aeUVU%??ueXljzh  P[B ]| Ӆ9,'L"aQ)Sۿ]tiejա0 F7h@4(' ^o[~?#j1gu[oUf=q)) ?\]~ul h @4 }hTfCDC3O?*~U!Ϛ5k:ꨚlL5kR>]?X͒K,)|C6 A4 D@#!@4Ty睷|* ?`ڴi/b&wo.X%׭[7z=!zh  DDC/N;Vh糟/ºoCxb„ E3I??OpWN ̆ ݖ\8Q||:?N_gJMӴlQN4 &hlٲj' .^4? ϖ3;Vgx _ǐdCo DрnE .ӿDӴ°@fC;fh _†Ot߫Z}*' aQl h EɅ rAӴha3Μtoiz4DC|UN] ]t;S\xᅓ'Oȿj~7A6 A4 D6Fuimn38PË-HtgT>*׮][ٷ*=؄?,{9sF7DCOɇvv*|>}Jh1c$~-(fm}gcIcvm??jԨχۯ{f ѣ?V[~~Ȑ!qQC66Ʀ颫!p`ĈCtA k"\bEW.$^RT{]b劮DɛDtJ'|~+_Ŗ[nY_rWZ5Yucy7*|0y*qѢEU"zê,Ⱦ[T%/P{~O|=z.EO p ?P W~Ů|1rgÍM^=TGVDCw c+g PThm؞3ΜD1Q& yo#D?(Ȭi88vv6M!1 3XЉ^L5'G,dGyd[r 7T3]w5cƌ2+YԸ{gΜ)uˆ ȑQjUƹ0s(5x# U0(Rgsk*z)\Lʼ%x+/rf&}k?e^_bgj7h@S0J2,sřqч(vjCo#eCRsf0Wƙ7**{f9兡Q։hH;^xU K*~ O뭷*01ӡo馛y.8Ɇ ˗tͯƹta[sk;>l8C61DCѳgqF(6jlG}t]& Tկ:.Cjcɒ%D/zh @4tU³ W~=Ν{;J?_n\SCi}7Ikl;aͿH("[/Ɨ_~yGĉ{tݺu`FfCo Dр"С7&‡)l DgQ| DCkG}4<s0⭷oF5Fvi 9s/_^WU͛WAoX"L( A4 DDf|< /k:27a֡ [34 O<ٳ+;SG5kV߻xj?^^ˆ  ]mhiG,Íqouا a@7 C3:(2ǍDC(P^z NZW'_~势7O^q'e)xl… Ǐ_ٷXfM_}7N6M6:)zh 4 ƙ2oߌsck> Eʞ!8K(„y{l5p=T3??gΜ.zw*0d5.zh @4tfE2-KqnL&e @4°[?Uk8ĐDCkOa:I&UGqkVu18l@ XhQȆ  Mʀ(z'{vԏDH.5OQ*-7bŊjϮk *+ހ7DC3^x2 ja5Xk(n]>'?vz).]IroWssΕgCo DрƳg)qhр C20,iQ cP ETw\0lw]f̛7dN8Q6j A4 DO0.oS:g5DZ- awugʧ~*w_zM{G_6@4 h<ǔ7qg Ђa*ά*c;B ݨ|z”w]o̪U>OdK\- suQQl h `C8CLNDZ0 aP8d( DC(^|ų:V+,̟?ٲelϯ3gdc.\o6 7hPX6xam;f @4ð_Yd j z~Yfp߯j"?S {j{オ7hPX6ߔ8q вa+,5/gʈ/~7'fZfcյ-Z6zh KZ05&q!QL]Ni'>~.:ujwl*4!^zi ֲD@4 8SR4Y[ :DeL[O5Svm't;io0c7 K,- A4 D`GъR@٧J=-SؒP*_|ӧOԽ&w3Ѱnݺ0رceÙB4 0Ǘ1 g2j8:DCOn(!$7o^~ [\uUSL g @4h^꽉qn' ϗz8GO o!, z藿._Z'L*n)ٳgˆ3h @4tUzĵqfzӆv-t4ZaaarDCmkvX慢Oݔ>Ȃ šL! ])e71.[epp//5,ύR-Ow^BOݗnaڴiL! ]Ktqnˈ  a}8έI/=[; D?5khx̙#h%UM ߊwgO4h0..q/@4p4k֬W^yOݝEM0A6)Dp]UjQhPOfDG0toJ4 -\>Iyn/n>TZ h Pɖam\!@4@hi) |`[8 DCOڵkhxΝ+h8LK.N4h0e}7q|>5Je[8 DC+OG}tX_O'ʆ3h @4t%/5  ygV%|D)p嗯XED]w5c p Dq&Y4\D!QzE8ӛh Z|Zd{"aGul8SDCWb]5 P[h#8ۇh Z|:.]V .4il8SDѠ jKCUOl?hh骫ZreK{w̙L! DF4T÷XON50<%Ĺ-t1kn eر=_͞Y_|^$>j6xՔsJkA4 DhgkKOqYI4ZOzٲeU> Ƭ~CK+Zo DA#L)Ѱh Z|~jժ0C=tˆ3h @4 *:+S&Lx駫| aݺu `u'|=֮]{I'UJkA4 Dh@4 ʧN{뭷|45kVnA0 )SdÙB4 h6l -!! Thh[n "VY_{SN/ZzEf={l8SDѠ cyKѐ[Z4y^x& .?~|7矯rþ/X`ܸqLi2]y4s=i@4hЈ D@4t)˗/VbΜ9 ;w߭roiӦɆ3I׾1q ܋/RY2zh  DѠiD@4(>=S}KWXQ^sΕ g @4h AӈhP>UHx &/\tEaqtwK_R^QZ h @4 D DѠ|+7 L$?{U/wu׌3dÙB4 h4h O[ʲ</^{ʕ+ܗQG%h@4 D DѠ|jwLYpoY|_4iڙB4 ػ h456Ѱ!{G2分V(k>SN7n\w'L3τr~_2h@4h4h uw]9裛 3,[e;?O\Si7h@4(iua.*ƿ[O$s/]j9sf7.ɓ'T6)D@4h DCw D@4(iu @4 Dh[' Dh @4h@h D D@ D @4 Dh[' D@4N @4h D @4 0 FI3`|8vth FT*E4 D4@ꄮ  P@ u@4hI4@4h R' Eh E4lۆC s> Q h@ C@uB`yKh 4D:h A]G4@ u@4DN @4h 4D:h AIC4@@4 @4@ u@4DN h 4D:h A]G4@@4 zaTcX> THh(, @4 P6Dh @4@I DJ$ѠhH@4Mhhг FI#  #CDh@4uh4H5NH,o @4 Dh[' D@4N @4h D  Dh[' D@4(iu DN @4h D @4 Dh[' D@4u@4DN @4EC FI#  M82 h@ P@4 @ h% h Dh@4@IN D&@44hح FI뮻ÐJ$! DPSԡQXR :!ad%h@4 Jjl@4 DѠ# :h DDR' Dh @4 Jjl@4 DѠ! Dh   :h DDR' Dh@4 Jjl@4 DѠ# D t=ѰUfȑ0q$@4 th (i ]@4 @ h% h Dh@4@IN D&@44h҆C &HhpdHRDh@4uh4H5NH,o @4 Dh[' w??\W_}ŋLO AIC4@sQG͛7N8gϞ=mڴ &t y`ƌDhP Pc|;~vOE~\s\q̙cǎm [EƟuY'NT @4 Dheͣ*|߽*1m4D#`QfeIAc32AdY Q=랓NI:L;oV<]j׻ߪg핕z߮}~L!-O>dϞ=fbT 9uֱ h@4 Hi @$ R* >}z…>hJNVѰs޽{ D) ȱDCECv+=Nhؼys;]ЫW/\D*h@4 @@2 DCǏ# Jv?~2q6g#,рhD d M$N"%!H{Q 6|;A4 рhRIh^[48qom ZD0|pDԛ@4 рhRI@44hZ`-خ+/cǞ:u hDHiH&AL<@{ӇtJV\YRhDHiH&Ȣ_L:Q {rқD  @J@@2  &M.j0aBm\wȑF;kƓO>$~ZCz4CUCʚ lܸq%& tFZ2d3F¨wmUq#рh t )@ Ip|3u>AY*gϞ~J"WX2SٳG|'K,1Ao.K^3_{ѢE.z?TOͥKꕕN^Bv#G^}5NƏ_߿j.oݺu+CDCUnQ. )}CKK WD4 J;)S(W/\(ygڶ X 8/o0SpZEDLADr? IecKWjV:DkQB\΢lڠQ$D̙3=#O8s&P ,C2c\^tD  RDc3 F Jwe&^+vhPO]GkPsѠ' x>YgѠ6u3gNΔ%hY~bÆ nE@44$hhOA碍/>p@!Vs(K,"T5& VEե\L=EluKfr̝;Iy1wD/:   DCD)˫P7n۶m޽JwTjg֜S;cOZ4ܹ3̡;v.k:}CK-bws#@/ рhRI@4H4TyE.m9wݧOiw 7`ȑ{Uj(PlO\c"^tD  Uq]+\R.]@1Hрh0,>Ak'NXQkι|jRDË/A@b QbF¨QE@4P[KCJC{KIp|3޲A9ԩSwhbD͛XbM^tD  RDc3 -Tp5?~|5)}BA<+6Ѡ25^pXD/: D) ȱDC;  *hѢaÆU{9~%]4۷/J96 @4  4 f ) .]uT 3fLI>ѐRPN:7辴]v-~\ w~_~}%]XYΝ;_7|s뵠4gA뮻#.Rw޽/{K.vmůг.Yjys^3_z]xl6[#wy>8?~ .:'җq/4; ~⳾+gdb^G_o=zLZt1q_W1F>k2%g}֗]vYG&rI7QYk!Zn}zAYYG\kvԵ+ W^>}k%!K.)M>k}uǎC:)D%7 7QGhЗUY̝;рhHH4[.F\iG1OYQh( o%l\sMlas=8^7.e7ݫ?.۹oпWkqB?lg?(,рh@4  Dрhh0t{_޽{ƫjӧh@4  Dрh@4    N +XG 6ԭ$$ѐR6j4 Knp<oj4 j"B o$;w4/1cF}\"JD5R@4x$-5D@{KDC{ ('ҥK}#yN:e zt D $h@4 R4*.1^Ŋ+*CSЎ+W8qBD hI@4  m),X^СC#FqT~"@%!TawIϞ=ǎR[Z h@4 @@2 D!բA9[oe}K/4n8^z=)SV^|rG=lfQ6kZ 7|̙ڸqݻ?nрhD d DC; =61^3gϞ>}8sԩՀh@4  DD)߿fxA5Jk Dрh A496h@44h*  bv2Z  @4@@" DC#ѯ_[cF48ۇbĉPnPˉe˖߿MDLQD3KhPuA%r+C iQAȮ'M޽BoO(4 &(0~{42u*E#<hD@Jhrl&P1j03gTc۷ܹsÆ ϟ {^xPm&6o޼cǎm۶鿬_~Ŋ3f3fVL рhD d @4`V44I$8|G4    @4) L рhDh` hDHiH&~i" i 7¥!C&DC7C.C4 p ($96f% рh@4 I@4 DyH&рhD hI@4 рhDL h@4 Hi @$ рh@4 @@2 D  hDL@4  4 f hD@^hrl&рhDڟhb+\R};d:$PV D34$f h@4HidD HDhD  @J@@2 @4KрhHh 梋. }h@4|3UXD  DCҐޒ$f D  RDc3 @4рh C4$  h@4 @@2 D  h@4 I@4 D) ȱD  @4H&рhD hI@4 DL h@4  @$  h@4@ ¥!޽{ lC@" % :th рhv&d  @4) L рhDh` hDHiH&~i"  ID  pV44_W`h@4 |%Kٳ"^~13@4 priHiԾ.h@4|3wyhkwΜO?C19@4  4 f  #FXv}bŏ=Yy@4  Dɓ-ZÇ?$ i &Mj}eJD hI@4 \ɓ%mzMZѠJ~pΜ97`рhD d f͚u钬I!C(ZOg ֭[2DaрhD d 'ΫW^  ;wd)J0h@4 @@2 D#Ggl;vmN4l޼8H?caрhD d /| 'N|GHR(TCu(,YpabрhD d h'N8SM66A̛7?,)=i4hSj'xw s+ľmy@4  D2|GO z>{lpQ-Ky7 FSrL6'e5Dڷh. )_\рh(fd2ڵZ <*$ 6s5DڷhRIH3Z4{WJ:߿$=t?NfQ@IaD @J$gʕQzZ~ԩwر, G4k%hD @J$$ jɣ>%ou( hhטZ D HI@4ԒS xgra˖-w]#O€hhh&<@4  ?V44W_}u $  !"*bǎ%6A (-^3<3qq :T jGIhٳփ{nњ$"A3#5ys]2sCPEfXӮ0rcƌQ*jÐGVr„ Iɶ% h@4TEҐޒ$f !C,}TJWXիW1X*U<?ŋVj0ECYC}9RhD hI@44hXlY~u1eʔB`|<鴞%3g!(QoH)LDPZ dF(oҥK1HaE-Q4ɼJh ѣG+ikoμ /T:j}.>"mڽ{~K h@4 Hi @$ R*[rK-,/N4\j/Wsfb3y MRsrLݹ27S>޽{K*QMZ-բ^s[sε&t $>ԂEn(Y.uV-PqS>}zvIN<(jqDDhD@^hI@44h4iYAr)(0evT8Ll,X,N %rlP *XSg r*k#Ԝ*mzw/%M(.m0OAI+A +Ue0|0`рhD d "|(~6]kg̘{hVk?~P [E}SOѠ'B!:%T2?~u B4(."ݛ)60dT1V рh@4 I@4?Ѡgfv!3%ЃM`. i&65kV{ټynܸQ["c_]c0u zYӚj~#_ʭW?Wj܏uxkCg{}RArt0kźj@tFǜ̚5ZqʙWE҉k6F$&9޳Pk0OAq Ε\Ae"tiBo#QрhD d /LTUVJO=WΚʕ+Gan׿ݦMo ѥ0u 2rKnٲENK${u=8JKH.Hߨ| XǬvPnnXCQvH[԰e53[I/vUoU[ e\(^!-A*t񨂬DUsa@4$ \4h/w0IYjyEߟ`vm=ВJF(& tQiG DR'hKCJsmf:$P͐ i _ܙi*m)eFr}R(1?DEQk H-]3igǜaڈ %RQxo$Vbԩoѣcw3gYhV[<рh@4@DLBiGaf=JWLElج[.)<|D7J$*'jgP 3D4h9XQvUط==c1FLa;рhD$@?nO%`.%uʕ,بrD x5aJ>DED >(_5eVGsJDzsӮ|{;\s[;TJ"-7:As$FB4  hR h@ѠlLeV>TҒF+㕖 P ki=Bp &9ѠX.גؒט+weODipKf]J3}t )h#`Ԭ4)ǏO2D @J$$ ~z37o^BzB+"<%-$'ƌcfUfu@f֕1 bF7gg=b$ D  ¥!!Hrl&! B4XT*3|֬YO.y͛7Wޚ(Gt F k֬1ZH` %K8~x  Q Y"sVZU-l2ShL&5@4 priHihoI@c3 o[:P5*5A9U1x*s35eg67p I` zlT uF{ _خW4XW-]iW9sώnOwC)DlZ}" @4H&Aۛ[3oذLKԼ@y`5Y˪!U2T &9Ѡf(m8@@[9G P$IG 4ʓ͠l/bʔ)OڬTjηрhD d M!TL;*ZСC:4j* ew1jU0ɉ%f!qm#>HS% &4hg*C.@=h͙TWxPx D $hh ѰvZj@ X,"f*.m$'mLJ%jQ! sA4eɴ[W-iͭ-рhD d M-8qLhyZ{16Gz_mkD4,kr*h':VXO0@4  hDLEJ k߽ܝ،1DK{7^8bLr) hP-LM4+DD0.iGj%>\ Uрh@4 @@2  *gn 9jR5qBQ0+[!UÇ'$tڬCugT 59ss='/&Z Q}LӎDuAn+`Ϟ=@4  h@4 I@4Tr0a(tot$6ǏǨԹFCŊF^2/ZJ!$]49jSRi?x`рh@4@Jhrl&P1Jzո(i`RJК( 2=zCH` `>vèQVt fbH]fa-U!IDрhD d M*=UXa޽fE3`MlΝeۅ3fu(ܽ"IN4X̛7snf_$ *M>=ڑhP>GT W^UN*3I=TD@4$ Y4h`d9({̞=̥Źr/\ԭ\\dew/&9ѠujÆ nZJMU$wkGA*GGJ~Dib0{LhрhF ¥!!Hrl&>!tyYj]'O4G=w5(:;gd&9Ѡ00UQBaM/۔DIۑhxLLm5{f D4hRIH3 gPf16h׽#~ƅ:0ɉ4nmP%6s6n{xb3nGAwXTZ֡рhD$@3+V @ƎkmJ-M84HS5D8q< V_/ 6cDtPnx3M4).xq--IHo! @4) L4hPrQ3UB"0 #,ǮQEsÇC0ɉeZ-FjEh)-Wr"h:th5yk#GT3օLA4  hR  ܕ[kCZ޸yРA|N8IT4L6:OMRc,)k_Ո@1=űc*Y4wOT3ڭc]zx]` D4h o !puHD䛡ѤP4l۶|\Uz^j}bȐ!RT &QѠ`Ѓtg2iB}FܪSi2Gޢ:;-و1a#A4  priHihoI@c3 o[VP){gmڴLTo^.sJN=+]ZT?JҕϼvX;PFC D  4 f h+=RM.T9F;/4l;TaxES>`ToXAh6OZBPR"Ѡ3UsOg CjFE)֘Tڿрh@4@Jhrl&¢E5v`hRkڅ&6Mc'z,O`)^]08T)RMk(hT3g h_.Sll̊Fh6tرTӈj рh@4@Jhrl&B̛Fg̘k㵙_;:BeҒ)V)LECZF,]a"JDZZ|:rgE * B ?i&-u*~;/ͅZ:r# @4 Hi @$ *E*#WOW!=$r Ll)PON ־}?~\Vv FQ]wbT_wAl_5w_o߮rfr+C4L4I;.Ig+D,Y̫ =Y2ܧ7I5`MlV3ND  RDc3  57Ӄ'Λ7OY'nܸQ ׭[ګ-L:vQ-9:q5o Ym1GSyS7LjvHp̟?_yiZУ{a=zf褤6$AkeOaT >\1'F%Yt G}(D4h. ) 7C&DC7C[)HDC.n  kޔH& D @J$D DHiH&рh@4   D  Hh\+\R$f # @4NҐޒ$f D4 DLрhD  :DLрh@4  DрhD RDc3 D h@4 Hi @$ D hI@4  D $h@4  hD@Jhrl&рh@4 DyȱDh@4 @@2 DD4hRIH3=Z5\z`Dב$ @4) L рhDL h@4) $ рh@4 R&lKCJsf:$P͐ hD!-ʥ!%I$8ho @4  4 f hD@^hI@4 рhD d @4 рh A496hD  RDc3 @4  hDLD $h@4 D) ȱD рh@4!I@4 рh'nKCJsUW>WD4 J, h@4) $o h@4HidD HDhD  @J@@2 @4KрhHh. )W\ HDf$@MXdɞ={<}23D4h(ܢ\R[L㛁5^ws\  hDLh@4рh A496hO=Ԙ1cM"@4@4$ E4|w{&MֳgOD  hDLрhD@4 HDh@4DLD! @4$  DрhD1HՁh@4 "O>UsQF=d@4 DԢA)h@4 yEZ 88ȱ &wqGk"# h@4D}hD  hDD  @4$  h@4DрhD_D D A߾}Gf&L1bēO>{xzһ <8xÇ8GI) 2nܸ'ꤔU3G}T' w3fСC=P}EԧOرc{9 @ݻw7O24hc=ֶ3FC4  V4rlD7Dm %(_bŚ"/S=u={>}'|dYЏ(]xmy}{}Go+/;;ZzƒSyzR۩S6m4~(A5}7xn>عsy$.egϞ9guV RMM֜*_~w1c2JJWmҥk V*z?vڷ~?|jUJDoZ/9ӦM3oD  upri ؈/5qy! EQqNGc%csq'RO<~3ZnV^z8\8/%2W.\\,T,L>UVP7Q믿nFN9rB{͑Ǹw}:zh_:ӿhIBwP.I8S J\j"L*&M[Dh@4  1gQ2X`J_x=heܹs+zn Y>ݻwKaT:-[֚ErQU^Y=2Pc_Ni?nThB9 ۂ ե.0W,[,2vОѪ Z`ܚj )?~?[١^6p{7bn҂C4 R.L:HXK#&ʾz35B6Kk•)9КEGϊ>|eh9keG9yf3+#%NԦBӥU 1R?VhH{YWH,hD#OQy/|nuDрhh:t(b˺p]î2.ao߮zǶaÆL`!1[Q_ 7}U|0kqJEbAp,8N8a꒧~k}VBQNhՈgyg g͚UTB4n~nрh@4  *Yɱ>7.Nj7/5XدGJt֪~{j#[ BmA7NABPG}^̭ cǎEQ+*MCj̎ҙj=J`ꌤSY4W(9 U&_ 3 [ w mq &M0j`(U #;*r hEðaֳӝ/ID 5>~W+Xh@4  IPx3VT\[ZOb 0@cjU/qlIEeʁDPrOz)VG\eO=K)X0zFI#D\^05Nڙ:qՆ4FWM,АB99zm y6n/WS-vVdڵ\8/L-U. dakHF'4x0j-Z$ѩSݗk׮ů B;//w.wR9:w\/~|ůן_g MxsGw-ܒ{a??>-|E-닿m.ݻg׿..+.]tmBZ_C֭#nЗ5׾>뫮}z^x΂n}zAYYG~;?_Q4\y4|K.7;vz|#nVi7n{}|Dh8]qgqז D;EQ.Z2%zo^#T}:_e'zVe$nѠJ1OHn*Z?BnEjúP2 3gδ3Ԟ-w)詸5VXHP"Nk#nbXɓCںDESD9]r.#bBC4H^X CFBU4s?Jok)?l[x@;݃/!}K_Rh6Ϲzv.)dZVPo>fwUzȎRrv`cBݴ!YW>Dy0-=*(OוZk?ur̎`}ELDQ6B;j K+7;J"hNgUE rF'zU*t:Vy\W D]WKuCg x\GwD!!(ܧ_:ւ˽Ҿ-4G=(K "fwZlo>3&eRfnX_ysKP&Pã|&D]4MpS[(avXsJSz%ֺ*Q*oDi 5*Ńh@4I4 ?~GzhHhH4P ) D գUf^+˝oSFDm˕3Txne](jXVN4ꊛEvmݭClnTŒ  VK /аF ^F7Zt ))֑0d 5RX@4ppձo'E:=a=\/l`(YhHh((/5S(bZ|y-{7[Ws64JI['F,fz%6ƍWAH )(C6Z7U44K-Dl\bZ"T.&t/I7ZDѠO)WfUi*Uf@4 88oHyA\' A4y6/wJ[!$zlZѠyEM;f j&{f}.כ(A mfy.8Ps͒W{Qw5;}8-[9*Qͽ  EBhDo(A>P͈{a h@4pp4C?w/!5W$J= kyEb GKNKZ! 0QS͡s{3KV1h+ThчlСC* 4k-ʥv4KXf`OCC{b ۹6q02͢!Jj&3f̼yT/P\W#uekwx瞋D*Lo≆r{kĉKfIY@ܪ{ef*% @ Jб n"%+F`mڐfKNGV$UUQ D{o 7~~H(TN?$ڟiň7/k`@4D ʫUn}iE]'t@QmAi j+Dh3!OT41a3(WWBK-:] -v0 oߩ5:f r&zo.k#`,TLCOD b[sִU4)?7~WWHϡKe=T.>M"T?s 7y# m_?9D!hN~\ kS[WY4j(bgDþ}4Ոkai#}dU;)J9hݺHAh&#͚@kE:7OF3\B4i̔^Pn5_l kl/+> -|Z.LRemқS4XW4HVk[Kw9 v%6#X>Y8uT.^ @y)C;&X#{쩴D ECWݕpfG *1ܮ_/ߺt{m&RY?yźf]hhu \C4 # V( -Cs,^3`G {`;,;VKvؑܭd6 m(2yds+b Ded1at ~B!TjCbe@4)MXLNl=f ӧO7 +BSDD>%5Rys?LA={yj6/αkSP޿jb?_z6٣ ڟ̕6[ZѣGSрh~aޚdshEP$B@4$LJ!ZТAͅ (>!oNѰi&̙Sօ*Æ +K^Pb"J2e養c̝;לS4CŒI FET\"Xgd$@44/5BOƪI$ǰ$M~ ) %- DCEZ`]?@9Eի҉: I#a\\''VK,re˖5Ն.SPk2]fi {NE$Nl:t$ҥF@44 C%'&"=&8}(.K,U?߇a,'ڎ]v;K&iӼ߰ :Y[\mHDC5ӿ߿VI]s?J 4OLäI?޽[yI:@yIA8C3BdpdEC~W6Jb 3f̨~ ~.4ފ7%pl>g ~.dsN(}jS I?gz.IzI]s-n7Wth2;;dPb^.$#G4O捲4ƺVB 2f =JUt( w"&xOpXvhz$onM,+k .#& #GuJ̯JMАTQSa:(U( ځ跃`dɒj8m  DM0Z~Pрhg|}Nr=%ǎmP= _a~7}՗o:!B <#,je|e@hŸEh#?יB44h¢Ebdwy=M+vV~- ӦMAK8p@{ JՈ$zhU%6* tH[} hhpF6}˿udʷx{]y0x)ݙޱ.ɱomrDC3Fymͫ,ys+Aq:`m8RZ_rRwAdv'ڔQ -תCSNghxJ48[fU+Pɋ5  oӊ/&9Sa" G.vk(* s8S?!_m4ٍ22D(N&DCrND,WرcIu3eU[ݻw< ߵk׎;J&qhhPnyiG-s-Ѡ[$WXQ .zhS3}t_Yl5/СC1+,\КK߿?MQ9ˠwVk(aBloMb k׮mb Da~n0@&A*?uE G㎰+#n'ek DCӈ=6;Eٚ  aDaqLMhBˤ)j!uҶ(6[KTT ^BERVue(єpCr1x)DXNYY  J߆M<| Io8J=HDC2u4ˠc8h $z_ЮOԢz%zdS>Z`Ʀ̹$kr Fm5(A+ O2E>gƍuk[fDУLbtE7=q=,Ll֞: X)Sh!6\?~C{E4r TѺD'v#AX!i^⛂ -/nrC?98 ա 2leD ѣGTrӧ+U.I bԩK*%h0OxC*&(gd„ h႔A=&~cAl]G44hqvrʽGQQ#Q23Q`Q`۶mkݍ)C-'uJEY48 \u ?:9G4@zDe >%!.Z 3H ?МAL<ٺ; zA⭊'OD4X8pZKVh4jc=޹B;)UƤitop,Ũ\rl5 E4UDo~=# =Ako/H ){sE 'F$uu"GC.qDhH * ==?mw{{9{l[.W h@4 MDí^Ix/H}9u(Cz뭷%kC4l ].P3?o@% оECP 2v霞7x]viG4@Esӌr\|9eLoxnݺe2\~7! h@4 +nHR4܀h ECEhHeA4 ,hЛ#"vz7?7˜ǯD рhd59* u@1Hl@4 zsDT$:^~X/s)c hD$zsDT$~2|<#[??2ՂhDрh(w|OKB4/KvPkrf~~-h/sϯDQD?6P8ydh=ȋhܲ  hDC; : poh@4hD  hwDC k=C66< h@4G4'G4 @4@4рhD FB9Uz! =0Ѡ[h@4eUh$pS?!_ueg$! 4a^# =&s;Qwho f0 hH)?s##DG4||@4D^oTU= YC HC^qFQ#_@4 рhr,$M??CD ޝf`)Pa[g4@4D)/N'=  kv;Xi9 ?N>_?8qŁhD (-Z"ᖄA /|~bw PV R=D  'a OLâ:2|+=/%ma~JoKD  h@4@(A8DXUk k6dàw_xJ9pJ@4 рhPƇi+? [U hx? T/+D  *,,?'[N=I9,/hsMz +7@4 hh >+ǨGZ;5p?_ha9 wu=gfCmjDDbhchcIA?߇a,-DD|Y3d%n +Kќ~Id/1_7 рh,c7~$7Phw#lh?[<',۪;k hh4*B oSk82\d)> Ƨ~ e DT*?RwDwPCѺc$#Ma|o]g  h@4@(_Z?(Lh ڐhO{o|A0$h`|=` tD #"~x#u [;/#tC3_ /ldˠc&R@r7;mV!рh$rybP_gO*$C*c?-y~6;&t|?$hD yɫ6dmPXE[=c#T _=/Õ@4\ѰdDEu]omDà(3mF4@BBtY%aa &M DC>}{yGѰ`.][l_Xڀh$rKm/~v| #G\hQ"~PkhG2\e@4!~ R{ƌA /W6B\z P]+ h@&>|xPRuq XiӦݦN/|׈D>P s/9  pzTU@=z4!f5!R`-ֻu-0ϓr4K??丽BBѯ6qqhxTG,ZB% d|>ϯHDT (8VAsc@u +3C͌vL-k P#biG{>~dzF;8TR_h@S=XΝhpB7pS?#)Ճ,-U-]rhTϟ/Ŭ~wuWN!hs-?C7p~~YvG.],m[R k`JP ڰ9 6yC~3G~nD`A=C0RH@44Wy[~qh~D @4[<)/[~ϫo>T*w~!e<@4% hDWhNT% hrK @4% hrK @4% hrK @4% hrKf` @4% hrK @4% hrK @4% hrK hF S7|s L ( \.l49]v%` .fрhhhhhsȷT @0A0l438[B:`(^ hrK @0 hrK @0 hrK @0 hrK @4% hD @4% hD @4% hD @4% h@4 @4% hrK @4% hrK @4% hrK h0@0 @0@Er |g6D D D D D-0Э[ 2 A0 P *l497tpбcGfрhP@0#hbt4ʥD @4% hD @4% hD @4% hD  @n    @n    @n    @n   D @n  @4 @n  @40pud ``:^K0%\uUA0`  8[a*@`} ( *E]D0%t)*"  pā@0@hD[  hD[  hD[  hD[@0-`D @0-`D @0-`D @0-`DH@4-`D[@4-`D[@4@zF+L菆 ,A0 P :t`6}k P­Å^l  P P3 P3 P3 P3 p_oUF@0 |hr(oSNFV `hiia6Jc6:%`kz):wl @4% hrK @4% hrK @4% hrK @4% @0N% hD @4% hD @4% hD @4% hD-`D[@4-`D[@4@>>A0 {w'[Uspsy8 %(aD.1(&^'D$buB" ( NTP9HD(z&\){U]W>ݵW={|Zσ`Xpј笽ڂ}/taW6DZhA4 @k xP`M6  -26H0@0[ /6Dр?%h`:B0`R{KQ4Lţ>e˖%K8}Kdkoƽ?mV{T1jcyN{}_vi|{>m~7w7ly{2iq|{?}*}v{PO{#0q?;m8im߾;ҥK}.R}}K믿~U&}٧O$z~-wܱW]uiϔ/_O3^u>aUګ@&Hz:i:ޏ;T޺O&|~c[}naU]'x^ꫯn^u^d۱{lAUgD>RXkDW'jUj^*&Hyu/3DyTD}bp*򰪾D< OpʼCw߾Sb~7QU?Tz:5UcFl.3,WAE:_Mu~端O_8M{ ޫMTNy^uniNy[~b-z:op{cpĸ7&}7ѴWIߛhڛ\sNN&<)ޫ>m8e:Nɉ:o::ҽWpʄ<].+jl©:<2@;Av}+V ޛ"MԗN;ONFl&6_EMԗTGl7<2h6oݴ|MU;)M(N D@4 D@4 D@4 D@4 D@4 D@4 D@4 D@4hp+р|c  Nbñ|LDFrK [B0hrK [B0hrK [B0hrK @4hD @4`F@]u'CkyH: N`k !Dh ZhXC x[ M젣!J/rK [B0hrK [B0hrK [B0hrKrK @4@n rK @4@n rK @4@n rK DH @4@n rK @4@n rK @4@n rK 0Xm`1YuUl… |hA4 @k 56lN0!M4 #[ M젣!J/rK [B0hrK [B0hrK [B0hrKrK @4@n rK @4@n rK @4@n rK DH @4@n rK @4@n rK 0lCڪ .4A0/X'l}lƝ`XuU@4 @k 5D+0   yΣ(>,Y E р%h`:B0`R{K  `%  `%  `%  `  `D  `D  `D  `@4D  `D  `V`(t -2A0//^l4䖂N0Fh @  5Dh р tF  јN},[ K.5Dр? ]'G  |#<' &M/ãh! [B0hD [B0hD [B0hD [B0@0H' `D  `D  `D  `  `%  `% G6\l    -2[O0@0^ Dh ZhP ]`@E0( 0 A4`D !P AGCL^jo D  `D  `D  `D D @4hD @4hD @4hD @4hD @4hD @4`|}y:taѢEFC0*b49;``@˖-ҥK@4 @k 5Dh  +0HN0h4xb1IGCe'Xc A4 O\@҉N0<0A0/G ``exm4DrK [B0hrK [B0hrK-[vGկ>կ~k_׿~[uURn q Wue}'?ɫoh%F[o}E׿+%`̊׿?%@4@n qGW3Qҗ.X-!hrKLo\}{ߚki FCzI4h%&c՟wKt"`0KDÈ8??{ozӛN:;E-!08_ŤƸr"xॗ^:owq0>CIC4h0??|~^z׻=xrK4cvHW\:=ԁ8ɖvi…o}[=ǎX{h, }r-/K_oZo ar{{禛np aҥ'CuVDCȬfɒ%'?yj Vv[ DN4tHO}ғ[$'x`M61zh?3vi 6gD@4\r:q D߾F= I47I|p _ @4 <P-ZŌ6'1ꪫ [97A4D@4 /_x/0)_r駯F G ohD@4hע!|t+a뭷Ȫ|C0`|+%?ʆn[v hZ ?O7tӡ^TM|k_kļ 5 D0^&0iY`-EGCˋkލfQ쒖(Pg?[bަ[h!wyD W5KB~:3z׻5ih D| O:%b}=_" DD]w5&]w]ѡ!hh[o;qM4@n ^M}+⦛n~6^{;.)ߤ hOx,ʉM @4S=XrKfJ4qCoW4K! IxV[m5rK l0+ D  >'w /k蚖>D 0+DCzO뮭warh $%>OLk-Z.uYi+=[#% R4dҥ^I' D Y)R__7,@4@n c.>ϭz^5\h @4h Ӟd3.V^ym<ꨣ2)Ig?pvm7&)6d yS<Nx^?w}G=QQ9;>˿Wc9C> -[oub N1!qGoZuUwy<)zh0a׽o}߼ lb-Xc Ε_Eb&Cg>}oxփaɒ%'J3 |骛N,`3s_4Di'y/zы`򓟜~dd?|dˏyc{ɋoٲesy&Iy|>w+ͳ4cxK DF$B74{% ۯ`?)a5Xkj-8∫{ؑqeEd^{_HWAiJW^ye:o1J9š.t;%/yI҇êHiwLkmLיfE/?s'?[Ĵ7 ;h7Pd+b=Yg!}5Gu-vTcNy8Wy2TG`ho~Ss$Uۺ5?ۿ_hȊBL >я~ߟ6{/~sNR,]k&ÜcQt~sRjYnl_|7^uyg}k_}ʲ͐|@4  IcX?/}AI%h tOoD$xެKӞ};ȌCPv[\zY$T>Og4ʟk_]4ϴ6bmw(,3|ԋ.W >ϓewij"?^o9|]svEC(-O=oS/j<5~3vmtyfKfT[Qes'+_i}#w\zk&@4`vk 4UJ '?^67Y]{*tCuo^r잽;[3+RstILB"+w_=see0KlokwIl͖7וiC=+'H o~}+ \ SM7x㬪aslD9SZ\7+6%roovI4kD4 DECȫeݮhȉIyjDg{Nj(0gC /ԧ>0NkW&3"{&% K.t85Tk=Û=ѐ\Nbx'L! Iˢp 7d~+' >DCnSƭ֞h +?튆0񩚊Vy4x_lVַFg^ -nWD3mDC>/#I|SV {杙5~A͙'G5A+əo#3OJ־/9oa&nDpDCvjt{&m+];%][5_Wٛ!'sԱلVk VTL>^fEC#)B1#arsG >ECZ?ِƿ܀~3-$h J\֓;V0hȣFWGg4{Ble=l=omJ_4d!V7R "IəܟuYj;7ldk׿ LweW 31Lz5hg>2+׆: 馛ߐ-3ѐΜ:s+ yk>"_wse)hWh, 8E 'Üh{0fn*p DW4tI ug=m{!ue*kk*uH~G>_ZOiAy2T1 }{ⲝ.bU}=yfZZ f-ߌFװ-Uv ׫zln6-2ح-28n!;x;GՒ9oF/9qkt}r ̙ { ϶STo w1#ʩl\({3N#rdR2LGl 8nKqI>kSz4|ECN3QD'JM"܏Ifg G2wiN:id=0<0n9/r9g3u)]>Dрq ,(lp:5s3|U޿R 3 WIAvTuf75Ur!SQ#+J!ɶfpP4d(OeKf7끳]4t28Bv7IB&5wOGdSiP,Nr#,rDz:̨RiXNʃ>\dYzրJ(!Qg*f_}8UQnU ! `ODClmlo9ꨣ, D0D,ϥ0DC+<9z<(ECBVy=Ӟ/x:vEia޸BL[i6|^pgYC/3<3wSNL' ^[>nw˖6hĽ;)0!;1.>XS2_Vwu׀g1;:]S1s\Eў۾Cc+_#ϸTTDCllP=C̀r)5ɛD[>٨-95/x RIdH=o_דG nL3dwO 8DC96*wtvpk_/gz71k5FKLG,r'T]6}on|8ZJS&8"ѐ12SMzE"?hcj>̾($?gQK31 C _3Sj!>|Rec)RVDCFM I5Jw'Th|zK,fB*ӆtW4tDjTm+wu D0tѐrWgD4du5d>񖷼%+٠JY,w}Ӹqffct=-rzECB"ѓxzիN8ᄄAJe֒ryCw65DDʗxhis\\pQAfO.ke)ۛ3W =hR/8B4dښAYv["`ROd-No~;Yg;C_]BE t .do`ZW򾫹O-־ OiK4p'Lw6>Sz -Ѻh|~jsb6)FDC~;_} DfR4l1gj!_aTy5{VzT^* \C^ՓK Ln1J9E}ڎ^4dKg2^,Ji ?LT7tSzB$VhGr4͛hQpEC*#Ud!n꤯E5wR ΢ygq i'ܛm:).&k3DCH˭<s'Z!=JC@4 Bn҉ !<ҜoRpًQnH:O SH4qf̐Jn.D4s9O~4-5լ.wVmo`VzhGe*T-jq A/x+JyISHFhiJScq 7&~syTsC~);SѾ \478 \hWjh/Ƣ!AZdgDȌ9% i`ݲ[¤%tGCo aLft,=GO# ]LvhVCoosRopE{T8Y>aէͩSfGCiQ!F "SjP̡_Vg.\4Dʔ6QJߥy{ nߥOh{ @4 c׿YvECElqH. 3`ZS|͙jrjZgf{t"$;PYFe]Zi1zҊu؊-+&\"rܸ }q'}ѐpА[&1kwHK!|(m|Nw#_J5uX)#|,v7M eʃ7;9 2+"XtA9ogO;,8獕:ftWż&K7@4d߸]Ж~k8+DC6{oo0DC;4RZ3ghwrz^yLYTቆlٹإ ԧ>լ, + <9h\# pV;ov\~dEKͭvzjisk,M4dG}/җt1qwuW_mѐY|/0D8M4$겋CPa>,O۹pkwoiofRսKhdWNqaPhYyr+jT>YT6zѐd/ջKgR07 fwIY-D< i%:u|ϐN+ak0ToL8f?E1צax*DQGU4 MrJYY*<ƣ ONix8+DC jxyH(biIB*6ܥ~޼yfɴK K4~5ndGNOIRܷNOyIڣ x# |Ϡhs=G&RA6UJO hHVEu!B}R°f=^VL"_Z-zSU,?CKW:r!sቆҒfu 1z̓O89&  A 1χKI|f5x >P&d}_{dGIjH4DCSbu|thDØ3%2+ L4q62(@4`vo9zaٲeg~keGÎ;XZJ0ofٛP"4^V'w}wWqx4."Ӥs,hD A۰q pV h|dyHh_C8ғ8z⤧ɀf\|g?Avţ ٖY8h|DCʫ. 5 YKq8C4 GѐeYk}aXv3ϐ6hH-;I'`SC|eY y I4 ʒ׸Yш|_̓s1`ghȓj /CR죴oq0[O憑lcKVι `{!aӸYшgD4tA5YcѐjɸYḧD+:*27xZшgD4d%DCde  E4|+_Ȃm'gSlq BV2)oD^Lj::ϸ/| 3rƤ%S 6>!MJ 7ܐ]tQ^Zo~_eI*9Kg# *`gh(!ﺳᗫ*KyMTjK4x;zKz<\N'ѩM@4$*$.9b~gqF^d|+";c*sE`7c94*DCj}/ΩH>_O>9 /x ߗn%/2Gkѐ! }7)W'/)Ҝ"%G Wkhf楘Rqyn=yO'aY;5ތТhȄf=Bu ZM*wԬ0G4L}yj 'բa3"}ѐ8K9MWN ҪdDa!<jUUDiQ)t.A&E\{uZ ѐ^$>RD{#D@4 Ӓ"mVu6Bnch T_:Rw%DİEC]R*{1 :h8Kg8EC͢Ȇ[h a6<,\yB6_RdCڐ7V _ǘså;h jCڐ`ȮIO=ѐ~ O?xkӛTU4kY0=J'{DÈECP΢rQW_}u鬣hHB>UzMh0!{・#ݪI@v| (Y!Rq )YCA  D@4$۬ o#C/k"yQc IS DC~4퐍3{yN˹K4F4<ˀS)r99B .*T !_Ѱ \4D~~.' g[֢Wp45>'x4Hb7 D0ECG}tҒw:N1myMT;튆=i5'$s_'򳩤Wؘv_D.쒮~)kpZ4L C!_Q 705EC*dJy&o0:;3KZ Dh-;#;~@TlL5LJZp}ecmneAh=։dh!Ҷ Irܣz}4edwygMwnA|3k4$DÁ`%/yI3ٔyfSk-h/~q-I4< OhP!'+|u76DaԢ!ϲ 2,/J_?\3L2Y慘?-!j3̬hlP!o*\phHsyYh7 oxCDCu45\S$N8op!DaDCHQϬ4 2hV6<1j>K|S2J`H443A)^4OgghW~C9dUV!lo*r w7 B3y5+E!4HgWhnnم D ){챕b z{VS+_ ~O}h\, x+doCdh y"/ǩggY$`it# hPu^٪2Kj 4$\?i'&'j M&qk'=I6;B4 fL4$v([%lu@ܤD)OXX:;כ駟C j, ys]QC'ayFB9AYOGٵafE^fGHU'ҷ^O>yڒCIn78\DC7wjгKJd$'̻ҟ3A5ECVn?'}*t ⶈDÌ9 18-Ӻ+f:4 ]{IKzI4%fk%yo?$/<b, feUW]{NyR#7ovD@4q02{:;39RJHS`ݘ< }[O4Sh?>R6dHDCw1 GZ%\///G!-%EΞI$T\&җ@4`.=W0[DCЇ>Tq`\veEikg~0Zd uwCuDCȒT~^) I2"{nznO4 D< \pA>xF-"wM5Q$njhp8<ƉY!k 9Iک0*QPɇ[]T0WEJjp`s al18ɳx Dh1 YheM-~dN<cx ѐܸ&h h(JDND/ޙC !̀?71lѐiMãZ4̓ӟC,r))B9""zA?0ytmu]~7t=Sm!'fN\z_eeߟ{UFwoI߻0 ğZrwyߝqHޟ2 X7V edfk^?ӹY|d9娣J垉7Q߾47JO V`;"zӼWO{tAg6)<;{<;W֨?(~K/-4Y?O[xD/iVzþ=jlv}7QQNOE|b|_b{uA/ 4?eOqi~+>Ħ:3ޛ"|?{*Uϝ>Sɻx,h fhcrOƉojRVgL\I&y,J_4^h a@pq4'EC耥^WӘ<~hhxE{[V4$>7&SPh q Icg8Y˚4h"Ďݷ/cEC |39lR‰h?!wqΪ|L.='ECT%h#ER~}o KU1\sM `׿^ѐF)>`fdzh.[iRk4@ hpDYZL)J#Α1Й%5M ~ֳU j4̮TabR<;9&!]* 8nY!d1#9Xy^j΋,# d:SS>bf¬cx)ԯy^]x#x^} _K vm5 sF ¾+Lgѧ/yKƤP:`]'Kîv|9Xo D0|?ǡN*[֙3T*r]fÒ6hX顆IÆ*2y|5Vh s^44y$qFC4tVOV=ٌO}"].S46C'{7Nȋ?~L Clq&Xoy;kw7{h &_˿Lb0&c6E:twVGPEC+,0-_Q1 !窆N+LAN;/j01hgyf^ҙX{<"Z*:`֮2<GDa9\v)y"gWHs9?ua1DC8#來ࢋ./fd@4 D4\G;~g{N8B?688=a:"Oo} IO'=?PC !cpO&}R mqDì +=tE/zQV; `JՌ<93&[&fs:O4 D| vw1oRJ c"R#fKN7xL4P嶬d&97uWy  JۗR0x[)MPo&:wSm5W2K:K5DaECL#Rw79g[O{cm"Cǹs8)Vz3g=`MF`u֙$|D@4mѐrw5zj_+!W򕢗iqs# +=TN?E=7OtF^>:E4L8餓Ri gYJ:X4t.$mۤ T/tcI hG>я]H&߃b"/tIOfkMLb2EK1o|cld9Ucg0r˩;<ӟrE٪mV'yGOۢۊ.yģ Oζ)nJ^EGɢ?dה%3g6hoLzUnIa, g48ꙿ-2'LF *}{K>&-LvkYJ-]w݌) '1m7 -6&/C谯"r"@4h:蠢YiyR4gϥA/@4sRFa}%s{uⵯ}0Dl箻 mݶ'eGxlh Ő'/yK=p5B;ꨣoy2n0x2p zի.]ڮe'>Qf8蠃}d``@)" -HD{aSgCl:DN_׋,??I`0+OkJM,C_C#!J/a\tEO SkDmooBw=O[h0;/U?QS0ZdAN@o{-rK @4 `hv{aSOC͢U.iy衇-A4hO>~zs>Eku։ˊF|pkHD ce~_C-MNM񅜈c=b^W^pW3<3 @4L6lM ) ]w5͕y/l}cO %;#o?% y90{wR% ƅLR7a}?A(Di\rtA hy ux }G>hsHL^G?FC$  `%  `%  `%  ``N@n D-!@4@n D-!@4@n D-!@4@n D@4@n rK @4@n rK 0C^ ,0A0/.\h49k``@kF'V^yeA4 Dh р 6tN0h"1xX;xbA4 [ M젣!J/rK [B0hrK [B0hrK [B0hrKrK @4@n rK @4@n rK @4@n rK DH @4@n rK @4@n rK 0C˗wa…FC0+l49;찃``@[ou',Yb4ZhA4 0C"-Z   р%h`:B0`R{K  `%  `%  `%  `  `D  `D  `D  `@4D  `D  `D  `UW`(  P ,0󜤔},^ .4ZhA4 0vXe:&+l49l``@oy'V[m5A4 [ M젣!J/rK [B0hrK [B0hrK [B0hrKrK @4@n rK @4@n rK @4@n rK DH @4@n rK @4@n rK 0lC-ܲ .4A0/X'l}lƝ``ZhA4 0X``@E0,XhsV_}u>.] E р%h`PN:B0`R{K  `%  `%  `%  `  `D  `D  `D  `@4D  `D  `~+0g}:h"!aFc{ ;wa50DZhA4 @k 5<=V`(nuAL UVYhsve>~N0@U? 7ܰy|izWHm]Uz:/iDUX}՛lIUoWaHb{U_uWjM{}&Hyu/3DyTD}bp*򰪾D< OpʼCw߾Sb~7QU?Tz:5UcFl.3,WAE:_Mu)W_u/hU&z:7Ѵsz?R~CUoWFu8wXNSDW&33{M{^kY}_i\DWGtu8!{ާ Lz)9QUMwU_u~[UCVSB*NU|t@́M8UG&cc'7^veܪ#6{S]Y}չiɉވĦ:< 轉>ͰG-fMv/7b3z' Ixh h h h h h h h  `%50s,Il8рH _@D:зo'Y>  #[ :з}D@CDh р Ш A0`b0u<$Xgu:יD5[ M젣!J/rK [B0hrK [B0hrK [B0hrKrK @4@n rK @4@n rK @4@n rK DH @4@n rK @4@n rK 0C]v٥ -2A0/VYe1Y|``@m]'.]j4ZhA4 @k x[ M젣!J/rK [B0hrK [B0hrK [B0hrKrK @4@n rK @4@n rK @4@n rK DH @4@n rK @4@n rK @4@n rK 0XC`X`ј,YD0@0UW] -2ZhA4 0[6l   Z`  cM7CB h]t4`@E0hb !0Uz% [B0hD [B0hD [B0hD [B0 [B0hrK [B0hrK [B0hrK [B0h DrK [B0hrK [B0hрe l6`Xp }:a  cM7êj4ZhA4 0P@0@0",X`49K,  #5 ;h"1!0D !P AGCL^jo D  `D  `рq I@VIENDB`pappl-1.0.3/pappl/icon-lg.png000066400000000000000000000632361403603036100160070ustar00rootroot00000000000000PNG  IHDRxiCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*i(0eiTXtXML:com.adobe.xmp Michael Sweet Copyright 2020 Michael Sweet New Image New Image ^IDATx Uř/pA8 h"8,8("qVPAyH4Fy󬀭_ߺk:kuJ:f0Qvw]{y<{r]ާ<BU88[!J\e #OۤQB)]:uq>ʩu.v@!Tre~; sqԨQCG=hȑ#6z#9M/ƌ塇 ?VY#F4`C0PN&Xti(/{ې!C>0`}GAn݂Ν;۷֭[yPm~_cOV[sM>xy.dpT77ۆi[ ~޷o߭ݻw:tP%(W?^^7e汾uCu7B)2Q Nafo5aMjF:~'o|0B~t28I榮w}7+4B֛(L.F a`@СC;σ|v Zj#_oȏUs;0~Azdnu&لt6wؑc?c?m lf(=]4"R:~Mr7]vh˯ȿh\.evQ6(0PsCj6[vaZt؏؏%O3nHQ5@ߌ2׃:hS6~D؏_,u)Cfq=zQ -7!bo|!P!#@>!t@`FZ8ߚ-Ś PLz`ypS*&-@4 2Je /j ؏؏UfY? } v7sG  ڔKu??~~/SW&b; Xlp\o)~~~Og6a}@h@]C@Xͷu]A`?c?'M_cgI [P)p̄Oh[G~!ˏɟ]h/0h'`k)~J. :Mr- PJɟɯ]k? "="%,}1nn۶-!0~~ n Pm@pfoB$5c?c?Wos0?Шvhc?Jh~NJW8K`.! UH9R3K_d?8L |h.W1ܚ9MSud%wBP%'~-6?耰.!.SB3u8p!O$.ZW+$U"?R  K.c# nM@ۤAgI`?c?c/ho1j40!wD:B]' 1EF }w"xzOspkR*EGS0ρ@ww. l4:CEp W )?jLJ(PzwfgoN}nP*_ !pg?s5{zcRGRS9 +8?51 g؂EY`3E{u'SWM)FG;T?+"6\KOƯ~b4+`X8+@q wPX3f̤@:|~"2O_ׯia*@TV )& P+zѺv-9 `̘8|:PAܿ#Fc֌㌴aa1ݳX\0ο SlmժUbBi8|RIpI_ڵF@W]&gL$0 lf>@p7 m8p YaA' 0ȉ͂fnnݺ:TdɿH31& W P4@Y&%4@.]N E/fbYw+_0`pHf`ZCkMS jMMp5iwu@(i[ø*;t#$~zv>'Q(޺vu@wR[Rԕ{x\CTe;WSǣ~`W>:,X@C 3Uqfߚ5pF.(h4SÆ #|rH}rg#_)zD@v}1 pW}) C#gVbzٿa>y3:"*@u_KTx76#)V3rR;?P ֙46 iw6@AuUG(p5~^10S ߄":J,g#Aܷ:[]SIh1iC 8'&(=0 0Kd . TP|?kad@%kI]l! / / T6O>_m(ԻJqM%{A}Jl4. 3 0k<ܣGr5="]x̙ `:hd;Q__? = qj5S7j(i49b:[oT|ԩS' qj1bFxz3 ۷~&'Ps_~?ON6:-B _oYg(&vII<Pt Q0~Ԩ|U)Bҏ{8b[nFxo 湮,I~"OFHת{xpQ 0> 84T]5Gt>o|D Z1=Q5I~5_e:{!*kkѷ i5pq00jO6o5-%Cq]EMtU/[ѿ`SZ̨O? K=z,Dg6L ތӣlHѿ}MT_6wk"Z hg-Mj;WM;~Z9[ o#R+/VG9vwTFR,ŀj 06|D~=r y?)>tp{Sdc7J@AXtFRU#/ۜFuڱ缟C>N9='ɋ}Vdѿ/)@jGD] `ء`cDYr}G*~?:o/۸_ j99>~u^k0cEeFN?¾^WDߋ0/F:g47a5!@}`d]~_=8\Ǣ*~ٟA{+ ܿ5Zᯐ׋*d;pmBV_Li]._#) vC vX#E?=ff/m0%%( `R]d{Se6sh@BP6h Ϸ? h@@P6(wP Ϸ?ѿ: e%x sQ-D s!vcc@RH1F%S@vGU?B(A``J :BrE` -*d߉O E:8vKHXX??" A`FUM@ɻ~[ Z?kyI@w>߳^yG jh{kl0jTi@#F)@i`$60nT۟Gz݌aC5Y`*)*@C৞{w]dlum'x'ыj;bV^fFl05v߻.z ջ2'@ 4e@S>c;v 6o8%Z3#g0zKԓ@=V='aGf[sE@UqLrXF!pZlyY]#]7#@)שt|FU .1\*KQOHU " 5@ƈȆP;Q_;P J0D@T5EࡱStͶE8]㮄@\-n7@9 iVSZg@#y] ]]\ 5kE}!?@XMiWMt iDaO+t ^_`6 4!rOԏG5kv9؆3Zk:fLEo?n3R}vSbgj=D!HWMksޡst-tMtmtXۿG@~D ?#8D B"jԿ›H ?P@   @:?7@ !:@:pB8P@I@$h@tp EO  h J O?:@O ?h@ttt"(~H'J O:h"@:h@@H DH`@~@?4 :ho"@Dr?Bh?h?@)httt"(~H'J O:h"@:h@@H DHHB@Ip:l@%YIz D Dq] /@ !Pj"X?XY?HZ-i ?@ h HHi0сPh%@?PrOԏG݀JOq}~K'Cd,сPh%@?@ o~ b? )?BtOI4 ::@)DE ?)@t@4mqOM^4 '?xPw:Oݽv?Pax Hh0mWm9ihn&6iq7Ʃ&~U-'BH0,jhER*ѳrl(G @6/70$ʭ^SmW(+#59bs:_Wg#@OrxaHF!t_SGS*/B~?>F!MBWu~Vh&28)IoOX%-9jZ $OvB]P%SQHO2UZZrCD#ۘ!' R쳬}Gy;7ْPs:pRHdϚ5+xr`k{BBI>(y霷5QHmMmNm([paAT?)VX~a"CQ Q6H ntyG[lIw*}P̉76dPN_[nݺ5ә?~g iK/m 6}]wݕPse, 6e[:5"Ep_(pv#Nj[oij]v_|qj'L'|cǎ ;U+H_׉p U>v)[aXW[S; )矧BguVj9-g5<V\2Xܥ;4"NusFaQ#׻DP[S8scݺuiƾҾwڵproN #`Ta j@yNioǏ⋼iEtBIrsoGv1PF͔ꫯ)Sľ̙EكסZr?O!.8@2&t.P:M{=شh߳aÆ߳fH@x(,ѶٌO;+j<kxgʙ dt}Y^;4iRm^WP /tkbXBI`? P@ !@H 8@8B@ "@"@@RBER80 80 R88@*B+;.8pfp`RpNW_}ujժ3fΜY[fMaÆ'! \uU)I=Xdɒ#^{Ϝ1cFI Gydp0PHׯVXQ=zSNO}Ǎ7ވBd>;5j;vl*OI'_}Y׼Iӧ^xap 'Aw}s[q <̞=;.袪uGuTo~ 5\n?pPW^ .lϝviϙ3'K{'xꩧx 曃SO=5/^\d()6^~u |(C?xpw_|q0q៫"w^(XvmΝs=urq9O`@nv@ `_sϥ(ߟzF۾W20[n?p_|TD]{wuW0k֬W_a@ Hn @HܾN(xG[8^z)U?>pԃF-[zu{Uw:5rW8@jY-[,k{+Vd|[?sTDݻ@ ~%{>ՂC N+T%ԲƎGҒ6P3sSNe@HPP B@pE 80R! )R\{b~<q|>>WO׭ZOKP#f @!")PXi?yG| ;DtQG&LO?=;S5kVs=lذ!x7{n͚5xسp”-֭KHJ')G)tM5GqDk55cƌ8qƥ_|2(N:f#iy5xSϽUaR/r~`ɑ9se]Pnk9S0} / N8ᄪ8y5_)SDF >촑[ǏxQuN=PM@+ %?!9̙;뮻.%֏>`Ŋ?](]ay{ zu]իSn{7xcj?:ߛo983up1ǤW_m߷KeܹꫯN[x)du4@:묳RZ_?/nw~]TM@ڵk;u9L4@z( !ΙS ,g Z~ e*ZwVLq9hӝb!w3$@@j{[j*7\o;Z'?:|FuS"x9{"ŗ qg( ?vrDټG]^ߣUX\uM9S k4*giI\?>͉W1"+WLѱܥ%MMk+7sRN_|:O&E;fB"+V`?PG)V2Kf>}'n#%9b|F!Ѵk[Mz$JkFQ{6[oxsz꩑^?>@  \7g@іƶo76W6F64lSXBu r/b~`ĉN6{5~RwG?ORs]*=-4=qGMN%yw?l~ϵ^"g sB(rb_F"M]/٣*E ׿K믿\@{u]\TdVP:[4z7ߜ + Q/ PZ \'/G†2=ϯ^:.ڸ+Ǘ,Yvt/ꟛu^{]prBnA}_.$Pw}wl =J >TJ ʠ  V~ugϞ+|c,d}0~:6gΜGnh~͊+z;W};#Z7:w~pG;#2JߡѸ{]?}f.E廑ECTA``̘1_666uO:F^i忣@Q9('o?KN:΁}[:XG UʅG}ؕ{!t>WNik|'ھlٲ)we]PEi7HI=ߕ(=֭[WgF(wV_Ǟhl6#/<:յ5ӵ"n!Fk^Oe E.V5ZԚC8Ε[{k4ѹ5GI) 7aS1CUjol=!O-(o)Ӕ<֭~ mjG ^ V675UZkS38) t ֊ [\KeaUI {GIyiM{3BF:pG n:^)le+5: ۙJ (i~(^jk TgTa_{}wȦ#.P {!l(5۷XdFQqLgY2w(ӈnF*ǎ^i6Бk-/zBT(u\#[4*}dz= {h7ZglVM￿QY=']L•^eIW k#`NǔFQN*rzmprL u:ᚯFRgTz^ ?Ii." T?1WkFB>Qb?n^-8TWj!uw\鈙R+n!k!ȃgtIu ;wFeF Cv֭bs664gokД5K2QJ'ps wvJ_DvVsNTϿ5nt-pa$S+ ;| ] C ѣ"`7nnȾ qYL KD5~a`R *70ɏZM`^@-Ө6uX~^GUǻNڭŊ)=`GJ}ؿRHw].Ҭ,(k5hk\ ;dȐv>}|Y U܎ocv7w]Æ&Q( o{Aq_z_[s&"IwF$* uk`wiC9X\2cۏ*!GUX.Pj BJ. & w tO0{F{Z"y `7ؙ] sv;RћQ J$p29:4e@) 1Ml%HPP@\(:j")G-T,p 9>uׯ!!L4:<z\6lHw?!Qv,Ew~[loI=hw%w|%@a|쳙tuqZt(h1= RZVw{!Ls`xf-mX _gH) hU%KV;(1bʆ /YFezT?wzFNgue48P==)l] Rq@PS#^:_M?Sj98~}y p#jow۴is9!,h4 }IW*pvMS4Z#W2ZT=|fΜY}s*j^F,~wkbΫ[:"Co4hj sݰQ|3ݻ'j7P$h W>_+fj*r %oM}p FpQ_7$rN߾}& Z%Ǽ6'srM5V ;NrN d1j>!f-!@z1q"n8p5_ n^`خ*z 7kWo{\[f Ma:@yO#o5F*rto'xGO۶m Gm@Mn(P\u'[L5*tyS\r խ˱A{S@* kJvء<qR# @jX״y@zyfv_D\W]Ck`;a}ĉqv_ߧgG/PQ;vp΀yZk[֙I+Ojۯ/]Nݗx-\/ bhN.{z\3l:m*K&]>8@KBi,^8 @2W{WW/oSNjrEm/v ŦrwlL% P4ZR @¡ʉ0 Zڷڹ{)$i":S?_w_73 < :~d0*uZV{l!EVfv#k$܏BI_kEYh_T/ @Zt`7w8QЖ&c?c?cuO_eGl_ClnQ=L*f1-4~~Ee Gv_1bVrU` 4-0BOQ ?L}ٌlQ!a &(Qc?c$y?5Ѝh'f+ BzFw+z_4vS) 5Սom  _,/G*QMbjyz_^4cvǝW)h`g}`Թsg~؏؏_w4t6W7>eŀF{5trmA{} /wBϮ?蠆o6v_2Q3D'ZH;/0FA؏_󒿓3sosBoHkkfտ]T*ͫڂ@. qСC7Wb@~!Ksw&SrnտoC/9DEܭ=Ð DBrsf߃wټBN'o7=ZW(?-00TtȨB؏؏_[Nӂ?'M5VGm_iel K\)Wa ?~~/uZD^_ V B+eZ h1+ EA~؏؏Щ~g]oI m_QT;ghAp~$`?c?'N"?~霿*FyO7_@wV]&8@PК3BI,B*E~T?7b?~џ]_7 ~;G+>+gD|/,rҥK&|6J(.BŒY8m_:~hz_[@YQE񇘩7hD `?c?c/$?㜿E{1ۼU8?CLJ`3[^?~~/DGKOe!>v[zwg[`!33@`+TDYݻw@b?7UV?#fZ?ې ЏqqE~:"ET1,[Mfsn݂R*H؏؟L/?O_ >gf5k>7m7cBtS7(cB˩sOl?_p JHZqRҭ PHe\8brH]wu`*6n l27b'@؏؟,?_04aBmUUb뷅~G~P/?c}Byh$-7C'%G [( 3,/| \cM^g٦sw!tЁB_y8}/wOp6?=;߆* p GZPk wwfؔOh[0) ̰ ް9f:֙~o6SLi]ѣG*4y E[B cOV[sߍ_Qv{#2';`lVwZϹr5 v`8Ц C)6pѰV_@0W@bӐ\U`}hMCdŘ1cT !Pe~Xe_~ES ~gϞ*} _ GQ:+w}y5A]1Н!+lZ8>,ְQfIoY~ șac9;9΍y!J>ѻw:#}oPmE[W?׺7%` hᴊ°KN͘Dtsz0pϒA,P;p !/?}]g4ߗ~~4`#;S 7%G8i).h 4mT`s< X@b ,X@pu*B:N:Ŏ_99ӟЏPou*Q;CD.)h@Ҧ\[#0&i] L <3ÆpBP0/l0'_BLQ:}VO'a\/89~;w FulJ.@`07)Za0`# L”- !1hN"*[?ճ:ia~\ӷ4y `g Q ndZ 6 %;~i_Be۾:ywan-sG黣}[ s82@\T\@z8Q Fž( Wr-('g:0T_ Ή 0=( z:B2rw.q#~{wkF |  vA!Tv}N᮳sH_# ~t ,p`!J]B\q}pgwro9}Q~O3jbIENDB`pappl-1.0.3/pappl/icon-md.png000066400000000000000000000142241403603036100157760ustar00rootroot00000000000000PNG  IHDR>aiCCPkCGColorSpaceGenericRGB8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*ikF&MeiTXtXML:com.adobe.xmp Michael Sweet Copyright 2020 Michael Sweet New Image New Image IDATx]{lU^@Xa}֔" `E@PQGy SU?0D]MԬMQ-9p{3QIܙ;sΜ|~\^x^^d.27-V+ի+++?:YSSs҈#=E=?$[WVVVExdJB/>.Çv :$"CHˣQߨw YYY3XGb_VRRrO> `OrG~Љ|> DOu y_2ɽ...>x(#6իW}ח,SΈZ>`R`&WӢ;U9bߵĒR"Ѳq!b(޾&WJoZ$]{"beذaIJ0%7Иj'$GpNyԽsmϡﱿ> XL%\ |DMdCTZn$0O 7Y@O p>Ξ,Ouō`zV@ p;`WK3UJHƛCH%!;>2u5}A+"kڨlqDC)|璍SU]KcsNI7񫯾˗/l ӟ ;v/֌HDs8y/QSg L"A3f[JtA20t=&LyI~xDdO |^ݹǏ='D @G ߸|ԏ[xy<R0 `0L&oHn61nܸ!~&@&%K֭[=]͜9S\:ӦMc̟?_8{rJ<3R `н+K˖-۶mO=,+&NHH.\(>9Y ܠoٲEqrȓO>)|A1k,I7|SNx믿 6o&Krnmm " xb z~i&-ݻW z衇d.[naV@~=c\ u& `X= .X0@@BPwI.eb%@HEhѢ7`1!H'RH$! , Tvw/v0u\QsHF|A9D#-C<6cęBZ*D6a z7d?w\h@ Ĭ(FW?g?pQ_sB0"dL8`Æ b͚5RCKD.)=_MٳgK pKXD"hSN  Oq>nL `AȽ[kZ ~{Xy` xx 4پ}hnn"<'2 PBx^Jp @yYb! Гf8X" AˑaP".΁&@=gE!DQA8sYAp< }Y"%P?RG(ih [mo??G^wH㐒!C `ᚡ >=gc?DuYAPM:4ȀL A9 -#Rnq֝d(G9ypO$f;|1aQǀb4r߾}xt|H ZL@WDK |dFFF'7Uv;]_C9$'Hgiq]ڍ>YAh@ Joig by`KEJJ~0A.0;;;_Ǧub5? !+)JddE.|1dd^'kjlOOv}Æ x 7"璍#+ HwYYY`iSDd!5$'LH,'ӧOaxEA :1WORddyHjtb⒒3N(_J"\ |,*l"Bx;LKZVVV6'#ZjE K Gr,dd>m&''8x7R< HۋȦU Pe&/YNEE @'>tL *Q8;7ؑ>;+ܨ\ ڞrUw;Yoի9V1mb\5ڴvkV=S£BᓫJWE90LS$G JVV Pe,7]UQx Pd)\  |S@z5^ssg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|U\ 8eXIfMM*i00N6eiTXtXML:com.adobe.xmp Michael Sweet Copyright 2020 Michael Sweet New Image New Image PLTE000&&&:::333+++######999888;;;:::999777;;;:::999>>>888======111;;;>>>???=== #111:::;<???@??@@@AAAB??BBBDDDEEEFGGGHHHIIIJ<D2it@JN9.x14VuT !y p-WZ`9=0uH@\`ޱЃ@Ѓ&sW"Zmz@!>>;h+^pղ)|^TdHT q.[`YhkPYf(+ v瑔Ų'i]?hE&l(6t(ZOkm;J 'Y@$kzfN-@iVoZDl@IENDB`pappl-1.0.3/pappl/job-accessors.c000066400000000000000000000264371403603036100166540ustar00rootroot00000000000000// // Job accessor functions for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local globals... // static const char * const pappl_jreasons[] = { "aborted-by-system", "compression-error", "document-format-error", "document-password-error", "document-permission-error", "document-unprintable-error", "errors-detected", "job-canceled-at-device", "job-canceled-by-user", "job-completed-successfully", "job-completed-with-errors", "job-completed-with-warnings", "job-data-insufficient", "job-incoming", "job-printing", "job-queued", "job-spooling", "printer-stopped", "printer-stopped-partly", "processing-to-stop-point", "queued-in-device", "warnings-detected" }; // // 'papplJobGetAttribute()' - Get an attribute from a job. // // This function gets the named IPP attribute from a job. The returned // attribute can be examined using the `ippGetXxx` functions. // ipp_attribute_t * // O - Attribute or `NULL` if not found papplJobGetAttribute(pappl_job_t *job, // I - Job const char *name) // I - Attribute name { ipp_attribute_t *attr = NULL; // Attribute if (job) { pthread_rwlock_rdlock(&job->rwlock); attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO); pthread_rwlock_unlock(&job->rwlock); } return (attr); } // // 'papplJobGetData()' - Get per-job driver data. // // This function returns the driver data associated with the job. It is // normally only called from drivers to maintain state for the processing of // the job, for example to store bitmap compression information. // void * // O - Per-job driver data or `NULL` if none papplJobGetData(pappl_job_t *job) // I - Job { return (job ? job->data : NULL); } // // 'papplJobGetJobFilename()' - Get the job's filename. // // This function returns the filename for the job's document data. // const char * // O - Filename or `NULL` if none papplJobGetFilename(pappl_job_t *job) // I - Job { return (job ? job->filename : NULL); } // // 'papplJobGetFormat()' - Get the MIME media type for the job's file. // // This function returns the MIME media type for the job's document data. // const char * // O - MIME media type or `NULL` for none papplJobGetFormat(pappl_job_t *job) // I - Job { return (job ? job->format : NULL); } // // 'papplJobGetId()' - Get the job ID value. // // This function returns the job's unique integer identifier. // int // O - Job ID or `0` for none papplJobGetID(pappl_job_t *job) // I - Job { return (job ? job->job_id : 0); } // // 'papplJobGetImpressions()' - Get the number of impressions (sides) in the job. // // This function returns the number of impressions in the job's document data. // An impression is one side of an output page. // int // O - Number of impressions in job papplJobGetImpressions(pappl_job_t *job)// I - Job { return (job ? job->impressions : 0); } // // 'papplJobGetImpressionsCompleted()' - Get the number of completed impressions // (sides) in the job. // // This function returns the number of impressions that have been printed. An // impression is one side of an output page. // int // O - Number of completed impressions in job papplJobGetImpressionsCompleted( pappl_job_t *job) // I - Job { return (job ? job->impcompleted : 0); } // // 'papplJobGetMessage()' - Get the current job message string, if any. // // This function returns the current job message string, if any. // const char * // O - Current "job-state-message" value or `NULL` for none papplJobGetMessage(pappl_job_t *job) // I - Job { return (job ? job->message : NULL); } // // 'papplJobGetName()' - Get the job name/title. // // This function returns the name or title of the job. // const char * // O - Job name/title or `NULL` for none papplJobGetName(pappl_job_t *job) // I - Job { return (job ? job->name : NULL); } // // 'papplJobGetPrinter()' - Get the printer for the job. // // This function returns the printer containing the job. // pappl_printer_t * // O - Printer papplJobGetPrinter(pappl_job_t *job) // I - Job { return (job ? job->printer : NULL); } // // 'papplJobGetReasons()' - Get the current job state reasons. // // This function returns the current job state reasons bitfield. // pappl_jreason_t // O - IPP "job-state-reasons" bits papplJobGetReasons(pappl_job_t *job) // I - Job { return (job ? job->state_reasons : PAPPL_JREASON_NONE); } // // 'papplJobGetState()' - Get the current job state. // // This function returns the current job processing state, which is represented // as an enumeration: // // - `IPP_JSTATE_ABORTED`: Job has been aborted by the system due to an error. // - `IPP_JSTATE_CANCELED`: Job has been canceled by a user. // - `IPP_JSTATE_COMPLETED`: Job has finished printing. // - `IPP_JSTATE_HELD`: Job is being held for some reason, typically because // the document data is being received. // - `IPP_JSTATE_PENDING`: Job is queued and waiting to be printed. // - `IPP_JSTATE_PROCESSING`: Job is being printed. // - `IPP_JSTATE_STOPPED`: Job is paused, typically when the printer is not // ready. // ipp_jstate_t // O - IPP "job-state" value papplJobGetState(pappl_job_t *job) // I - Job { return (job ? job->state : IPP_JSTATE_ABORTED); } // // 'papplJobGetTimeCompleted()' - Get the job completion time, if any. // // This function returns the date and time when the job reached the completed, // canceled, or aborted states. `0` is returned if the job is not yet in one of // those states. // time_t // O - Date/time when the job completed or `0` if not completed papplJobGetTimeCompleted( pappl_job_t *job) // I - Job { return (job ? job->completed : 0); } // // 'papplJobGetTimeCreated()' - Get the job creation time. // // This function returns the date and time when the job was created. // time_t // O - Date/time when the job was created papplJobGetTimeCreated(pappl_job_t *job)// I - Job { return (job ? job->created : 0); } // // 'papplJobGetTimeProcessed()' - Get the job processing time. // // This function returns the date and time when the job started processing // (printing). // time_t // O - Date/time when the job started processing (printing) or `0` if not yet processed papplJobGetTimeProcessed( pappl_job_t *job) // I - Job { return (job ? job->processing : 0); } // // 'papplJobGetUsername()' - Get the name of the user that submitted the job. // // This function returns the name of the user that submitted the job. // const char * // O - Username or `NULL` for unknown papplJobGetUsername(pappl_job_t *job) // I - Job { return (job ? job->username : NULL); } // // 'papplJobIsCanceled()' - Return whether the job is canceled. // // This function returns `true` if the job has been canceled or aborted. // bool // O - `true` if the job is canceled or aborted, `false` otherwise papplJobIsCanceled(pappl_job_t *job) // I - Job { if (job) return (job->is_canceled || job->state == IPP_JSTATE_CANCELED || job->state == IPP_JSTATE_ABORTED); else return (false); } // // '_papplJobReasonString()' - Return the keyword value associated with the IPP "job-state-reasons" bit value. // const char * // O - IPP "job-state-reasons" keyword value _papplJobReasonString( pappl_jreason_t reason) // I - IPP "job-state-reasons" bit value { if (reason == PAPPL_JREASON_NONE) return ("none"); else return (_PAPPL_LOOKUP_STRING(reason, pappl_jreasons)); } // // 'papplJobSetData()' - Set the per-job driver data pointer. // // This function sets the driver data for the specified job. It is // normally only called from drivers to maintain state for the processing of // the job, for example to store bitmap compression information. void papplJobSetData(pappl_job_t *job, // I - Job void *data) // I - Data pointer { if (job) job->data = data; } // // 'papplJobSetImpressions()' - Set the number of impressions (sides) in a job. // // This function sets the number of impressions in a job. An impression is one // side of an output page. // void papplJobSetImpressions( pappl_job_t *job, // I - Job int impressions) // I - Number of impressions/sides { if (job) job->impressions = impressions; } // // 'papplJobSetImpressionsCompleted()' - Add completed impressions (sides) to // the job. // // This function updates the number of completed impressions in a job. An // impression is one side of an output page. // void papplJobSetImpressionsCompleted( pappl_job_t *job, // I - Job int add) // I - Number of impressions/sides to add { if (job) { pthread_rwlock_wrlock(&job->rwlock); job->impcompleted += add; pthread_rwlock_unlock(&job->rwlock); } } // // 'papplJobSetMessage()' - Set the job message string. // // This function sets the job message string using a `printf`-style format // string. // // > Note: The maximum length of the job message string is 1023 bytes. // void papplJobSetMessage(pappl_job_t *job, // I - Job const char *message, // I - Printf-style message string ...) // I - Additional arguments as needed { if (job) { char buffer[1024]; // Message buffer va_list ap; // Pointer to arguments va_start(ap, message); vsnprintf(buffer, sizeof(buffer), message, ap); va_end(ap); pthread_rwlock_wrlock(&job->rwlock); free(job->message); job->message = strdup(buffer); pthread_rwlock_unlock(&job->rwlock); } } // // 'papplJobSetReasons()' - Set the job state reasons bit values. // // This function updates the job state reasons bitfield. The "remove" bits // are cleared first, then the "add" bits are set. // void papplJobSetReasons( pappl_job_t *job, // I - Job pappl_jreason_t add, // I - IPP "job-state-reasons" bit value(s) to add pappl_jreason_t remove) // I - IPP "job-state-reasons" bit value(s) to remove { if (job) { pthread_rwlock_wrlock(&job->rwlock); job->state_reasons &= ~remove; job->state_reasons |= add; pthread_rwlock_unlock(&job->rwlock); } } // // '_papplJobSetState()' - Set the IPP "job-state" value. // void _papplJobSetState(pappl_job_t *job, // I - Job ipp_jstate_t state) // I - New IPP "job-state" value { if (job && job->state != state) { pthread_rwlock_wrlock(&job->rwlock); job->state = state; if (state == IPP_JSTATE_PROCESSING) { job->processing = time(NULL); job->state_reasons |= PAPPL_JREASON_JOB_PRINTING; } else if (state >= IPP_JSTATE_CANCELED) { job->completed = time(NULL); job->state_reasons &= (unsigned)~PAPPL_JREASON_JOB_PRINTING; if (state == IPP_JSTATE_ABORTED) job->state_reasons |= PAPPL_JREASON_ABORTED_BY_SYSTEM; else if (state == IPP_JSTATE_CANCELED) job->state_reasons |= PAPPL_JREASON_JOB_CANCELED_BY_USER; if (job->state_reasons & PAPPL_JREASON_ERRORS_DETECTED) job->state_reasons |= PAPPL_JREASON_JOB_COMPLETED_WITH_ERRORS; if (job->state_reasons & PAPPL_JREASON_WARNINGS_DETECTED) job->state_reasons |= PAPPL_JREASON_JOB_COMPLETED_WITH_WARNINGS; } pthread_rwlock_unlock(&job->rwlock); } } pappl-1.0.3/pappl/job-filter.c000066400000000000000000000507501403603036100161470ustar00rootroot00000000000000// // Job MIME filter functions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl.h" #include "job-private.h" #ifdef HAVE_LIBJPEG # include # include #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG # include #endif // HAVE_LIBPNG // // Local types... // #ifdef HAVE_LIBJPEG typedef struct _pappl_jpeg_err_s // JPEG error manager extension { struct jpeg_error_mgr jerr; // JPEG error manager information jmp_buf retbuf; // setjmp() return buffer char message[JMSG_LENGTH_MAX]; // Last error message } _pappl_jpeg_err_t; #endif // HAVE_LIBJPEG // // Local functions... // #ifdef HAVE_LIBJPEG static void jpeg_error_handler(j_common_ptr p) _PAPPL_NORETURN; #endif // HAVE_LIBJPEG // // 'papplJobFilterImage()' - Filter an image in memory. // // This function will print a grayscale or sRGB image using the printer's raster // driver interface, scaling and positioning the image as necessary based on // the job options, and printing as many copies as requested. // // The image data is an array of grayscale ("depth" = `1`) or sRGB // ("depth" = `3`) pixels starting at the top-left corner of the image. // // The image resolution ("ppi") is expressed in pixels per inch and is used for // some "print-scaling" modes. Pass `0` if the image has no explicit resolution // information. // bool // O - `true` on success, `false` otherwise papplJobFilterImage( pappl_job_t *job, // I - Job pappl_device_t *device, // I - Device pappl_pr_options_t *options, // I - Print options const unsigned char *pixels, // I - Pointer to the top-left corner of the image data int width, // I - Width in columns int height, // I - Height in lines int depth, // I - Bytes per pixel (`1` for grayscale or `3` for sRGB) int ppi, // I - Pixels per inch (`0` for unknown) bool smoothing) // I - `true` to smooth/interpolate the image, `false` for nearest-neighbor sampling { bool started = false;// Have we started the job? int i; // Looping var pappl_pr_driver_data_t driver_data; // Printer driver data const unsigned char *dither; // Dither line int ileft, // Imageable left margin itop, // Imageable top margin iwidth, // Imageable width iheight; // Imageable length/height unsigned char white, // White color *line = NULL, // Output line *lineptr, // Pointer in line byte, // Byte in line bit; // Current bit const unsigned char *pixbase, // Pointer to first pixel *pixptr; // Pointer into image int img_width, // Rotated image width img_height, // Rotated image height x, // X position xsize, // Scaled width xstart, // X start position xend, // X end position y, // Y position ysize, // Scaled height ystart, // Y start position yend; // Y end position int xdir, // X direction xerr, // X error accumulator xmod, // X modulus xstep, // X step ydir; // Y direction // TODO: Implement interpolation (Issue #64) (void)smoothing; // Images contain a single page/impression... papplJobSetImpressions(job, 1); if (options->print_scaling == PAPPL_SCALING_FILL) { // Scale to fill the entire media area... ileft = 0; itop = 0; iwidth = (int)options->header.cupsWidth; iheight = (int)options->header.cupsHeight; } else { // Scale/center within the margins... ileft = options->media.left_margin * options->printer_resolution[0] / 2540; itop = options->media.top_margin * options->printer_resolution[1] / 2540; iwidth = (int)options->header.cupsWidth - (options->media.left_margin + options->media.right_margin) * options->printer_resolution[0] / 2540; iheight = (int)options->header.cupsHeight - (options->media.bottom_margin + options->media.top_margin) * options->printer_resolution[1] / 2540; } papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "ileft=%d, itop=%d, iwidth=%d, iheight=%d", ileft, itop, iwidth, iheight); if (iwidth <= 0 || iheight <= 0) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Invalid media size"); return (false); } // Figure out the scaling and rotation of the image... if (options->orientation_requested == IPP_ORIENT_NONE) { if (width > height && options->header.cupsWidth < options->header.cupsHeight) { options->orientation_requested = IPP_ORIENT_LANDSCAPE; papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Auto-orientation: landscape"); } else { options->orientation_requested = IPP_ORIENT_PORTRAIT; papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Auto-orientation: portrait"); } } if (options->print_scaling == PAPPL_SCALING_AUTO || options->print_scaling == PAPPL_SCALING_AUTO_FIT) { if (ppi <= 0) { // No resolution information, so just force scaling the image to fit/fill xsize = iwidth + 1; ysize = iheight + 1; } else if (options->orientation_requested == IPP_ORIENT_PORTRAIT || options->orientation_requested == IPP_ORIENT_REVERSE_PORTRAIT) { xsize = width * options->printer_resolution[0] / ppi; ysize = height * options->printer_resolution[1] / ppi; } else { xsize = height * options->printer_resolution[0] / ppi; ysize = width * options->printer_resolution[1] / ppi; } if (xsize > iwidth || ysize > iheight) { // Scale to fit/fill based on "print-scaling" and margins... if (options->print_scaling == PAPPL_SCALING_AUTO && options->media.bottom_margin == 0 && options->media.left_margin == 0 && options->media.right_margin == 0 && options->media.top_margin == 0) options->print_scaling = PAPPL_SCALING_FILL; else options->print_scaling = PAPPL_SCALING_FIT; } else { // Do no scaling... options->print_scaling = PAPPL_SCALING_NONE; } } else if (options->print_scaling == PAPPL_SCALING_NONE && ppi <= 0) { // Force a default PPI value of 200, which fits a typical 1080p sized // screenshot on a standard letter/A4 page. ppi = 200; } switch (options->orientation_requested) { default : case IPP_ORIENT_PORTRAIT : pixbase = pixels; img_width = width; img_height = height; xdir = (int)depth; ydir = (int)depth * (int)width; if (options->print_scaling == PAPPL_SCALING_NONE) { // No scaling xsize = img_width * options->printer_resolution[0] / ppi; ysize = img_height * options->printer_resolution[1] / ppi; } else { // Fit/fill xsize = iwidth; ysize = xsize * height / width; if ((ysize > iheight && options->print_scaling == PAPPL_SCALING_FIT) || (ysize < iheight && options->print_scaling == PAPPL_SCALING_FILL)) { ysize = iheight; xsize = ysize * width / height; } } break; case IPP_ORIENT_REVERSE_PORTRAIT : pixbase = pixels + depth * width * height - depth; img_width = width; img_height = height; xdir = -(int)depth; ydir = -(int)depth * (int)width; if (options->print_scaling == PAPPL_SCALING_NONE) { // No scaling xsize = img_width * options->printer_resolution[0] / ppi; ysize = img_height * options->printer_resolution[1] / ppi; } else { // Fit/fill xsize = iwidth; ysize = xsize * height / width; if ((ysize > iheight && options->print_scaling == PAPPL_SCALING_FIT) || (ysize < iheight && options->print_scaling == PAPPL_SCALING_FILL)) { ysize = iheight; xsize = ysize * width / height; } } break; case IPP_ORIENT_LANDSCAPE : // 90 counter-clockwise pixbase = pixels + depth * width - depth; img_width = height; img_height = width; xdir = (int)depth * (int)width; ydir = -(int)depth; if (options->print_scaling == PAPPL_SCALING_NONE) { // No scaling xsize = img_width * options->printer_resolution[0] / ppi; ysize = img_height * options->printer_resolution[1] / ppi; } else { // Fit/fill xsize = iwidth; ysize = xsize * width / height; if ((ysize > iheight && options->print_scaling == PAPPL_SCALING_FIT) || (ysize < iheight && options->print_scaling == PAPPL_SCALING_FILL)) { ysize = iheight; xsize = ysize * height / width; } } break; case IPP_ORIENT_REVERSE_LANDSCAPE : // 90 clockwise pixbase = pixels + depth * (height - 1) * width; img_width = height; img_height = width; xdir = -(int)depth * (int)width; ydir = (int)depth; if (options->print_scaling == PAPPL_SCALING_NONE) { // No scaling xsize = img_width * options->printer_resolution[0] / ppi; ysize = img_height * options->printer_resolution[1] / ppi; } else { // Fit/fill xsize = iwidth; ysize = xsize * width / height; if ((ysize > iheight && options->print_scaling == PAPPL_SCALING_FIT) || (ysize < iheight && options->print_scaling == PAPPL_SCALING_FILL)) { ysize = iheight; xsize = ysize * height / width; } } break; } // Don't rotate in the driver... options->orientation_requested = IPP_ORIENT_PORTRAIT; xstart = ileft + (iwidth - xsize) / 2; xend = xstart + xsize; ystart = itop + (iheight - ysize) / 2; yend = ystart + ysize; xmod = (int)(img_width % xsize); xstep = (int)(img_width / xsize) * xdir; if (xend > (int)options->header.cupsWidth) xend = (int)options->header.cupsWidth; if (yend > (int)options->header.cupsHeight) yend = (int)options->header.cupsHeight; papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "xsize=%d, xstart=%d, xend=%d, xdir=%d, xmod=%d, xstep=%d", xsize, xstart, xend, xdir, xmod, xstep); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "ysize=%d, ystart=%d, yend=%d, ydir=%d", ysize, ystart, yend, ydir); papplPrinterGetDriverData(papplJobGetPrinter(job), &driver_data); if ((line = malloc(options->header.cupsBytesPerLine)) == NULL) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for raster line."); goto abort_job; } // Start the job... if (!(driver_data.rstartjob_cb)(job, options, device)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to start raster job."); goto abort_job; } started = true; if (options->header.cupsColorSpace == CUPS_CSPACE_K || options->header.cupsColorSpace == CUPS_CSPACE_CMYK) white = 0x00; else white = 0xff; // Print every copy... for (i = 0; i < options->copies; i ++) { if (!(driver_data.rstartpage_cb)(job, options, device, 1)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to start raster page."); goto abort_job; } // Leading blank space... memset(line, white, options->header.cupsBytesPerLine); for (y = 0; y < ystart; y ++) { if (!(driver_data.rwriteline_cb)(job, options, device, (unsigned)y, line)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to write raster line %u.", y); goto abort_job; } } // Now RIP the image... for (; y < yend && !job->is_canceled; y ++) { pixptr = pixbase + ydir * (int)((y - ystart) * (img_height - 1) / (ysize - 1)); if (xstart < 0) { pixptr -= (xstart * xmod / xsize) * xdir; x = 0; xerr = -xmod / 2 - (xstart * xmod) % xsize; } else { x = xstart; xerr = -xmod / 2; } if (options->header.cupsBitsPerPixel == 1) { // Need to dither the image to 1-bit black... dither = options->dither[y & 15]; for (lineptr = line + x / 8, bit = 128 >> (x & 7), byte = 0; x < xend; x ++) { // Dither the current pixel... if (*pixptr <= dither[x & 15]) byte |= bit; // Advance to the next pixel... pixptr += xstep; xerr += xmod; if (xerr >= (int)xsize) { // Accumulated error has overflowed, advance another pixel... xerr -= xsize; pixptr += xdir; } // and the next bit if (bit == 1) { // Current byte is "full", save it... *lineptr++ = byte; byte = 0; bit = 128; } else bit /= 2; } if (bit < 128) *lineptr = byte; } else if (options->header.cupsColorSpace == CUPS_CSPACE_K) { // Need to invert the image... for (lineptr = line + x; x < xend; x ++) { // Copy an inverted grayscale pixel... *lineptr++ = ~*pixptr; // Advance to the next pixel... pixptr += xstep; xerr += xmod; if (xerr >= (int)xsize) { // Accumulated error has overflowed, advance another pixel... xerr -= xsize; pixptr += xdir; } } } else { // Need to copy the image... int bpp = (int)options->header.cupsBitsPerPixel / 8; for (lineptr = line + x * bpp; x < xend; x ++) { // Copy a grayscale or RGB pixel... memcpy(lineptr, pixptr, (unsigned)bpp); lineptr += bpp; // Advance to the next pixel... pixptr += xstep; xerr += xmod; if (xerr >= (int)xsize) { // Accumulated error has overflowed, advance another pixel... xerr -= xsize; pixptr += xdir; } } } if (!(driver_data.rwriteline_cb)(job, options, device, (unsigned)y, line)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to write raster line %u.", y); goto abort_job; } } // Trailing blank space... memset(line, white, options->header.cupsBytesPerLine); for (; y < (int)options->header.cupsHeight; y ++) { if (!(driver_data.rwriteline_cb)(job, options, device, (unsigned)y, line)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to write raster line %u.", y); goto abort_job; } } // End the page... if (!(driver_data.rendpage_cb)(job, options, device, 1)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to end raster page."); goto abort_job; } papplJobSetImpressionsCompleted(job, 1); } // End the job... if (!(driver_data.rendjob_cb)(job, options, device)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to end raster job."); goto abort_job; } // Free memory and return... free(line); return (true); // Abort the job... abort_job: if (started) (driver_data.rendjob_cb)(job, options, device); free(line); return (false); } // // '_papplJobFilterJPEG()' - Filter a JPEG image file. // #ifdef HAVE_LIBJPEG bool _papplJobFilterJPEG( pappl_job_t *job, // I - Job pappl_device_t *device, // I - Device void *data) // I - Filter data (unused) { const char *filename; // JPEG filename FILE *fp; // JPEG file pappl_pr_options_t *options = NULL;// Job options struct jpeg_decompress_struct dinfo; // Decompressor info int ppi; // Pixels per inch _pappl_jpeg_err_t jerr; // Error handler info unsigned char *pixels = NULL; // Image pixels JSAMPROW row; // Sample row pointer bool ret = false; // Return value (void)data; // Open the JPEG file... filename = papplJobGetFilename(job); if ((fp = fopen(filename, "rb")) == NULL) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to open JPEG file '%s': %s", filename, strerror(errno)); return (false); } // Read the image header... jpeg_std_error(&jerr.jerr); jerr.jerr.error_exit = jpeg_error_handler; if (setjmp(jerr.retbuf)) { // JPEG library errors are directed to this point... papplJobSetReasons(job, PAPPL_JREASON_DOCUMENT_FORMAT_ERROR, PAPPL_JREASON_NONE); papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to open JPEG file '%s': %s", filename, jerr.message); ret = false; goto finish_jpeg; } dinfo.err = (struct jpeg_error_mgr *)&jerr; jpeg_create_decompress(&dinfo); jpeg_stdio_src(&dinfo, fp); jpeg_read_header(&dinfo, TRUE); // Get job options and request the image data in the format we need... options = papplJobCreatePrintOptions(job, 1, dinfo.num_components > 1); dinfo.quantize_colors = FALSE; if (options->header.cupsNumColors == 1) { dinfo.out_color_space = JCS_GRAYSCALE; dinfo.out_color_components = 1; dinfo.output_components = 1; } else { dinfo.out_color_space = JCS_RGB; dinfo.out_color_components = 3; dinfo.output_components = 3; } jpeg_calc_output_dimensions(&dinfo); papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Loading %dx%dx%d JPEG image.", dinfo.output_width, dinfo.output_height, dinfo.output_components); if ((pixels = (unsigned char *)malloc((size_t)(dinfo.output_width * dinfo.output_height * (unsigned)dinfo.output_components))) == NULL) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for %dx%dx%d JPEG image.", dinfo.output_width, dinfo.output_height, dinfo.output_components); papplJobSetReasons(job, PAPPL_JREASON_ERRORS_DETECTED, PAPPL_JREASON_NONE); goto finish_jpeg; } jpeg_start_decompress(&dinfo); while (dinfo.output_scanline < dinfo.output_height) { row = (JSAMPROW)(pixels + (size_t)dinfo.output_scanline * (size_t)dinfo.output_width * (size_t)dinfo.output_components); jpeg_read_scanlines(&dinfo, &row, 1); } if (dinfo.X_density != dinfo.Y_density) { papplLogJob(job, PAPPL_LOGLEVEL_WARN, "Unsupported non-square JPEG resolution %ux%u%s, using default.", dinfo.X_density, dinfo.Y_density, dinfo.density_unit == 1 ? "dpi" : dinfo.density_unit == 2 ? "dpcm" : "???"); ppi = 0; } else { switch (dinfo.density_unit) { default : case 0 : // Unknown units ppi = 0; break; case 1 : // Dots-per-inch ppi = dinfo.X_density; break; case 2 : // Dots-per-centimeter ppi = dinfo.X_density * 254 / 100; break; } } ret = papplJobFilterImage(job, device, options, pixels, (int)dinfo.output_width, (int)dinfo.output_height, dinfo.output_components, ppi, true); finish_jpeg: papplJobDeletePrintOptions(options); free(pixels); jpeg_finish_decompress(&dinfo); jpeg_destroy_decompress(&dinfo); fclose(fp); return (ret); } #endif // HAVE_LIBJPEG // // 'process_png()' - Process a PNG image file. // #ifdef HAVE_LIBPNG bool // O - `true` on success and `false` otherwise _papplJobFilterPNG( pappl_job_t *job, // I - Job pappl_device_t *device, // I - Device void *data) // I - Filter data (unused) { pappl_pr_options_t *options = NULL;// Job options png_image png; // PNG image data png_color bg; // Background color int png_bpp; // Bytes per pixel unsigned char *pixels = NULL; // Image pixels bool ret = false; // Return value // Load the PNG... (void)data; memset(&png, 0, sizeof(png)); png.version = PNG_IMAGE_VERSION; bg.red = bg.green = bg.blue = 255; png_image_begin_read_from_file(&png, job->filename); if (png.warning_or_error & PNG_IMAGE_ERROR) { papplJobSetReasons(job, PAPPL_JREASON_DOCUMENT_FORMAT_ERROR, PAPPL_JREASON_NONE); papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to open PNG file '%s': %s", job->filename, png.message); goto finish_job; } papplLogJob(job, PAPPL_LOGLEVEL_INFO, "PNG image is %ux%u", png.width, png.height); // Prepare options... options = papplJobCreatePrintOptions(job, 1, (png.format & PNG_FORMAT_FLAG_COLOR) != 0); if (options->header.cupsNumColors > 1) { png.format = PNG_FORMAT_RGB; png_bpp = 3; } else { png.format = PNG_FORMAT_GRAY; png_bpp = 1; } pixels = malloc(PNG_IMAGE_SIZE(png)); png_image_finish_read(&png, &bg, pixels, 0, NULL); if (png.warning_or_error & PNG_IMAGE_ERROR) { papplJobSetReasons(job, PAPPL_JREASON_DOCUMENT_FORMAT_ERROR, PAPPL_JREASON_NONE); papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to open PNG file '%s': %s", job->filename, png.message); goto finish_job; } // TODO: Get PNG image resolution information (Issue #65) // Print the image... ret = papplJobFilterImage(job, device, options, pixels, (int)png.width, (int)png.height, png_bpp, 0, false); finish_job: papplJobDeletePrintOptions(options); // Free the image data when we're done... png_image_free(&png); free(pixels); return (ret); } #endif // HAVE_LIBPNG #ifdef HAVE_LIBJPEG // // 'jpeg_error_handler()' - Handle JPEG errors by not exiting. // static void jpeg_error_handler(j_common_ptr p) // I - JPEG data { _pappl_jpeg_err_t *jerr = (_pappl_jpeg_err_t *)p->err; // JPEG error handler // Save the error message in the string buffer... (jerr->jerr.format_message)(p, jerr->message); // Return to the point we called setjmp()... longjmp(jerr->retbuf, 1); } #endif // HAVE_LIBJPEG pappl-1.0.3/pappl/job-ipp.c000066400000000000000000000535071403603036100154550ustar00rootroot00000000000000// // Job IPP processing for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local type... // typedef struct _pappl_attr_s // Input attribute structure { const char *name; // Attribute name ipp_tag_t value_tag; // Value tag int max_count; // Max number of values } _pappl_attr_t; // // Local functions... // static void ipp_cancel_job(pappl_client_t *client); static void ipp_close_job(pappl_client_t *client); static void ipp_get_job_attributes(pappl_client_t *client); static void ipp_send_document(pappl_client_t *client); // // '_papplJobCopyAttributes()' - Copy job attributes to the response. // void _papplJobCopyAttributes( pappl_client_t *client, // I - Client pappl_job_t *job, // I - Job cups_array_t *ra) // I - requested-attributes { _papplCopyAttributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0); if (!ra || cupsArrayFind(ra, "date-time-at-creation")) ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(job->created)); if (!ra || cupsArrayFind(ra, "date-time-at-completed")) { if (job->completed) ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed)); else ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed"); } if (!ra || cupsArrayFind(ra, "date-time-at-processing")) { if (job->processing) ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing)); else ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing"); } if (!ra || cupsArrayFind(ra, "job-impressions")) ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions); if (!ra || cupsArrayFind(ra, "job-impressions-completed")) ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted); if (!ra || cupsArrayFind(ra, "job-printer-up-time")) ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time)); if (!ra || cupsArrayFind(ra, "job-state")) ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state); if (!ra || cupsArrayFind(ra, "job-state-message")) { if (job->message) { ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, job->message); } else { switch (job->state) { case IPP_JSTATE_PENDING : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending."); break; case IPP_JSTATE_HELD : if (job->fd >= 0) ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming."); else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO)) ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held."); else ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created."); break; case IPP_JSTATE_PROCESSING : if (job->is_canceled) ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling."); else ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing."); break; case IPP_JSTATE_STOPPED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped."); break; case IPP_JSTATE_CANCELED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled."); break; case IPP_JSTATE_ABORTED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted."); break; case IPP_JSTATE_COMPLETED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed."); break; } } } if (!ra || cupsArrayFind(ra, "job-state-reasons")) { if (job->state_reasons) { int num_values = 0; // Number of string values const char *svalues[32]; // String values pappl_jreason_t bit; // Current reason bit for (bit = PAPPL_JREASON_ABORTED_BY_SYSTEM; bit <= PAPPL_JREASON_WARNINGS_DETECTED; bit *= 2) { if (bit & job->state_reasons) svalues[num_values ++] = _papplJobReasonString(bit); } ippAddStrings(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", num_values, NULL, svalues); } else { switch (job->state) { case IPP_JSTATE_PENDING : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "none"); break; case IPP_JSTATE_HELD : if (job->fd >= 0) ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "job-incoming"); else ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "job-data-insufficient"); break; case IPP_JSTATE_PROCESSING : if (job->is_canceled) ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "processing-to-stop-point"); else ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "job-printing"); break; case IPP_JSTATE_STOPPED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "job-stopped"); break; case IPP_JSTATE_CANCELED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "job-canceled-by-user"); break; case IPP_JSTATE_ABORTED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "aborted-by-system"); break; case IPP_JSTATE_COMPLETED : ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "job-completed-successfully"); break; } } } if (!ra || cupsArrayFind(ra, "time-at-creation")) ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time)); if (!ra || cupsArrayFind(ra, "time-at-completed")) ippAddInteger(client->response, IPP_TAG_JOB, job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE, "time-at-completed", (int)(job->completed - client->printer->start_time)); if (!ra || cupsArrayFind(ra, "time-at-processing")) ippAddInteger(client->response, IPP_TAG_JOB, job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE, "time-at-processing", (int)(job->processing - client->printer->start_time)); } // // '_papplJobCopyDocumentData()' - Finish receiving a document file in an IPP // request and start processing. // void _papplJobCopyDocumentData( pappl_client_t *client, // I - Client pappl_job_t *job) // I - Job { char filename[1024], // Filename buffer buffer[4096]; // Copy buffer ssize_t bytes; // Bytes read cups_array_t *ra; // Attributes to send in response // If we have a PWG or Apple raster file, process it directly or return // server-error-busy... if (!strcmp(job->format, "image/pwg-raster") || !strcmp(job->format, "image/urf")) { if (job->printer->processing_job) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job."); _papplClientFlushDocumentData(client); return; } job->state = IPP_JSTATE_PENDING; _papplJobProcessRaster(job, client); goto complete_job; } // Create a file for the request data... if ((job->fd = papplJobOpenFile(job, filename, sizeof(filename), client->system->directory, NULL, "w")) < 0) { papplClientRespondIPP(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno)); goto abort_job; } papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Created job file \"%s\", format \"%s\".", filename, job->format); while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0) { if (write(job->fd, buffer, (size_t)bytes) < bytes) { int error = errno; // Write error close(job->fd); job->fd = -1; unlink(filename); papplClientRespondIPP(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error)); goto abort_job; } } if (bytes < 0) { // Got an error while reading the print data, so abort this job. close(job->fd); job->fd = -1; unlink(filename); papplClientRespondIPP(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file."); goto abort_job; } if (close(job->fd)) { int error = errno; // Write error job->fd = -1; unlink(filename); papplClientRespondIPP(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error)); goto abort_job; } job->fd = -1; // Submit the job for processing... _papplJobSubmitFile(job, filename); complete_job: // Return the job info... papplClientRespondIPP(client, IPP_STATUS_OK, NULL); ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); cupsArrayAdd(ra, "job-id"); cupsArrayAdd(ra, "job-state"); cupsArrayAdd(ra, "job-state-message"); cupsArrayAdd(ra, "job-state-reasons"); cupsArrayAdd(ra, "job-uri"); _papplJobCopyAttributes(client, job, ra); cupsArrayDelete(ra); return; // If we get here we had to abort the job... abort_job: _papplClientFlushDocumentData(client); job->state = IPP_JSTATE_ABORTED; job->completed = time(NULL); pthread_rwlock_wrlock(&client->printer->rwlock); cupsArrayRemove(client->printer->active_jobs, job); cupsArrayAdd(client->printer->completed_jobs, job); if (!client->system->clean_time) client->system->clean_time = time(NULL) + 60; pthread_rwlock_unlock(&client->printer->rwlock); ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); cupsArrayAdd(ra, "job-id"); cupsArrayAdd(ra, "job-state"); cupsArrayAdd(ra, "job-state-reasons"); cupsArrayAdd(ra, "job-uri"); _papplJobCopyAttributes(client, job, ra); cupsArrayDelete(ra); } // // '_papplJobProcessIPP()' - Process an IPP Job request. // void _papplJobProcessIPP( pappl_client_t *client) // I - Client { switch (ippGetOperation(client->request)) { case IPP_OP_SEND_DOCUMENT : ipp_send_document(client); break; case IPP_OP_CANCEL_JOB : ipp_cancel_job(client); break; case IPP_OP_GET_JOB_ATTRIBUTES : ipp_get_job_attributes(client); break; case IPP_OP_CLOSE_JOB : ipp_close_job(client); break; default : if (client->system->op_cb && (client->system->op_cb)(client, client->system->op_cbdata)) break; papplClientRespondIPP(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, "Operation not supported."); break; } } // // '_papplJobValidateDocumentAttributes()' - Determine whether the document // attributes are valid. // // When one or more document attributes are invalid, this function adds a // suitable response and attributes to the unsupported group. // bool // O - `true` if valid, `false` if not _papplJobValidateDocumentAttributes( pappl_client_t *client) // I - Client { bool valid = true; // Valid attributes? ipp_op_t op = ippGetOperation(client->request); // IPP operation const char *op_name = ippOpString(op); // IPP operation name ipp_attribute_t *attr, // Current attribute *supported; // xxx-supported attribute const char *compression = NULL, // compression value *format = NULL; // document-format value // Check operation attributes... if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL) { // If compression is specified, only accept a supported value in a Print-Job // or Send-Document request... compression = ippGetString(attr, 0, NULL); supported = ippFindAttribute(client->printer->attrs, "compression-supported", IPP_TAG_KEYWORD); if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || ippGetGroupTag(attr) != IPP_TAG_OPERATION || (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT && op != IPP_OP_VALIDATE_JOB) || !ippContainsString(supported, compression)) { papplClientRespondIPPUnsupported(client, attr); valid = false; } else { papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s \"compression\"='%s'", op_name, compression); ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression); if (strcmp(compression, "none")) { papplLogClient(client, PAPPL_LOGLEVEL_INFO, "Receiving job file with '%s' compression.", compression); httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression); } } } // Is it a format we support? if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE || ippGetGroupTag(attr) != IPP_TAG_OPERATION) { papplClientRespondIPPUnsupported(client, attr); valid = false; } else { format = ippGetString(attr, 0, NULL); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s \"document-format\"='%s'", op_name, format); ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format); } } else { format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL); if (!format) format = "application/octet-stream"; /* Should never happen */ attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format); } if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT)) { // Auto-type the file using the first N bytes of the file... unsigned char header[8192]; // First 8k bytes of file ssize_t headersize; // Number of bytes read memset(header, 0, sizeof(header)); headersize = httpPeek(client->http, (char *)header, sizeof(header)); if (!memcmp(header, "%PDF", 4)) format = "application/pdf"; else if (!memcmp(header, "%!", 2)) format = "application/postscript"; else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef) format = "image/jpeg"; else if (!memcmp(header, "\211PNG", 4)) format = "image/png"; else if (!memcmp(header, "RaS2PwgR", 8)) format = "image/pwg-raster"; else if (!memcmp(header, "UNIRAST", 8)) format = "image/urf"; else if (client->system->mime_cb) format = (client->system->mime_cb)(header, (size_t)headersize, client->system->mime_cbdata); else format = NULL; papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Auto-type header: %02X%02X%02X%02X%02X%02X%02X%02X... format: %s\n", header[0], header[1], header[2], header[3], header[4], header[5], header[6], header[7], format ? format : "unknown"); if (format) { papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s Auto-typed \"document-format\"='%s'.", op_name, format); ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format); } } pthread_rwlock_rdlock(&client->printer->rwlock); if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format)) { papplClientRespondIPPUnsupported(client, attr); valid = false; } pthread_rwlock_unlock(&client->printer->rwlock); return (valid); } // // 'ipp_cancel_job()' - Cancel a job. // static void ipp_cancel_job(pappl_client_t *client) // I - Client { pappl_job_t *job; // Job information // Get the job... if (ippGetOperation(client->request) == IPP_OP_CANCEL_CURRENT_JOB) job = client->printer->processing_job; else job = client->job; if (!job) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); return; } // See if the job is already completed, canceled, or aborted; if so, // we can't cancel... switch (job->state) { case IPP_JSTATE_CANCELED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is already canceled - can\'t cancel.", job->job_id); break; case IPP_JSTATE_ABORTED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is already aborted - can\'t cancel.", job->job_id); break; case IPP_JSTATE_COMPLETED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is already completed - can\'t cancel.", job->job_id); break; default : // Cancel the job... papplJobCancel(job); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); break; } } // // 'ipp_close_job()' - Close an open job. // static void ipp_close_job(pappl_client_t *client) // I - Client { pappl_job_t *job = client->job; // Job information // Get the job... if (!job) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); return; } // See if the job is already completed, canceled, or aborted; if so, // we can't cancel... switch (job->state) { case IPP_JSTATE_CANCELED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is canceled - can\'t close.", job->job_id); break; case IPP_JSTATE_ABORTED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is aborted - can\'t close.", job->job_id); break; case IPP_JSTATE_COMPLETED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is completed - can\'t close.", job->job_id); break; case IPP_JSTATE_PROCESSING : case IPP_JSTATE_STOPPED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is already closed.", job->job_id); break; default : papplClientRespondIPP(client, IPP_STATUS_OK, NULL); break; } } // // 'ipp_get_job_attributes()' - Get the attributes for a job object. // static void ipp_get_job_attributes( pappl_client_t *client) // I - Client { pappl_job_t *job = client->job; // Job information cups_array_t *ra; // requested-attributes if (!job) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found."); return; } papplClientRespondIPP(client, IPP_STATUS_OK, NULL); ra = ippCreateRequestedArray(client->request); _papplJobCopyAttributes(client, job, ra); cupsArrayDelete(ra); } // // 'ipp_send_document()' - Add an attached document to a job object created with // Create-Job. // static void ipp_send_document( pappl_client_t *client) // I - Client { pappl_job_t *job = client->job; // Job information ipp_attribute_t *attr; // Current attribute bool have_data; // Do we have document data? // Get the job... if (!job) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); _papplClientFlushDocumentData(client); return; } // See if we already have a document for this job or the job has already // in a non-pending state... have_data = _papplClientHaveDocumentData(client); if (have_data) { if (job->filename || job->fd >= 0 || job->streaming) { papplClientRespondIPP(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported."); _papplClientFlushDocumentData(client); return; } else if (job->state > IPP_JSTATE_HELD) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state."); _papplClientFlushDocumentData(client); return; } } // Make sure we have the "last-document" operation attribute... if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required \"last-document\" attribute."); _papplClientFlushDocumentData(client); return; } else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "The \"last-document\" attribute is not in the operation group."); _papplClientFlushDocumentData(client); return; } else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1) { papplClientRespondIPPUnsupported(client, attr); _papplClientFlushDocumentData(client); return; } // Validate document attributes... if (have_data && !_papplJobValidateDocumentAttributes(client)) { _papplClientFlushDocumentData(client); return; } if (!have_data && !job->filename) job->state = IPP_JSTATE_ABORTED; // Then finish getting the document data and process things... pthread_rwlock_wrlock(&(client->printer->rwlock)); _papplCopyAttributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0); if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL) job->format = ippGetString(attr, 0, NULL); else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL) job->format = ippGetString(attr, 0, NULL); else job->format = client->printer->driver_data.format; pthread_rwlock_unlock(&(client->printer->rwlock)); if (have_data) _papplJobCopyDocumentData(client, job); } pappl-1.0.3/pappl/job-private.h000066400000000000000000000063001403603036100163310ustar00rootroot00000000000000// // Private job header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_JOB_PRIVATE_H_ # define _PAPPL_JOB_PRIVATE_H_ // // Include necessary headers... // # include "base-private.h" # include "job.h" # include "log.h" # include extern char **environ; // // Types and structures... // struct _pappl_job_s // Job data { pthread_rwlock_t rwlock; // Reader/writer lock pappl_system_t *system; // Containing system pappl_printer_t *printer; // Containing printer int job_id; // "job-id" value const char *name, // "job-name" value *username, // "job-originating-user-name" value *format; // "document-format" value ipp_jstate_t state; // "job-state" value pappl_jreason_t state_reasons; // "job-state-reasons" values bool is_canceled; // Has this job been canceled? char *message; // "job-state-message" value pappl_loglevel_t msglevel; // "job-state-message" log level time_t created, // "[date-]time-at-creation" value processing, // "[date-]time-at-processing" value completed; // "[date-]time-at-completed" value int impressions, // "job-impressions" value impcompleted; // "job-impressions-completed" value ipp_t *attrs; // Static attributes char *filename; // Print file name int fd; // Print file descriptor bool streaming; // Streaming job? void *data; // Per-job driver data }; // // Functions... // extern int _papplJobCompareActive(pappl_job_t *a, pappl_job_t *b) _PAPPL_PRIVATE; extern int _papplJobCompareAll(pappl_job_t *a, pappl_job_t *b) _PAPPL_PRIVATE; extern int _papplJobCompareCompleted(pappl_job_t *a, pappl_job_t *b) _PAPPL_PRIVATE; extern void _papplJobCopyAttributes(pappl_client_t *client, pappl_job_t *job, cups_array_t *ra) _PAPPL_PRIVATE; extern void _papplJobCopyDocumentData(pappl_client_t *client, pappl_job_t *job) _PAPPL_PRIVATE; extern pappl_job_t *_papplJobCreate(pappl_printer_t *printer, int job_id, const char *username, const char *format, const char *job_name, ipp_t *attrs) _PAPPL_PRIVATE; extern void _papplJobDelete(pappl_job_t *job) _PAPPL_PRIVATE; # ifdef HAVE_LIBJPEG extern bool _papplJobFilterJPEG(pappl_job_t *job, pappl_device_t *device, void *data); # endif // HAVE_LIBJPEG # ifdef HAVE_LIBPNG extern bool _papplJobFilterPNG(pappl_job_t *job, pappl_device_t *device, void *data); # endif // HAVE_LIBPNG extern void *_papplJobProcess(pappl_job_t *job) _PAPPL_PRIVATE; extern void _papplJobProcessIPP(pappl_client_t *client) _PAPPL_PRIVATE; extern void _papplJobProcessRaster(pappl_job_t *job, pappl_client_t *client) _PAPPL_PRIVATE; extern const char *_papplJobReasonString(pappl_jreason_t reason) _PAPPL_PRIVATE; extern void _papplJobRemoveFile(pappl_job_t *job) _PAPPL_PRIVATE; extern void _papplJobSetState(pappl_job_t *job, ipp_jstate_t state) _PAPPL_PRIVATE; extern void _papplJobSubmitFile(pappl_job_t *job, const char *filename) _PAPPL_PRIVATE; extern bool _papplJobValidateDocumentAttributes(pappl_client_t *client) _PAPPL_PRIVATE; #endif // !_PAPPL_JOB_PRIVATE_H_ pappl-1.0.3/pappl/job-process.c000066400000000000000000000754541403603036100163500ustar00rootroot00000000000000// // Job processing (printing) functions for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local functions... // static const char *cups_cspace_string(cups_cspace_t cspace); static bool filter_raw(pappl_job_t *job, pappl_device_t *device); static void finish_job(pappl_job_t *job); static void start_job(pappl_job_t *job); // // 'papplJobCreatePrintOptions()' - Create the printer options for a job. // // This function allocates a printer options structure and computes the print // options for a job based upon the Job Template attributes submitted in the // print request and the default values set in the printer driver data. // // The "num_pages" and "color" arguments specify the number of pages and whether // the document contains non-grayscale colors - this information typically comes // from parsing the job file. // pappl_pr_options_t * // O - Job options data or `NULL` on error papplJobCreatePrintOptions( pappl_job_t *job, // I - Job unsigned num_pages, // I - Number of pages (`0` for unknown) bool color) // I - Is the document in color? { pappl_pr_options_t *options; // New options data int i, // Looping var count; // Number of values ipp_attribute_t *attr; // Attribute pappl_printer_t *printer = job->printer; // Printer const char *raster_type; // Raster type for output static const char * const sheet_back[] = { // "pwg-raster-document-sheet-back values "normal", "flipped", "rotated", "manual-tumble" }; papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Getting options for num_pages=%u, color=%s", num_pages, color ? "true" : "false"); // Clear all options... if ((options = calloc(1, sizeof(pappl_pr_options_t))) == NULL) return (NULL); options->media = printer->driver_data.media_default; pthread_rwlock_rdlock(&printer->rwlock); // copies if ((attr = ippFindAttribute(job->attrs, "copies", IPP_TAG_INTEGER)) != NULL) options->copies = ippGetInteger(attr, 0); else options->copies = 1; // finishings options->finishings = PAPPL_FINISHINGS_NONE; if ((attr = ippFindAttribute(job->attrs, "finishings", IPP_TAG_ENUM)) != NULL) { if (ippContainsInteger(attr, IPP_FINISHINGS_PUNCH)) options->finishings |= PAPPL_FINISHINGS_PUNCH; if (ippContainsInteger(attr, IPP_FINISHINGS_STAPLE)) options->finishings |= PAPPL_FINISHINGS_STAPLE; if (ippContainsInteger(attr, IPP_FINISHINGS_TRIM)) options->finishings |= PAPPL_FINISHINGS_TRIM; } else if ((attr = ippFindAttribute(job->attrs, "finishings-col", IPP_TAG_BEGIN_COLLECTION)) != NULL) { for (i = 0, count = ippGetCount(attr); i < count; i ++) { ipp_t *col = ippGetCollection(attr, i); // "finishings-col" collection value const char *template = ippGetString(ippFindAttribute(col, "finishing-template", IPP_TAG_ZERO), 0, NULL); // "finishing-template" value if (template && !strcmp(template, "punch")) options->finishings |= PAPPL_FINISHINGS_PUNCH; else if (template && !strcmp(template, "staple")) options->finishings |= PAPPL_FINISHINGS_STAPLE; else if (template && !strcmp(template, "trim")) options->finishings |= PAPPL_FINISHINGS_TRIM; } } // media-xxx options->media = printer->driver_data.media_default; if ((attr = ippFindAttribute(job->attrs, "media-col", IPP_TAG_BEGIN_COLLECTION)) != NULL) { options->media.source[0] = '\0'; _papplMediaColImport(ippGetCollection(attr, 0), &options->media); } else if ((attr = ippFindAttribute(job->attrs, "media", IPP_TAG_ZERO)) != NULL) { const char *pwg_name = ippGetString(attr, 0, NULL); pwg_media_t *pwg_media = pwgMediaForPWG(pwg_name); if (pwg_name && pwg_media) { strlcpy(options->media.size_name, pwg_name, sizeof(options->media.size_name)); options->media.size_width = pwg_media->width; options->media.size_length = pwg_media->length; options->media.source[0] = '\0'; } } if (!options->media.source[0]) { for (i = 0; i < printer->driver_data.num_source; i ++) { if (!strcmp(options->media.size_name, printer->driver_data.media_ready[i].size_name)) { strlcpy(options->media.source, printer->driver_data.source[i], sizeof(options->media.source)); break; } } if (!options->media.source[0]) strlcpy(options->media.source, printer->driver_data.media_default.source, sizeof(options->media.source)); } // orientation-requested if ((attr = ippFindAttribute(job->attrs, "orientation-requested", IPP_TAG_ENUM)) != NULL) options->orientation_requested = (ipp_orient_t)ippGetInteger(attr, 0); else if (printer->driver_data.orient_default) options->orientation_requested = printer->driver_data.orient_default; else options->orientation_requested = IPP_ORIENT_NONE; // output-bin if (printer->driver_data.num_bin > 0) { const char *value; // Attribute string value if ((value = ippGetString(ippFindAttribute(job->attrs, "output-bin", IPP_TAG_ZERO), 0, NULL)) != NULL) strlcpy(options->output_bin, value, sizeof(options->output_bin)); else strlcpy(options->output_bin, printer->driver_data.bin[printer->driver_data.bin_default], sizeof(options->output_bin)); } // page-ranges if ((attr = ippFindAttribute(job->attrs, "page-ranges", IPP_TAG_RANGE)) != NULL && ippGetCount(attr) == 1) { int last, first = ippGetRange(attr, 0, &last); // pages-ranges values if (first > (int)num_pages && num_pages != 0) { options->first_page = num_pages + 1; options->last_page = num_pages + 1; options->num_pages = 0; } else { options->first_page = (unsigned)first; if (last > (int)num_pages && num_pages != 0) options->last_page = num_pages; else options->last_page = (unsigned)last; options->num_pages = options->last_page - options->first_page + 1; } } else if (num_pages > 0) { options->first_page = 1; options->last_page = num_pages; options->num_pages = num_pages; } else { // Unknown number of pages... options->first_page = 1; options->last_page = INT_MAX; options->num_pages = 0; } // print-color-mode if ((attr = ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_KEYWORD)) != NULL) options->print_color_mode = _papplColorModeValue(ippGetString(attr, 0, NULL)); else options->print_color_mode = printer->driver_data.color_default; papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "print-color-mode=%s", _papplColorModeString(options->print_color_mode)); if (options->print_color_mode == PAPPL_COLOR_MODE_AUTO) { if (color) options->print_color_mode = PAPPL_COLOR_MODE_COLOR; else options->print_color_mode = PAPPL_COLOR_MODE_MONOCHROME; papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "new print-color-mode=%s", _papplColorModeString(options->print_color_mode)); } else if (options->print_color_mode == PAPPL_COLOR_MODE_AUTO_MONOCHROME) { options->print_color_mode = PAPPL_COLOR_MODE_MONOCHROME; papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "new print-color-mode=%s", _papplColorModeString(options->print_color_mode)); } // print-content-optimize if ((attr = ippFindAttribute(job->attrs, "print-content-optimize", IPP_TAG_KEYWORD)) != NULL) options->print_content_optimize = _papplContentValue(ippGetString(attr, 0, NULL)); else options->print_content_optimize = printer->driver_data.content_default; // print-darkness if ((attr = ippFindAttribute(job->attrs, "print-darkness", IPP_TAG_INTEGER)) != NULL) options->print_darkness = ippGetInteger(attr, 0); else options->print_darkness = printer->driver_data.darkness_default; options->darkness_configured = printer->driver_data.darkness_configured; // print-quality if ((attr = ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ENUM)) != NULL) options->print_quality = (ipp_quality_t)ippGetInteger(attr, 0); else options->print_quality = printer->driver_data.quality_default; // print-scaling if ((attr = ippFindAttribute(job->attrs, "print-scaling", IPP_TAG_KEYWORD)) != NULL) options->print_scaling = _papplScalingValue(ippGetString(attr, 0, NULL)); else options->print_scaling = printer->driver_data.scaling_default; // print-speed if ((attr = ippFindAttribute(job->attrs, "print-speed", IPP_TAG_INTEGER)) != NULL) options->print_speed = ippGetInteger(attr, 0); else options->print_speed = printer->driver_data.speed_default; // printer-resolution if ((attr = ippFindAttribute(job->attrs, "printer-resolution", IPP_TAG_RESOLUTION)) != NULL) { ipp_res_t units; // Resolution units options->printer_resolution[0] = ippGetResolution(attr, 0, options->printer_resolution + 1, &units); } else if (options->print_quality == IPP_QUALITY_DRAFT) { // print-quality=draft options->printer_resolution[0] = printer->driver_data.x_resolution[0]; options->printer_resolution[1] = printer->driver_data.y_resolution[0]; } else if (options->print_quality == IPP_QUALITY_NORMAL) { // print-quality=normal i = printer->driver_data.num_resolution / 2; options->printer_resolution[0] = printer->driver_data.x_resolution[i]; options->printer_resolution[1] = printer->driver_data.y_resolution[i]; } else { // print-quality=high i = printer->driver_data.num_resolution - 1; options->printer_resolution[0] = printer->driver_data.x_resolution[i]; options->printer_resolution[1] = printer->driver_data.y_resolution[i]; } // sides if ((attr = ippFindAttribute(job->attrs, "sides", IPP_TAG_KEYWORD)) != NULL) options->sides = _papplSidesValue(ippGetString(attr, 0, NULL)); else if (printer->driver_data.sides_default != PAPPL_SIDES_ONE_SIDED && options->num_pages != 1) options->sides = printer->driver_data.sides_default; else options->sides = PAPPL_SIDES_ONE_SIDED; // Vendor options... for (i = 0; i < printer->driver_data.num_vendor; i ++) { const char *name = printer->driver_data.vendor[i]; // Vendor attribute name if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) == NULL) { char defname[128]; // xxx-default attribute snprintf(defname, sizeof(defname), "%s-default", name); attr = ippFindAttribute(job->attrs, defname, IPP_TAG_ZERO); } if (attr) { char value[1024]; // Value of attribute ippAttributeString(attr, value, sizeof(value)); options->num_vendor = cupsAddOption(name, value, options->num_vendor, &options->vendor); } } // Figure out the PWG raster header... if (printer->driver_data.force_raster_type == PAPPL_PWG_RASTER_TYPE_BLACK_1) { // Force bitmap output... raster_type = "black_1"; if (options->print_color_mode == PAPPL_COLOR_MODE_BI_LEVEL || options->print_quality == IPP_QUALITY_DRAFT) memset(options->dither, 127, sizeof(options->dither)); else if (options->print_content_optimize == PAPPL_CONTENT_PHOTO || !strcmp(job->format, "image/jpeg") || options->print_quality == IPP_QUALITY_HIGH) memcpy(options->dither, printer->driver_data.pdither, sizeof(options->dither)); else memcpy(options->dither, printer->driver_data.gdither, sizeof(options->dither)); } else if (options->print_color_mode == PAPPL_COLOR_MODE_COLOR) { // Color output... if (printer->driver_data.raster_types & PAPPL_PWG_RASTER_TYPE_SRGB_8) raster_type = "srgb_8"; else if (printer->driver_data.raster_types & PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_8) raster_type = "adobe-rgb_8"; else raster_type = "rgb_8"; } else { // Monochrome output... if (printer->driver_data.raster_types & PAPPL_PWG_RASTER_TYPE_SGRAY_8) raster_type = "sgray_8"; else raster_type = "black_8"; } cupsRasterInitPWGHeader(&options->header, pwgMediaForPWG(options->media.size_name), raster_type, options->printer_resolution[0], options->printer_resolution[1], _papplSidesString(options->sides), sheet_back[printer->driver_data.duplex]); options->header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = (unsigned)options->copies * options->num_pages; // Log options... papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsWidth=%u", options->header.cupsWidth); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsHeight=%u", options->header.cupsHeight); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsBitsPerColor=%u", options->header.cupsBitsPerColor); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsBitsPerPixel=%u", options->header.cupsBitsPerPixel); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsBytesPerLine=%u", options->header.cupsBytesPerLine); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsColorOrder=%u", options->header.cupsColorOrder); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsColorSpace=%u (%s)", options->header.cupsColorSpace, cups_cspace_string(options->header.cupsColorSpace)); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.cupsNumColors=%u", options->header.cupsNumColors); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "header.HWResolution=[%u %u]", options->header.HWResolution[0], options->header.HWResolution[1]); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "num_pages=%u", options->num_pages); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "copies=%d", options->copies); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "finishings=0x%x", options->finishings); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.bottom-margin=%d", options->media.bottom_margin); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.left-margin=%d", options->media.left_margin); if (printer->driver_data.left_offset_supported[1]) papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.left-offset=%d", options->media.left_offset); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.right-margin=%d", options->media.right_margin); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.size=%dx%d", options->media.size_width, options->media.size_length); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.size-name='%s'", options->media.size_name); if (printer->driver_data.num_source) papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.source='%s'", options->media.source); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.top-margin=%d", options->media.top_margin); if (printer->driver_data.top_offset_supported[1]) papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.top-offset=%d", options->media.top_offset); if (printer->driver_data.tracking_supported) papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.tracking='%s'", _papplMediaTrackingString(options->media.tracking)); if (printer->driver_data.num_type) papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "media-col.type='%s'", options->media.type); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "orientation-requested=%s", ippEnumString("orientation-requested", (int)options->orientation_requested)); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "page-ranges=%u-%u", options->first_page, options->last_page); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "print-color-mode='%s'", _papplColorModeString(options->print_color_mode)); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "print-content-optimize='%s'", _papplContentString(options->print_content_optimize)); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "print-darkness=%d", options->print_darkness); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "print-quality=%s", ippEnumString("print-quality", (int)options->print_quality)); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "print-scaling='%s'", _papplScalingString(options->print_scaling)); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "print-speed=%d", options->print_speed); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "printer-resolution=%dx%ddpi", options->printer_resolution[0], options->printer_resolution[1]); for (i = 0; i < options->num_vendor; i ++) papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "%s=%s", options->vendor[i].name, options->vendor[i].value); pthread_rwlock_unlock(&printer->rwlock); return (options); } // // 'papplJobDeletePrintOptions()' - Delete a job options structure. // // This function frees the memory used for a job options structure. // void papplJobDeletePrintOptions( pappl_pr_options_t *options) // I - Options { if (options) { cupsFreeOptions(options->num_vendor, options->vendor); free(options); } } // // '_papplJobProcess()' - Process a print job. // void * // O - Thread exit status _papplJobProcess(pappl_job_t *job) // I - Job { _pappl_mime_filter_t *filter; // Filter for printing // Start processing the job... start_job(job); // Do file-specific conversions... if ((filter = _papplSystemFindMIMEFilter(job->system, job->format, job->printer->driver_data.format)) == NULL) filter =_papplSystemFindMIMEFilter(job->system, job->format, "image/pwg-raster"); if (filter) { if (!(filter->cb)(job, job->printer->device, filter->cbdata)) job->state = IPP_JSTATE_ABORTED; } else if (!strcmp(job->format, job->printer->driver_data.format)) { if (!filter_raw(job, job->printer->device)) job->state = IPP_JSTATE_ABORTED; } else { // Abort a job we can't process... papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to process job with format '%s'.", job->format); job->state = IPP_JSTATE_ABORTED; } // Move the job to a completed state... finish_job(job); return (NULL); } // // '_papplJobProcessRaster()' - Process an Apple/PWG Raster file. // void _papplJobProcessRaster( pappl_job_t *job, // I - Job pappl_client_t *client) // I - Client { pappl_printer_t *printer = job->printer; // Printer for job pappl_pr_options_t *options = NULL;// Job options cups_raster_t *ras = NULL; // Raster stream cups_page_header2_t header; // Page header unsigned header_pages; // Number of pages from page header const unsigned char *dither; // Dither line unsigned char *pixels, // Incoming pixel line *pixptr, // Pixel pointer in line *line, // Output (bitmap) line *lineptr, // Pointer in line byte, // Byte in line bit; // Current bit unsigned page = 0, // Current page x, // Current column y; // Current line // Start processing the job... job->streaming = true; start_job(job); // Open the raster stream... if ((ras = cupsRasterOpenIO((cups_raster_iocb_t)httpRead2, client->http, CUPS_RASTER_READ)) == NULL) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to open raster stream from client - %s", cupsLastErrorString()); job->state = IPP_JSTATE_ABORTED; goto complete_job; } // Prepare options... if (!cupsRasterReadHeader2(ras, &header)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to read raster stream from client - %s", cupsLastErrorString()); job->state = IPP_JSTATE_ABORTED; goto complete_job; } if ((header_pages = header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount]) > 0) papplJobSetImpressions(job, (int)header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount]); options = papplJobCreatePrintOptions(job, (unsigned)job->impressions, header.cupsBitsPerPixel > 8); if (!(printer->driver_data.rstartjob_cb)(job, options, job->printer->device)) { job->state = IPP_JSTATE_ABORTED; goto complete_job; } // Print pages... do { if (job->is_canceled) break; page ++; papplJobSetImpressionsCompleted(job, 1); papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Page %u raster data is %ux%ux%u (%s)", page, header.cupsWidth, header.cupsHeight, header.cupsBitsPerPixel, cups_cspace_string(header.cupsColorSpace)); // Set options for this page... papplJobDeletePrintOptions(options); options = papplJobCreatePrintOptions(job, (unsigned)job->impressions, header.cupsBitsPerPixel > 8); if (header.cupsWidth == 0 || header.cupsHeight == 0 || (header.cupsBitsPerColor != 1 && header.cupsBitsPerColor != 8) || header.cupsColorOrder != CUPS_ORDER_CHUNKED || (header.cupsBytesPerLine != ((header.cupsWidth * header.cupsBitsPerPixel + 7) / 8))) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Bad raster data seen."); papplJobSetReasons(job, PAPPL_JREASON_DOCUMENT_FORMAT_ERROR, PAPPL_JREASON_NONE); job->state = IPP_JSTATE_ABORTED; break; } if (header.cupsBitsPerPixel > 8 && !(printer->driver_data.color_supported & PAPPL_COLOR_MODE_COLOR)) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unsupported raster data seen."); papplJobSetReasons(job, PAPPL_JREASON_DOCUMENT_UNPRINTABLE_ERROR, PAPPL_JREASON_NONE); job->state = IPP_JSTATE_ABORTED; break; } if (options->header.cupsBitsPerPixel >= 8 && header.cupsBitsPerPixel >= 8) options->header = header; // Use page header from client if (!(printer->driver_data.rstartpage_cb)(job, options, job->printer->device, page)) { job->state = IPP_JSTATE_ABORTED; break; } if (options->header.cupsBytesPerLine > header.cupsBytesPerLine) { // Allocate enough space for the entire output line and clear to white if ((pixels = malloc(options->header.cupsBytesPerLine)) == NULL) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to allocate raster line."); job->state = IPP_JSTATE_ABORTED; break; } if (options->header.cupsColorSpace == CUPS_CSPACE_K) memset(pixels, 0, options->header.cupsBytesPerLine); else memset(pixels, 255, options->header.cupsBytesPerLine); } else { // The input raster is at least as wide as the output raster... if ((pixels = malloc(header.cupsBytesPerLine)) == NULL) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to allocate raster line."); job->state = IPP_JSTATE_ABORTED; break; } } if ((line = malloc(options->header.cupsBytesPerLine)) == NULL) { free(pixels); papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to allocate raster line."); job->state = IPP_JSTATE_ABORTED; break; } for (y = 0; !job->is_canceled && y < header.cupsHeight && y < options->header.cupsHeight; y ++) { if (cupsRasterReadPixels(ras, pixels, header.cupsBytesPerLine)) { if (header.cupsBitsPerPixel == 8 && options->header.cupsBitsPerPixel == 1) { // Dither the line... dither = options->dither[y & 15]; memset(line, 0, options->header.cupsBytesPerLine); if (header.cupsColorSpace == CUPS_CSPACE_K) { // Black... for (x = 0, lineptr = line, pixptr = pixels, bit = 128, byte = 0; x < header.cupsWidth; x ++, pixptr ++) { if (*pixptr > dither[x & 15]) byte |= bit; if (bit == 1) { *lineptr++ = byte; byte = 0; bit = 128; } else bit /= 2; } if (bit < 128) *lineptr = byte; } else { // Grayscale to black... for (x = 0, lineptr = line, pixptr = pixels, bit = 128, byte = 0; x < header.cupsWidth; x ++, pixptr ++) { if (*pixptr <= dither[x & 15]) byte |= bit; if (bit == 1) { *lineptr++ = byte; byte = 0; bit = 128; } else bit /= 2; } if (bit < 128) *lineptr = byte; } (printer->driver_data.rwriteline_cb)(job, options, job->printer->device, y, line); } else (printer->driver_data.rwriteline_cb)(job, options, job->printer->device, y, pixels); } else break; } if (!job->is_canceled && y < header.cupsHeight) { // Discard excess lines from client... while (y < header.cupsHeight) { cupsRasterReadPixels(ras, pixels, header.cupsBytesPerLine); y ++; } } else { // Pad missing lines with whitespace... if (header.cupsBitsPerPixel == 8 && options->header.cupsBitsPerPixel == 1) { memset(line, 0, options->header.cupsBytesPerLine); while (y < options->header.cupsHeight) { (printer->driver_data.rwriteline_cb)(job, options, job->printer->device, y, line); y ++; } } else { if (header.cupsColorSpace == CUPS_CSPACE_K || header.cupsColorSpace == CUPS_CSPACE_CMYK) memset(pixels, 0x00, header.cupsBytesPerLine); else memset(pixels, 0xff, header.cupsBytesPerLine); while (y < options->header.cupsHeight) { (printer->driver_data.rwriteline_cb)(job, options, job->printer->device, y, pixels); y ++; } } } free(pixels); free(line); if (!(printer->driver_data.rendpage_cb)(job, options, job->printer->device, page)) { job->state = IPP_JSTATE_ABORTED; break; } if (job->is_canceled) break; else if (y < header.cupsHeight) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to read page from raster stream from client - %s", cupsLastErrorString()); job->state = IPP_JSTATE_ABORTED; break; } } while (cupsRasterReadHeader2(ras, &header)); if (!(printer->driver_data.rendjob_cb)(job, options, job->printer->device)) job->state = IPP_JSTATE_ABORTED; else if (header_pages == 0) papplJobSetImpressions(job, (int)page); complete_job: papplJobDeletePrintOptions(options); if (httpGetState(client->http) == HTTP_STATE_POST_RECV) { // Flush excess data... char buffer[8192]; // Read buffer while (httpRead2(client->http, buffer, sizeof(buffer)) > 0) ; // Read all document data } cupsRasterClose(ras); finish_job(job); return; } // // 'cups_cspace_string()' - Get a string corresponding to a cupsColorSpace enum value. // static const char * // O - cupsColorSpace string value cups_cspace_string( cups_cspace_t value) // I - cupsColorSpace enum value { static const char * const cspace[] = // cupsColorSpace values { "Gray", "RGB", "RGBA", "Black", "CMY", "YMC", "CMYK", "YMCK", "KCMY", "KCMYcm", "GMCK", "GMCS", "White", "Gold", "Silver", "CIE-XYZ", "CIE-Lab", "RGBW", "sGray", "sRGB", "Adobe-RGB", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "ICC-1", "ICC-2", "ICC-3", "ICC-4", "ICC-5", "ICC-6", "ICC-7", "ICC-8", "ICC-9", "ICC-10", "ICC-11", "ICC-12", "ICC-13", "ICC-14", "ICC-15", "47", "Device-1", "Device-2", "Device-3", "Device-4", "Device-5", "Device-6", "Device-7", "Device-8", "Device-9", "Device-10", "Device-11", "Device-12", "Device-13", "Device-14", "Device-15" }; if (value >= CUPS_CSPACE_W && value <= CUPS_CSPACE_DEVICEF) return (cspace[value]); else return ("Unknown"); } // // 'filter_raw()' - "Filter" a raw print file. // static bool // O - `true` on success, `false` otherwise filter_raw(pappl_job_t *job, // I - Job pappl_device_t *device) // I - Device { pappl_pr_options_t *options; // Job options papplJobSetImpressions(job, 1); options = papplJobCreatePrintOptions(job, 1, false); if (!(job->printer->driver_data.printfile_cb)(job, options, device)) { papplJobDeletePrintOptions(options); return (false); } papplJobDeletePrintOptions(options); papplJobSetImpressionsCompleted(job, 1); return (true); } // // 'finish_job()' - Finish job processing... // static void finish_job(pappl_job_t *job) // I - Job { pappl_printer_t *printer = job->printer; // Printer pthread_rwlock_wrlock(&job->rwlock); pthread_rwlock_wrlock(&printer->rwlock); if (job->is_canceled) job->state = IPP_JSTATE_CANCELED; else if (job->state == IPP_JSTATE_PROCESSING) job->state = IPP_JSTATE_COMPLETED; papplLogJob(job, PAPPL_LOGLEVEL_INFO, "%s, job-impressions-completed=%d.", job->state == IPP_JSTATE_COMPLETED ? "Completed" : job->state == IPP_JSTATE_CANCELED ? "Canceled" : "Aborted", job->impcompleted); job->completed = time(NULL); printer->processing_job = NULL; _papplJobRemoveFile(job); pthread_rwlock_unlock(&job->rwlock); if (printer->is_stopped) { // New printer-state is 'stopped'... printer->state = IPP_PSTATE_STOPPED; printer->is_stopped = false; } else { // New printer-state is 'idle'... printer->state = IPP_PSTATE_IDLE; } printer->state_time = time(NULL); cupsArrayRemove(printer->active_jobs, job); cupsArrayAdd(printer->completed_jobs, job); printer->impcompleted += job->impcompleted; if (!job->system->clean_time) job->system->clean_time = time(NULL) + 60; pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); if (printer->is_deleted) { papplPrinterDelete(printer); } else if (cupsArrayCount(printer->active_jobs) > 0) { _papplPrinterCheckJobs(printer); } else { pappl_devmetrics_t metrics; // Metrics for device IO pthread_rwlock_wrlock(&printer->rwlock); papplDeviceGetMetrics(printer->device, &metrics); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Device read metrics: %lu requests, %lu bytes, %lu msecs", (unsigned long)metrics.read_requests, (unsigned long)metrics.read_bytes, (unsigned long)metrics.read_msecs); papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Device write metrics: %lu requests, %lu bytes, %lu msecs", (unsigned long)metrics.write_requests, (unsigned long)metrics.write_bytes, (unsigned long)metrics.write_msecs); papplDeviceClose(printer->device); printer->device = NULL; pthread_rwlock_unlock(&printer->rwlock); } } // // 'start_job()' - Start processing a job... // static void start_job(pappl_job_t *job) // I - Job { pappl_printer_t *printer = job->printer; // Printer bool first_open = true; // Is this the first time we try to open the device? // Move the job to the 'processing' state... pthread_rwlock_wrlock(&job->rwlock); pthread_rwlock_wrlock(&printer->rwlock); papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Starting print job."); job->state = IPP_JSTATE_PROCESSING; job->processing = time(NULL); printer->processing_job = job; pthread_rwlock_unlock(&job->rwlock); // Open the output device... while (!printer->device) { printer->device = papplDeviceOpen(printer->device_uri, job->name, papplLogDevice, job->system); if (!printer->device) { // Log that the printer is unavailable then sleep for 5 seconds to retry. if (first_open) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to open device '%s', pausing queue until printer becomes available.", printer->device_uri); first_open = false; printer->state = IPP_PSTATE_STOPPED; printer->state_time = time(NULL); } pthread_rwlock_unlock(&printer->rwlock); sleep(5); pthread_rwlock_wrlock(&printer->rwlock); } } // Move the printer to the 'processing' state... printer->state = IPP_PSTATE_PROCESSING; printer->state_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); } pappl-1.0.3/pappl/job.c000066400000000000000000000376221403603036100146670ustar00rootroot00000000000000// // Job object for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // 'papplJobCancel()' - Cancel a job. // // This function cancels the specified job. If the job is currently being // printed, it will be stopped at a convenient time (usually the end of a page) // so that the printer will be left in a known state. // void papplJobCancel(pappl_job_t *job) // I - Job { if (!job) return; pthread_rwlock_wrlock(&job->rwlock); pthread_rwlock_wrlock(&job->printer->rwlock); if (job->state == IPP_JSTATE_PROCESSING || (job->state == IPP_JSTATE_HELD && job->fd >= 0)) { job->is_canceled = true; } else { job->state = IPP_JSTATE_CANCELED; job->completed = time(NULL); _papplJobRemoveFile(job); cupsArrayRemove(job->printer->active_jobs, job); cupsArrayAdd(job->printer->completed_jobs, job); } pthread_rwlock_unlock(&job->printer->rwlock); if (!job->system->clean_time) job->system->clean_time = time(NULL) + 60; pthread_rwlock_unlock(&job->rwlock); } // // '_papplJobCreate()' - Create a new/existing job object. // pappl_job_t * // O - Job _papplJobCreate( pappl_printer_t *printer, // I - Printer int job_id, // I - Existing Job ID or `0` for new job const char *username, // I - Username const char *format, // I - Document format or `NULL` for none const char *job_name, // I - Job name ipp_t *attrs) // I - Job creation attributes or `NULL` for none { pappl_job_t *job; // Job ipp_attribute_t *attr; // Job attribute char job_printer_uri[1024], // job-printer-uri value job_uri[1024], // job-uri value job_uuid[64]; // job-uuid value pthread_rwlock_wrlock(&printer->rwlock); if (printer->max_active_jobs > 0 && cupsArrayCount(printer->active_jobs) >= printer->max_active_jobs) { pthread_rwlock_unlock(&printer->rwlock); return (NULL); } // Allocate and initialize the job object... if ((job = calloc(1, sizeof(pappl_job_t))) == NULL) { papplLog(printer->system, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for job: %s", strerror(errno)); pthread_rwlock_unlock(&printer->rwlock); return (NULL); } job->attrs = ippNew(); job->fd = -1; job->format = format; job->name = job_name; job->printer = printer; job->state = IPP_JSTATE_HELD; job->system = printer->system; job->created = time(NULL); if (attrs) { // Copy all of the job attributes... _papplCopyAttributes(job->attrs, attrs, NULL, IPP_TAG_JOB, 0); if (!format && ippGetOperation(attrs) != IPP_OP_CREATE_JOB) { if ((attr = ippFindAttribute(attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL) job->format = ippGetString(attr, 0, NULL); else if ((attr = ippFindAttribute(attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL) job->format = ippGetString(attr, 0, NULL); else job->format = "application/octet-stream"; } } else ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, job_name); if ((attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, username)) != NULL) job->username = ippGetString(attr, 0, NULL); if ((attr = ippFindAttribute(attrs, "job-impressions", IPP_TAG_INTEGER)) != NULL) job->impressions = ippGetInteger(attr, 0); // Add job description attributes and add to the jobs array... job->job_id = job_id > 0 ? job_id : printer->next_job_id ++; if ((attr = ippFindAttribute(attrs, "printer-uri", IPP_TAG_URI)) != NULL) { strlcpy(job_printer_uri, ippGetString(attr, 0, NULL), sizeof(job_printer_uri)); snprintf(job_uri, sizeof(job_uri), "%s/%d", ippGetString(attr, 0, NULL), job->job_id); } else { httpAssembleURI(HTTP_URI_CODING_ALL, job_printer_uri, sizeof(job_printer_uri), "ipps", NULL, printer->system->hostname, printer->system->port, printer->resource); httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipps", NULL, printer->system->hostname, printer->system->port, "%s/%d", printer->resource, job->job_id); } _papplSystemMakeUUID(printer->system, printer->name, job->job_id, job_uuid, sizeof(job_uuid)); ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->job_id); ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri); ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, job_uuid); ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, job_printer_uri); cupsArrayAdd(printer->all_jobs, job); if (!job_id) cupsArrayAdd(printer->active_jobs, job); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); return (job); } // // '_papplJobDelete()' - Remove a job from the system and free its memory. // void _papplJobDelete(pappl_job_t *job) // I - Job { papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Removing job from history."); ippDelete(job->attrs); free(job->message); // Only remove the job file (document) if the job is in a terminating state... if (job->state >= IPP_JSTATE_CANCELED) _papplJobRemoveFile(job); free(job); } // // 'papplJobOpenFile()' - Create or open a file for the document in a job. // // This function creates or opens a file for a job. The "fname" and "fnamesize" // arguments specify the location and size of a buffer to store the job // filename, which incorporates the "directory", printer ID, job ID, job name // (title), and "ext" values. The job name is "sanitized" to only contain // alphanumeric characters. // // The "mode" argument is "r" to read an existing job file or "w" to write a // new job file. New files are created with restricted permissions for // security purposes. // int // O - File descriptor or -1 on error papplJobOpenFile( pappl_job_t *job, // I - Job char *fname, // I - Filename buffer size_t fnamesize, // I - Size of filename buffer const char *directory, // I - Directory to store in (`NULL` for default) const char *ext, // I - Extension (`NULL` for default) const char *mode) // I - Open mode - "r" for reading or "w" for writing { char name[64], // "Safe" filename *nameptr; // Pointer into filename const char *job_name; // job-name value // Make sure the spool directory exists... if (!directory) directory = job->system->directory; if (access(directory, X_OK)) { if (errno == ENOENT) { // Spool directory does not exist, might have been deleted... if (mkdir(directory, 0777)) { papplLogJob(job, PAPPL_LOGLEVEL_FATAL, "Unable to create spool directory '%s': %s", directory, strerror(errno)); return (-1); } } else { papplLogJob(job, PAPPL_LOGLEVEL_FATAL, "Unable to access spool directory '%s': %s", directory, strerror(errno)); return (-1); } } // Make a name from the job-name attribute... if ((job_name = ippGetString(ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME), 0, NULL)) == NULL) job_name = "untitled"; if ((nameptr = strrchr(job_name, '/')) != NULL && nameptr[1]) job_name = nameptr + 1; for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++) { if (isalnum(*job_name & 255) || *job_name == '-') { *nameptr++ = (char)tolower(*job_name & 255); } else { *nameptr++ = '_'; while (job_name[1] && !isalnum(job_name[1] & 255) && job_name[1] != '-') job_name ++; } } *nameptr = '\0'; // Figure out the extension... if (!ext) { if (!strcasecmp(job->format, "image/jpeg")) ext = "jpg"; else if (!strcasecmp(job->format, "image/png")) ext = "png"; else if (!strcasecmp(job->format, "image/pwg-raster")) ext = "pwg"; else if (!strcasecmp(job->format, "image/urf")) ext = "urf"; else if (!strcasecmp(job->format, "application/pdf")) ext = "pdf"; else if (!strcasecmp(job->format, "application/postscript")) ext = "ps"; else ext = "prn"; } // Create a filename with the job-id, job-name, and document-format (extension)... snprintf(fname, fnamesize, "%s/p%05dj%09d-%s.%s", directory, job->printer->printer_id, job->job_id, name, ext); if (!strcmp(mode, "r")) return (open(fname, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); else if (!strcmp(mode, "w")) return (open(fname, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, 0600)); else if (!strcmp(mode, "x")) return (unlink(fname)); else return (-1); } // // '_papplJobRemoveFile()' - Remove a file in spool directory // void _papplJobRemoveFile(pappl_job_t *job) // I - Job { size_t dirlen = strlen(job->system->directory); // Length of spool directory // Only remove the file if it is in spool directory... if (job->filename && !strncmp(job->filename, job->system->directory, dirlen) && job->filename[dirlen] == '/') unlink(job->filename); free(job->filename); job->filename = NULL; } // // '_papplJobSubmitFile()' - Submit a file for printing. // void _papplJobSubmitFile( pappl_job_t *job, // I - Job const char *filename) // I - Filename { if (!job->format) { // Open the file unsigned char header[8192]; // First 8k bytes of file ssize_t headersize; // Number of bytes read int fd; // File descriptor if ((fd = open(filename, O_RDONLY)) >= 0) { // Auto-type the file using the first N bytes of the file... memset(header, 0, sizeof(header)); headersize = read(fd, (char *)header, sizeof(header)); close(fd); if (!memcmp(header, "%PDF", 4)) job->format = "application/pdf"; else if (!memcmp(header, "%!", 2)) job->format = "application/postscript"; else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef) job->format = "image/jpeg"; else if (!memcmp(header, "\211PNG", 4)) job->format = "image/png"; else if (!memcmp(header, "RaS2PwgR", 8)) job->format = "image/pwg-raster"; else if (!memcmp(header, "UNIRAST", 8)) job->format = "image/urf"; else if (job->system->mime_cb) job->format = (job->system->mime_cb)(header, (size_t)headersize, job->system->mime_cbdata); } } if (!job->format) { // Guess the format using the filename extension... const char *ext = strrchr(filename, '.'); // Extension on filename if (!ext) job->format = job->printer->driver_data.format; else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) job->format = "image/jpeg"; else if (!strcmp(ext, ".png")) job->format = "image/png"; else if (!strcmp(ext, ".pwg")) job->format = "image/pwg-raster"; else if (!strcmp(ext, ".urf")) job->format = "image/urf"; else if (!strcmp(ext, ".txt")) job->format = "text/plain"; else if (!strcmp(ext, ".pdf")) job->format = "application/pdf"; else if (!strcmp(ext, ".ps")) job->format = "application/postscript"; else job->format = job->printer->driver_data.format; } // Save the print file information... if ((job->filename = strdup(filename)) != NULL) { // Process the job... job->state = IPP_JSTATE_PENDING; _papplPrinterCheckJobs(job->printer); } else { // Abort the job... job->state = IPP_JSTATE_ABORTED; job->completed = time(NULL); papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to allocate filename."); unlink(filename); pthread_rwlock_wrlock(&job->printer->rwlock); cupsArrayRemove(job->printer->active_jobs, job); cupsArrayAdd(job->printer->completed_jobs, job); pthread_rwlock_unlock(&job->printer->rwlock); if (!job->system->clean_time) job->system->clean_time = time(NULL) + 60; } } // // '_papplPrinterCheckJobs()' - Check for new jobs to process. // void _papplPrinterCheckJobs( pappl_printer_t *printer) // I - Printer { pappl_job_t *job; // Current job papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Checking for new jobs to process."); if (printer->processing_job) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Printer is already processing job %d.", printer->processing_job->job_id); return; } else if (printer->is_deleted) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Printer is being deleted."); return; } else if (printer->state == IPP_PSTATE_STOPPED || printer->is_stopped) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Printer is stopped."); return; } pthread_rwlock_wrlock(&printer->rwlock); // Enumerate the jobs. Since we have a writer (exclusive) lock, we are the // only thread enumerating and can use cupsArrayFirst/Last... for (job = (pappl_job_t *)cupsArrayFirst(printer->active_jobs); job; job = (pappl_job_t *)cupsArrayNext(printer->active_jobs)) { if (job->state == IPP_JSTATE_PENDING) { pthread_t t; // Thread papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Starting job %d.", job->job_id); if (pthread_create(&t, NULL, (void *(*)(void *))_papplJobProcess, job)) { job->state = IPP_JSTATE_ABORTED; job->completed = time(NULL); cupsArrayRemove(printer->active_jobs, job); cupsArrayAdd(printer->completed_jobs, job); if (!printer->system->clean_time) printer->system->clean_time = time(NULL) + 60; } else pthread_detach(t); break; } } if (!job) papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "No jobs to process at this time."); pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterFindJob()' - Find a job. // // This function finds a job submitted to a printer using its integer ID value. // pappl_job_t * // O - Job or `NULL` if not found papplPrinterFindJob( pappl_printer_t *printer, // I - Printer int job_id) // I - Job ID { pappl_job_t key, // Job search key *job; // Matching job, if any key.job_id = job_id; pthread_rwlock_rdlock(&(printer->rwlock)); job = (pappl_job_t *)cupsArrayFind(printer->all_jobs, &key); pthread_rwlock_unlock(&(printer->rwlock)); return (job); } // // 'papplSystemCleanJobs()' - Clean out old (completed) jobs. // // This function deletes all old (completed) jobs above the limit set by the // @link papplPrinterSetMaxCompletedJobs@ function. The level may temporarily // exceed this limit if the jobs were completed within the last 60 seconds. // // > Note: This function is normally called automatically from the // > @link papplSystemRun@ function. // void papplSystemCleanJobs( pappl_system_t *system) // I - System { int i, // Looping var count; // Number of printers pappl_printer_t *printer; // Current printer pappl_job_t *job; // Current job time_t cleantime; // Clean time cleantime = time(NULL) - 60; pthread_rwlock_rdlock(&system->rwlock); // Loop through the printers. // // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the printers array. for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); if (cupsArrayCount(printer->completed_jobs) == 0 || printer->max_completed_jobs <= 0) continue; pthread_rwlock_wrlock(&printer->rwlock); // Enumerate the jobs. Since we have a writer (exclusive) lock, we are the // only thread enumerating and can use cupsArrayFirst/Last... for (job = (pappl_job_t *)cupsArrayFirst(printer->completed_jobs); job; job = (pappl_job_t *)cupsArrayNext(printer->completed_jobs)) { if (job->completed && job->completed < cleantime && cupsArrayCount(printer->completed_jobs) > printer->max_completed_jobs) { cupsArrayRemove(printer->completed_jobs, job); cupsArrayRemove(printer->all_jobs, job); } else break; } pthread_rwlock_unlock(&printer->rwlock); } pthread_rwlock_unlock(&system->rwlock); } pappl-1.0.3/pappl/job.h000066400000000000000000000111561403603036100146660ustar00rootroot00000000000000// // Public job header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_JOB_H_ # define _PAPPL_JOB_H_ // // Include necessary headers... // # include "base.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Constants... // enum pappl_jreason_e // IPP "job-state-reasons" bit values { PAPPL_JREASON_NONE = 0x00000000, // 'none' PAPPL_JREASON_ABORTED_BY_SYSTEM = 0x00000001, // 'aborted-by-system' PAPPL_JREASON_COMPRESSION_ERROR = 0x00000002, // 'compression-error' PAPPL_JREASON_DOCUMENT_FORMAT_ERROR = 0x00000004, // 'document-format-error' PAPPL_JREASON_DOCUMENT_PASSWORD_ERROR = 0x00000008, // 'document-password-error' PAPPL_JREASON_DOCUMENT_PERMISSION_ERROR = 0x00000010, // 'document-permission-error' PAPPL_JREASON_DOCUMENT_UNPRINTABLE_ERROR = 0x00000020,// 'document-unprintable-error' PAPPL_JREASON_ERRORS_DETECTED = 0x00000040, // 'errors-detected' PAPPL_JREASON_JOB_CANCELED_AT_DEVICE = 0x00000080, // 'job-canceled-at-device' PAPPL_JREASON_JOB_CANCELED_BY_USER = 0x00000100, // 'job-canceled-by-user' PAPPL_JREASON_JOB_COMPLETED_SUCCESSFULLY = 0x00000200,// 'job-completed-successfully' PAPPL_JREASON_JOB_COMPLETED_WITH_ERRORS = 0x00000400, // 'job-completed-with-errors' PAPPL_JREASON_JOB_COMPLETED_WITH_WARNINGS = 0x00000800,// 'job-completed-with-warnings' PAPPL_JREASON_JOB_DATA_INSUFFICIENT = 0x00001000, // 'job-data-insufficient' PAPPL_JREASON_JOB_INCOMING = 0x000002000, // 'job-incoming' PAPPL_JREASON_JOB_PRINTING = 0x00004000, // 'job-printing' PAPPL_JREASON_JOB_QUEUED = 0x00008000, // 'job-queued' PAPPL_JREASON_JOB_SPOOLING = 0x00010000, // 'job-spooling' PAPPL_JREASON_PRINTER_STOPPED = 0x00020000, // 'printer-stopped' PAPPL_JREASON_PRINTER_STOPPED_PARTLY = 0x00040000, // 'printer-stopped-partly' PAPPL_JREASON_PROCESSING_TO_STOP_POINT = 0x00080000, // 'processing-to-stop-point' PAPPL_JREASON_QUEUED_IN_DEVICE = 0x00100000, // 'queued-in-device' PAPPL_JREASON_WARNINGS_DETECTED = 0x00200000 // 'warnings-detected' }; typedef unsigned int pappl_jreason_t; // Bitfield for IPP "job-state-reasons" values // // Functions... // extern void papplJobCancel(pappl_job_t *job) _PAPPL_PUBLIC; extern pappl_pr_options_t *papplJobCreatePrintOptions(pappl_job_t *job, unsigned num_pages, bool color) _PAPPL_PUBLIC; extern void papplJobDeletePrintOptions(pappl_pr_options_t *options); extern bool papplJobFilterImage(pappl_job_t *job, pappl_device_t *device, pappl_pr_options_t *options, const unsigned char *pixels, int width, int height, int depth, int ppi, bool smoothing) _PAPPL_PUBLIC; extern ipp_attribute_t *papplJobGetAttribute(pappl_job_t *job, const char *name) _PAPPL_PUBLIC; extern void *papplJobGetData(pappl_job_t *job) _PAPPL_PUBLIC; extern const char *papplJobGetFilename(pappl_job_t *job) _PAPPL_PUBLIC; extern const char *papplJobGetFormat(pappl_job_t *job) _PAPPL_PUBLIC; extern int papplJobGetID(pappl_job_t *job) _PAPPL_PUBLIC; extern int papplJobGetImpressions(pappl_job_t *job) _PAPPL_PUBLIC; extern int papplJobGetImpressionsCompleted(pappl_job_t *job) _PAPPL_PUBLIC; extern const char *papplJobGetMessage(pappl_job_t *job) _PAPPL_PUBLIC; extern const char *papplJobGetName(pappl_job_t *job) _PAPPL_PUBLIC; extern pappl_printer_t *papplJobGetPrinter(pappl_job_t *job) _PAPPL_PUBLIC; extern pappl_jreason_t papplJobGetReasons(pappl_job_t *job) _PAPPL_PUBLIC; extern ipp_jstate_t papplJobGetState(pappl_job_t *job) _PAPPL_PUBLIC; extern time_t papplJobGetTimeCompleted(pappl_job_t *job) _PAPPL_PUBLIC; extern time_t papplJobGetTimeCreated(pappl_job_t *job) _PAPPL_PUBLIC; extern time_t papplJobGetTimeProcessed(pappl_job_t *job) _PAPPL_PUBLIC; extern const char *papplJobGetUsername(pappl_job_t *job) _PAPPL_PUBLIC; extern bool papplJobIsCanceled(pappl_job_t *job) _PAPPL_PUBLIC; extern int papplJobOpenFile(pappl_job_t *job, char *fname, size_t fnamesize, const char *directory, const char *ext, const char *mode) _PAPPL_PUBLIC; extern void papplJobSetData(pappl_job_t *job, void *data) _PAPPL_PUBLIC; extern void papplJobSetImpressions(pappl_job_t *job, int impressions) _PAPPL_PUBLIC; extern void papplJobSetImpressionsCompleted(pappl_job_t *job, int add) _PAPPL_PUBLIC; extern void papplJobSetMessage(pappl_job_t *job, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(2,3); extern void papplJobSetReasons(pappl_job_t *job, pappl_jreason_t add, pappl_jreason_t remove) _PAPPL_PUBLIC; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_JOB_H_ pappl-1.0.3/pappl/link.c000066400000000000000000000111551403603036100150430ustar00rootroot00000000000000// // Link functions for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local functions... // static int compare_links(_pappl_link_t *a, _pappl_link_t *b); static _pappl_link_t *copy_link(_pappl_link_t *l); static void free_link(_pappl_link_t *l); // // 'papplPrinterAddLink()' - Add a printer link to the navigation header. // // This function adds a navigation link for a printer. The "path_or_url" // argument specifies a absolute path such as "/ipp/print/example/page" or an // absolute URL such as "https://www.example.com/". The "options" argument // specifies where the link is shown and whether the link should redirect an // absolute path to the secure ("https://.../path") web interface. // void papplPrinterAddLink( pappl_printer_t *printer, // I - Printer const char *label, // I - Label string const char *path_or_url, // I - Path or URL pappl_loptions_t options) // I - Link options { _pappl_link_t l; // Link if (!printer || !label || !path_or_url) return; pthread_rwlock_wrlock(&printer->rwlock); if (!printer->links) printer->links = cupsArrayNew3((cups_array_func_t)compare_links, NULL, NULL, 0, (cups_acopy_func_t)copy_link, (cups_afree_func_t)free_link); l.label = (char *)label; l.path_or_url = (char *)path_or_url; l.options = options; if (!cupsArrayFind(printer->links, &l)) cupsArrayAdd(printer->links, &l); pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterRemoveLink()' - Remove a printer link from the navigation header. // // This function removes the named link for the printer. // void papplPrinterRemoveLink( pappl_printer_t *printer, // I - Printer const char *label) // I - Label string { _pappl_link_t l; // Link if (!printer || !label) return; pthread_rwlock_wrlock(&printer->rwlock); l.label = (char *)label; cupsArrayRemove(printer->links, &l); pthread_rwlock_unlock(&printer->rwlock); } // // 'papplSystemAddLink()' - Add a link to the navigation header. // // This function adds a navigation link for the system. The "path_or_url" // argument specifies a absolute path such as "/page" or an absolute URL such // as "https://www.example.com/". The "options" argument specifies where the // link is shown and whether the link should redirect an absolute path to the // secure ("https://.../path") web interface. // void papplSystemAddLink( pappl_system_t *system, // I - System const char *label, // I - Label string const char *path_or_url, // I - Path or URL pappl_loptions_t options) // I - Link options { _pappl_link_t l; // Link if (!system || !label || !path_or_url) return; pthread_rwlock_wrlock(&system->rwlock); if (!system->links) system->links = cupsArrayNew3((cups_array_func_t)compare_links, NULL, NULL, 0, (cups_acopy_func_t)copy_link, (cups_afree_func_t)free_link); l.label = (char *)label; l.path_or_url = (char *)path_or_url; l.options = options; if (!cupsArrayFind(system->links, &l)) cupsArrayAdd(system->links, &l); pthread_rwlock_unlock(&system->rwlock); } // // 'papplSystemRemoveLink()' - Remove a link from the navigation header. // // This function removes the named link for the system. // void papplSystemRemoveLink( pappl_system_t *system, // I - System const char *label) // I - Label string { _pappl_link_t l; // Link if (!system || !label) return; pthread_rwlock_wrlock(&system->rwlock); l.label = (char *)label; cupsArrayRemove(system->links, &l); pthread_rwlock_unlock(&system->rwlock); } // // 'compare_links()' - Compare two links. // static int // O - Result of comparison compare_links(_pappl_link_t *a, // I - First link _pappl_link_t *b) // I - Second link { return (strcmp(a->label, b->label)); } // // 'copy_link()' - Copy a link. // static _pappl_link_t * // O - New link copy_link(_pappl_link_t *l) // I - Current link { _pappl_link_t *newl = calloc(1, sizeof(_pappl_link_t)); // New link if (newl) { newl->label = strdup(l->label); newl->path_or_url = strdup(l->path_or_url); newl->options = l->options; if (!newl->label || !newl->path_or_url) { free_link(newl); return (NULL); } } return (newl); } // // 'free_link()' - Free the memory used by a link. // static void free_link(_pappl_link_t *l) // I - Link { free(l->label); free(l->path_or_url); free(l); } pappl-1.0.3/pappl/log-private.h000066400000000000000000000011311403603036100163350ustar00rootroot00000000000000// // Private log header file for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_LOG_PRIVATE_H_ # define _PAPPL_LOG_PRIVATE_H_ // // inlcude necessary headers // # include "base-private.h" # include "log.h" // // Functions... // extern void _papplLogAttributes(pappl_client_t *client, const char *title, ipp_t *ipp, bool is_response) _PAPPL_PRIVATE; extern void _papplLogOpen(pappl_system_t *system) _PAPPL_PRIVATE; #endif // !_PAPPL_LOG_PRIVATE_H_ pappl-1.0.3/pappl/log.c000066400000000000000000000414471403603036100146760ustar00rootroot00000000000000// // Logging functions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "client-private.h" #include "job-private.h" #include "log-private.h" #include "printer-private.h" #include "system-private.h" #include #include // // Local functions... // static void rotate_log(pappl_system_t *system); static void write_log(pappl_system_t *system, pappl_loglevel_t level, const char *message, va_list ap); // // Local globals... // static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; // Log rotation mutex static const int syslevels[] = // Mapping of log levels to syslog { LOG_DEBUG | LOG_PID | LOG_LPR, LOG_INFO | LOG_PID | LOG_LPR, LOG_WARNING | LOG_PID | LOG_LPR, LOG_ERR | LOG_PID | LOG_LPR, LOG_CRIT | LOG_PID | LOG_LPR }; // // 'papplLog()' - Log a message for the system. // // This function sends a message to the system's log file. The "level" argument // specifies the urgency of the message: // // - `PAPPL_LOGLEVEL_DEBUG`: A debugging message. // - `PAPPL_LOGLEVEL_ERROR`: An error message. // - `PAPPL_LOGLEVEL_FATAL`: A fatal error message. // - `PAPPL_LOGLEVEL_INFO`: An informational message. // - `PAPPL_LOGLEVEL_WARN`: A warning message. // // The "message" argument specifies a `printf`-style format string. Values // logged using the "%c" and "%s" format specifiers are sanitized to not // contain control characters. // void papplLog(pappl_system_t *system, // I - System pappl_loglevel_t level, // I - Log level const char *message, // I - Printf-style message string ...) // I - Additional arguments as needed { va_list ap; // Pointer to arguments if (!message) return; if (!system) { if (level >= PAPPL_LOGLEVEL_WARN) { va_start(ap, message); vfprintf(stderr, message, ap); putc('\n', stderr); va_end(ap); } return; } if (level < system->loglevel) return; va_start(ap, message); if (system->logfd >= 0) write_log(system, level, message, ap); else vsyslog(syslevels[level], message, ap); va_end(ap); } // // '_papplLogAttributes()' - Log IPP attributes for a client connection. // // This function logs the IPP attributes sent or recieved on a client // connection at the `PAPPL_LOGLEVEL_DEBUG` (debug) log level. // void _papplLogAttributes( pappl_client_t *client, // I - Client const char *title, // I - Title for attributes ipp_t *ipp, // I - IPP message bool is_response) // I - `true` if a response, `false` if a request { int major, // Major version number minor; // Minor version number ipp_attribute_t *attr; // Current attribute ipp_tag_t group = IPP_TAG_ZERO; // Current group const char *name; // Name char value[1024]; // Value if (!client || !title || !ipp) return; if (client->system->loglevel > PAPPL_LOGLEVEL_DEBUG) return; major = ippGetVersion(ipp, &minor); if (is_response) papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s response: IPP/%d.%d request-id=%d, status-code=%s", title, major, minor, ippGetRequestId(ipp), ippErrorString(ippGetStatusCode(ipp))); else papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s request: IPP/%d.%d request-id=%d", title, major, minor, ippGetRequestId(ipp)); for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp)) { if ((name = ippGetName(attr)) == NULL) { group = IPP_TAG_ZERO; continue; } if (ippGetGroupTag(attr) != group) { group = ippGetGroupTag(attr); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s %s: %s", title, is_response ? "response" : "request", ippTagString(group)); } ippAttributeString(attr, value, sizeof(value)); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s %s: %s %s%s %s", title, is_response ? "response" : "request", name, ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), value); } } // // 'papplLogClient()' - Log a message for a client. // // This function sends a client message to the system's log file. The "level" // argument specifies the urgency of the message: // // - `PAPPL_LOGLEVEL_DEBUG`: A debugging message. // - `PAPPL_LOGLEVEL_ERROR`: An error message. // - `PAPPL_LOGLEVEL_FATAL`: A fatal error message. // - `PAPPL_LOGLEVEL_INFO`: An informational message. // - `PAPPL_LOGLEVEL_WARN`: A warning message. // // The "message" argument specifies a `printf`-style format string. Values // logged using the "%c" and "%s" format specifiers are sanitized to not // contain control characters. // void papplLogClient( pappl_client_t *client, // I - Client pappl_loglevel_t level, // I - Log level const char *message, // I - Printf-style message string ...) // I - Additional arguments as needed { char cmessage[1024]; // Message with client prefix va_list ap; // Pointer to arguments if (!client || !message) return; if (level < client->system->loglevel) return; snprintf(cmessage, sizeof(cmessage), "[Client %d] %s", client->number, message); va_start(ap, message); if (client->system->logfd >= 0) write_log(client->system, level, cmessage, ap); else vsyslog(syslevels[level], cmessage, ap); va_end(ap); } // // 'papplLogDevice()' - Log a device error for the system... // // This function sends a device error message to the system's log file. // void papplLogDevice( const char *message, // I - Message void *data) // I - System { pappl_system_t *system = (pappl_system_t *)data; // System papplLog(system, PAPPL_LOGLEVEL_ERROR, "[Device] %s", message); } // // 'papplLogJob()' - Log a message for a job. // // This function sends a job message to the system's log file. The "level" // argument specifies the urgency of the message: // // - `PAPPL_LOGLEVEL_DEBUG`: A debugging message. // - `PAPPL_LOGLEVEL_ERROR`: An error message. // - `PAPPL_LOGLEVEL_FATAL`: A fatal error message. // - `PAPPL_LOGLEVEL_INFO`: An informational message. // - `PAPPL_LOGLEVEL_WARN`: A warning message. // // The "message" argument specifies a `printf`-style format string. Values // logged using the "%c" and "%s" format specifiers are sanitized to not // contain control characters. // void papplLogJob( pappl_job_t *job, // I - Job pappl_loglevel_t level, // I - Log level const char *message, // I - Printf-style message string ...) // I - Additional arguments as needed { char jmessage[1024]; // Message with job prefix va_list ap; // Pointer to arguments if (!job || !message) return; if (level < job->system->loglevel) return; snprintf(jmessage, sizeof(jmessage), "[Job %d] %s", job->job_id, message); va_start(ap, message); if (job->system->logfd >= 0) write_log(job->system, level, jmessage, ap); else vsyslog(syslevels[level], jmessage, ap); va_end(ap); } // // '_papplLogOpen()' - Open the log file // void _papplLogOpen( pappl_system_t *system) // I - System { // Open the log file... if (!strcmp(system->logfile, "syslog")) { // Log to syslog... system->logfd = -1; } else if (!strcmp(system->logfile, "-")) { // Log to stderr... system->logfd = 2; } else { int oldfd = system->logfd; // Old log file descriptor // Log to a file... if ((system->logfd = open(system->logfile, O_CREAT | O_WRONLY | O_APPEND | O_NOFOLLOW | O_CLOEXEC, 0600)) < 0) { // Fallback to logging to stderr if we can't open the log file... perror(system->logfile); system->logfd = 2; } // Close any old file... if (oldfd != -1) close(oldfd); } // Log the system status information papplLog(system, PAPPL_LOGLEVEL_INFO, "Starting log, system up %ld second(s), %d printer(s), listening for connections on '%s:%d'.", (long)(time(NULL) - system->start_time), cupsArrayCount(system->printers), system->hostname, system->port); } // // 'papplLogPrinter()' - Log a message for a printer. // // This function sends a printer message to the system's log file. The "level" // argument specifies the urgency of the message: // // - `PAPPL_LOGLEVEL_DEBUG`: A debugging message. // - `PAPPL_LOGLEVEL_ERROR`: An error message. // - `PAPPL_LOGLEVEL_FATAL`: A fatal error message. // - `PAPPL_LOGLEVEL_INFO`: An informational message. // - `PAPPL_LOGLEVEL_WARN`: A warning message. // // The "message" argument specifies a `printf`-style format string. Values // logged using the "%c" and "%s" format specifiers are sanitized to not // contain control characters. // void papplLogPrinter( pappl_printer_t *printer, // I - Printer pappl_loglevel_t level, // I - Log level const char *message, // I - Printf-style message string ...) // I - Additional arguments as needed { char pmessage[1024], // Message with printer prefix *pptr, // Pointer into prefix *nameptr; // Pointer into printer name va_list ap; // Pointer to arguments if (!printer || !message) return; if (level < printer->system->loglevel) return; // Prefix the message with "[Printer foo]", making sure to not insert any // printf format specifiers. strlcpy(pmessage, "[Printer ", sizeof(pmessage)); for (pptr = pmessage + 9, nameptr = printer->name; *nameptr && pptr < (pmessage + 200); pptr ++) { if (*nameptr == '%') *pptr++ = '%'; *pptr = *nameptr++; } *pptr++ = ']'; *pptr++ = ' '; strlcpy(pptr, message, sizeof(pmessage) - (size_t)(pptr - pmessage)); // Write the log message... va_start(ap, message); if (printer->system->logfd >= 0) write_log(printer->system, level, pmessage, ap); else vsyslog(syslevels[level], pmessage, ap); va_end(ap); } // // 'rotate_log()' - Rotate the log file... // static void rotate_log(pappl_system_t *system) // I - System { struct stat loginfo; // Lof file information // Re-check whether we need to rotate the log file... if (!fstat(system->logfd, &loginfo) && loginfo.st_size >= (off_t)system->logmaxsize) { // Rename existing log file to "xxx.O" char backname[1024]; // Backup log filename snprintf(backname, sizeof(backname), "%s.O", system->logfile); unlink(backname); rename(system->logfile, backname); _papplLogOpen(system); } } // // 'write_log()' - Write a line to the log file... // static void write_log(pappl_system_t *system, // I - System pappl_loglevel_t level, // I - Log level const char *message, // I - Printf-style message string va_list ap) // I - Pointer to additional arguments { struct stat loginfo; // Log file information char buffer[2048], // Output buffer *bufptr, // Pointer into buffer *bufend; // Pointer to end of buffer struct timeval curtime; // Current time struct tm curdate; // Current date static const char *prefix = "DIWEF"; // Message prefix const char *sval; // String value char size, // Size character (h, l, L) type; // Format type character int width, // Width of field prec; // Number of characters of precision char tformat[100], // Temporary format string for sprintf() *tptr; // Pointer into temporary format // Rotate log as needed... if (system->logmaxsize > 0 && !fstat(system->logfd, &loginfo) && loginfo.st_size >= (off_t)system->logmaxsize) { pthread_mutex_lock(&log_mutex); rotate_log(system); pthread_mutex_unlock(&log_mutex); } // Each log line starts with a standard prefix of log level and date/time... gettimeofday(&curtime, NULL); gmtime_r(&curtime.tv_sec, &curdate); snprintf(buffer, sizeof(buffer), "%c [%04d-%02d-%02dT%02d:%02d:%02d.%03dZ] ", prefix[level], curdate.tm_year + 1900, curdate.tm_mon + 1, curdate.tm_mday, curdate.tm_hour, curdate.tm_min, curdate.tm_sec, (int)(curtime.tv_usec / 1000)); bufptr = buffer + 29; // Skip level/date/time bufend = buffer + sizeof(buffer) - 1; // Leave room for newline on end // Then format the message line using printf format sequences... while (*message && bufptr < bufend) { if (*message == '%') { tptr = tformat; *tptr++ = *message++; if (*message == '%') { *bufptr++ = *message++; continue; } else if (strchr(" -+#\'", *message)) *tptr++ = *message++; if (*message == '*') { // Get width from argument... message ++; width = va_arg(ap, int); snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width); tptr += strlen(tptr); } else { width = 0; while (isdigit(*message & 255)) { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *message; width = width * 10 + *message++ - '0'; } } if (*message == '.') { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *message; message ++; if (*message == '*') { // Get precision from argument... message ++; prec = va_arg(ap, int); snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec); tptr += strlen(tptr); } else { prec = 0; while (isdigit(*message & 255)) { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *message; prec = prec * 10 + *message++ - '0'; } } } if (*message == 'l' && message[1] == 'l') { size = 'L'; if (tptr < (tformat + sizeof(tformat) - 2)) { *tptr++ = 'l'; *tptr++ = 'l'; } message += 2; } else if (*message == 'h' || *message == 'l' || *message == 'L') { if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *message; size = *message++; } else size = 0; if (!*message) break; if (tptr < (tformat + sizeof(tformat) - 1)) *tptr++ = *message; type = *message++; *tptr = '\0'; switch (type) { case 'E' : // Floating point formats case 'G' : case 'e' : case 'f' : case 'g' : snprintf(bufptr, (size_t)(bufptr - bufend + 1), tformat, va_arg(ap, double)); bufptr += strlen(bufptr); break; case 'B' : // Integer formats case 'X' : case 'b' : case 'd' : case 'i' : case 'o' : case 'u' : case 'x' : # ifdef HAVE_LONG_LONG if (size == 'L') snprintf(bufptr, (size_t)(bufptr - bufend + 1), tformat, va_arg(ap, long long)); else # endif // HAVE_LONG_LONG if (size == 'l') snprintf(bufptr, (size_t)(bufptr - bufend + 1), tformat, va_arg(ap, long)); else snprintf(bufptr, (size_t)(bufptr - bufend + 1), tformat, va_arg(ap, int)); bufptr += strlen(bufptr); break; case 'p' : // Log a pointer snprintf(bufptr, (size_t)(bufptr - bufend + 1), "%p", va_arg(ap, void *)); bufptr += strlen(bufptr); break; case 'c' : // Character or character array if (width <= 1) { *bufptr++ = (char)va_arg(ap, int); } else { if ((bufend - bufptr) < width) width = (int)(bufend - bufptr); memcpy(bufptr, va_arg(ap, char *), (size_t)width); bufptr += width; } break; case 's' : // Log a string if ((sval = va_arg(ap, char *)) == NULL) sval = "(null)"; while (*sval && bufptr < bufend) { int val = (*sval++) & 255; if (val < ' ' || val == 0x7f || val == '\\' || val == '\'' || val == '\"') { // Escape control and special characters in the string... if (bufptr > (bufend - 4)) break; *bufptr++ = '\\'; if (val == '\\') *bufptr++ = '\\'; else if (val == '\'') *bufptr++ = '\''; else if (val == '\"') *bufptr++ = '\"'; else if (val == '\n') *bufptr++ = 'n'; else if (val == '\r') *bufptr++ = 'r'; else if (val == '\t') *bufptr++ = 't'; else { // Use octal escape for other control characters... *bufptr++ = (char)('0' + (val / 64)); *bufptr++ = (char)('0' + ((val / 8) & 7)); *bufptr++ = (char)('0' + (val & 7)); } } else *bufptr++ = (char)val; } break; default : // Something else we don't support strlcpy(bufptr, tformat, (size_t)(bufptr - bufend + 1)); bufptr += strlen(bufptr); break; } } else *bufptr++ = *message++; } // Add a newline and write it out... *bufptr++ = '\n'; write(system->logfd, buffer, (size_t)(bufptr - buffer)); } pappl-1.0.3/pappl/log.h000066400000000000000000000030051403603036100146670ustar00rootroot00000000000000// // Logging header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_LOG_H_ # define _PAPPL_LOG_H_ // // Include necessary headers... // # include "base.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Constants... // typedef enum pappl_loglevel_e // Log levels { PAPPL_LOGLEVEL_UNSPEC = -1, // Not specified PAPPL_LOGLEVEL_DEBUG, // Debug message PAPPL_LOGLEVEL_INFO, // Informational message PAPPL_LOGLEVEL_WARN, // Warning message PAPPL_LOGLEVEL_ERROR, // Error message PAPPL_LOGLEVEL_FATAL // Fatal message } pappl_loglevel_t; // // Functions... // extern void papplLog(pappl_system_t *system, pappl_loglevel_t level, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3,4); extern void papplLogClient(pappl_client_t *client, pappl_loglevel_t level, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3, 4); extern void papplLogDevice(const char *message, void *data) _PAPPL_PUBLIC; extern void papplLogJob(pappl_job_t *job, pappl_loglevel_t level, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3, 4); extern void papplLogPrinter(pappl_printer_t *printer, pappl_loglevel_t level, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3, 4); // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_LOG_H_ pappl-1.0.3/pappl/lookup.c000066400000000000000000000024021403603036100154120ustar00rootroot00000000000000// // Lookup functions for the Printer Application Framework // // Copyright © 2020-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "base-private.h" // // '_papplLookupString()' - Lookup the string value for a bit. // const char * // O - Keyword or `NULL` _papplLookupString( unsigned value, // I - Bit value size_t num_strings, // I - Number of strings const char * const *strings) // I - Strings { size_t i; // Looking var unsigned bit; // Current bit for (i = 0, bit = 1; i < num_strings; i ++, bit *= 2) { if (bit == value) return (strings[i]); } return (NULL); } // // '_papplLookupValue()' - Lookup the bit value for a string. // unsigned // O - Bit value or `0` _papplLookupValue( const char *value, // I - Keyword value size_t num_strings, // I - Number of strings const char * const *strings) // I - Strings { size_t i; // Looking var unsigned bit; // Current bit if (!value) return (0); for (i = 0, bit = 1; i < num_strings; i ++, bit *= 2) { if (!strcmp(strings[i], value)) return (bit); } return (0); } pappl-1.0.3/pappl/mainloop-private.h000066400000000000000000000065071403603036100174060ustar00rootroot00000000000000// // Private mainloop header file for the Printer Application Framework // // Copyright � 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_MAINLOOP_PRIVATE_H_ # define _PAPPL_MAINLOOP_PRIVATE_H_ // // Include necessary headers // # include "mainloop.h" # include "base-private.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Globals... // extern char *_papplMainloopPath _PAPPL_PRIVATE; // // Functions... // extern int _papplMainloopAddPrinter(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopAutoAddPrinters(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopCancelJob(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopDeletePrinter(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopGetSetDefaultPrinter(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopModifyPrinter(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopRunServer(const char *base_name, const char *version, const char *footer_html, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_driver_cb_t driver_cb, int num_options, cups_option_t *options, pappl_ml_system_cb_t system_cb, void *data) _PAPPL_PRIVATE; extern int _papplMainloopShowDevices(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopShowDrivers(const char *base_name, int num_options, cups_option_t *options, pappl_ml_system_cb_t system_cb, void *data) _PAPPL_PRIVATE; extern int _papplMainloopShowJobs(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopShowOptions(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopShowPrinters(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopShowStatus(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopShutdownServer(const char *base_name, int num_options, cups_option_t *options) _PAPPL_PRIVATE; extern int _papplMainloopSubmitJob(const char *base_name, int num_options, cups_option_t *options, int num_files, char **files) _PAPPL_PRIVATE; extern void _papplMainloopAddOptions(ipp_t *request, int num_options, cups_option_t *options, ipp_t *supported) _PAPPL_PRIVATE; extern void _papplMainloopAddPrinterURI(ipp_t *request, const char *printer_name, char *resource,size_t rsize) _PAPPL_PRIVATE; extern http_t *_papplMainloopConnect(const char *base_name, bool auto_start) _PAPPL_PRIVATE; extern http_t *_papplMainloopConnectURI(const char *base_name, const char *printer_uri, char *resource, size_t rsize) _PAPPL_PRIVATE; extern char *_papplMainloopGetDefaultPrinter(http_t *http, char *buffer, size_t bufsize) _PAPPL_PRIVATE; extern char *_papplMainloopGetServerPath(const char *base_name, char *buffer, size_t bufsize) _PAPPL_PRIVATE; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus # endif // !_PAPPL_MAIN_PRIVATE_H pappl-1.0.3/pappl/mainloop-subcommands.c000066400000000000000000001411421403603036100202350ustar00rootroot00000000000000// // Standard papplMainloop sub-commands for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers // # include "pappl-private.h" // // Local types... // typedef struct _pappl_ml_autoadd_s // Auto-add data { const char *base_name; // Base name cups_array_t *printers; // Printers array http_t *http; // HTTP connection to server } _pappl_ml_autoadd_t; typedef struct _pappl_ml_printer_s // Printer data { char *name; // Queue name, if any bool seen; // Was the printer seen? char *device_uri; // Device URI char *device_id; // IEEE-1284 device ID } _pappl_ml_printer_t; // // Local functions // static int compare_printers(_pappl_ml_printer_t *a, _pappl_ml_printer_t *b); static _pappl_ml_printer_t *copy_printer(_pappl_ml_printer_t *p); static char *copy_stdin(const char *base_name, char *name, size_t namesize); static bool device_autoadd_cb(const char *device_info, const char *device_uri, const char *device_id, void *data); static void device_error_cb(const char *message, void *err_data); static bool device_list_cb(const char *device_info, const char *device_uri, const char *device_id, void *data); static void free_printer(_pappl_ml_printer_t *p); static char *get_value(ipp_attribute_t *attr, const char *name, int element, char *buffer, size_t bufsize); static void print_option(ipp_t *response, const char *name); // // '_papplMainloopAddPrinter()' - Add a printer. // int // O - Exit status _papplMainloopAddPrinter( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { http_t *http; // Connection to server ipp_t *request; // Create-Printer request const char *device_uri, // Device URI *driver_name, // Name of driver *printer_name, // Name of printer *printer_uri; // Printer URI // Get required values... device_uri = cupsGetOption("smi2699-device-uri", num_options, options); driver_name = cupsGetOption("smi2699-device-command", num_options, options); printer_name = cupsGetOption("printer-name", num_options, options); if (!device_uri || !driver_name || !printer_name) { if (!printer_name) fprintf(stderr, "%s: Missing '-d PRINTER'.\n", base_name); if (!driver_name) fprintf(stderr, "%s: Missing '-m DRIVER-NAME'.\n", base_name); if (!device_uri) fprintf(stderr, "%s: Missing '-v DEVICE-URI'.\n", base_name); return (1); } if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { char resource[1024]; // Resource path // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else if ((http = _papplMainloopConnect(base_name, true)) == NULL) { return (1); } // Send a Create-Printer request to the server... request = ippNewRequest(IPP_OP_CREATE_PRINTER); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "printer-service-type", NULL, "print"); ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, printer_name); ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "smi2699-device-command", NULL, driver_name); ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "smi2699-device-uri", NULL, device_uri); _papplMainloopAddOptions(request, num_options, options, NULL); ippDelete(cupsDoRequest(http, request, "/ipp/system")); httpClose(http); if (cupsLastError() != IPP_STATUS_OK) { fprintf(stderr, "%s: Unable to add printer - %s\n", base_name, cupsLastErrorString()); return (1); } return (0); } // // '_papplMainloopAutoAddPrinters()' - Automatically add printers. // int // O - Exit status _papplMainloopAutoAddPrinters( const char *base_name, // I - Basename of application int num_options, // I - Number of options cups_option_t *options) // I - Options { _pappl_ml_autoadd_t autoadd; // Auto-add callback data ipp_t *request, // IPP request *response; // IPP response ipp_attribute_t *attr; // Current attribute const char *attrname; // Attribute name _pappl_ml_printer_t printer; // Current printer (void)num_options; (void)options; // Try connecting to server... if ((autoadd.http = _papplMainloopConnect(base_name, true)) == NULL) return (1); // Build an array of printers... autoadd.printers = cupsArrayNew3((cups_array_func_t)compare_printers, NULL, NULL, 0, (cups_acopy_func_t)copy_printer, (cups_afree_func_t)free_printer); request = ippNewRequest(IPP_OP_GET_PRINTERS); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(autoadd.http, request, "/ipp/system"); for (attr = ippFirstAttribute(response); attr; attr = ippNextAttribute(response)) { if (ippGetGroupTag(attr) == IPP_TAG_OPERATION) continue; // Get a single printer... memset(&printer, 0, sizeof(printer)); while (ippGetGroupTag(attr) == IPP_TAG_PRINTER) { attrname = ippGetName(attr); if (!strcmp(attrname, "printer-name")) printer.name = (char *)ippGetString(attr, 0, NULL); else if (!strcmp(attrname, "printer-device-id")) printer.device_id = (char *)ippGetString(attr, 0, NULL); else if (!strcmp(attrname, "smi2699-device-uri")) printer.device_uri = (char *)ippGetString(attr, 0, NULL); attr = ippNextAttribute(response); } if (printer.name && printer.device_uri) cupsArrayAdd(autoadd.printers, &printer); } ippDelete(response); // Scan for USB devices that need to be auto-added... autoadd.base_name = base_name; papplDeviceList(PAPPL_DEVTYPE_USB, (pappl_device_cb_t)device_autoadd_cb, &autoadd, device_error_cb, (void *)base_name); // Close the connection to the server and return... cupsArrayDelete(autoadd.printers); httpClose(autoadd.http); return (0); } // // '_papplMainloopCancelJob()' - Cancel job(s). // int // O - Exit status _papplMainloopCancelJob( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { const char *printer_uri, // Printer URI *printer_name; // Printer name char default_printer[256], // Default printer resource[1024]; // Resource path http_t *http; // Server connection ipp_t *request; // IPP request const char *value; // Option value int job_id = 0; // job-id if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else { // Connect to the server and get the destination printer... if ((http = _papplMainloopConnect(base_name, true)) == NULL) return (1); if ((printer_name = cupsGetOption("printer-name", num_options, options)) == NULL) { if ((printer_name = _papplMainloopGetDefaultPrinter(http, default_printer, sizeof(default_printer))) == NULL) { fprintf(stderr, "%s: No default printer available.\n", base_name); httpClose(http); return (1); } } } // Figure out which job(s) to cancel... if (cupsGetOption("cancel-all", num_options, options)) { request = ippNewRequest(IPP_OP_CANCEL_MY_JOBS); } else if ((value = cupsGetOption("job-id", num_options, options)) != NULL) { char *end; // End of value request = ippNewRequest(IPP_OP_CANCEL_JOB); job_id = (int)strtol(value, &end, 10); if (job_id < 1 || errno == ERANGE || *end) { fprintf(stderr, "%s: Bad job ID.\n", base_name); httpClose(http); return (1); } } else request = ippNewRequest(IPP_OP_CANCEL_CURRENT_JOB); if (printer_uri) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); if (job_id) ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippDelete(cupsDoRequest(http, request, resource)); httpClose(http); if (cupsLastError() != IPP_STATUS_OK) { fprintf(stderr, "%s: Unable to cancel - %s\n", base_name, cupsLastErrorString()); return (1); } return (0); } // // '_papplMainloopDeletePrinter()' - Delete a printer. // int // O - Exit status _papplMainloopDeletePrinter( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { const char *printer_uri, // Printer URI *printer_name; // Printer name http_t *http; // Server connection ipp_t *request, // IPP request *response; // IPP response char resource[1024]; // Resource path int printer_id; // printer-id value // Connect to/start up the server and get the destination printer... if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else if ((http = _papplMainloopConnect(base_name, true)) == NULL) { return (1); } else if ((printer_name = cupsGetOption("printer-name", num_options, options)) == NULL) { fprintf(stderr, "%s: Missing '-d PRINTER'.\n", base_name); httpClose(http); return (1); } // Get the printer-id for the printer we are deleting... request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); if (printer_uri) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "printer-id"); response = cupsDoRequest(http, request, resource); printer_id = ippGetInteger(ippFindAttribute(response, "printer-id", IPP_TAG_INTEGER), 0); ippDelete(response); if (printer_id == 0) { fprintf(stderr, "%s: Unable to get information for printer: %s\n", base_name, cupsLastErrorString()); httpClose(http); return (1); } // Now that we have the printer-id, delete it from the system service... request = ippNewRequest(IPP_OP_DELETE_PRINTER); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "printer-id", printer_id); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippDelete(cupsDoRequest(http, request, "/ipp/system")); httpClose(http); if (cupsLastError() != IPP_STATUS_OK) { fprintf(stderr, "%s: Unable to delete printer: %s\n", base_name, cupsLastErrorString()); return (1); } return (0); } // // '_papplMainloopGetSetDefaultPrinter()' - Get/set the default printer. // int // O - Exit status _papplMainloopGetSetDefaultPrinter( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { const char *printer_uri, // Printer URI *printer_name; // Printer name http_t *http; // Server connection ipp_t *request, // IPP request *response; // IPP response char resource[1024]; // Resource path int printer_id; // printer-id value // Connect to/start up the server and get the destination printer... if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else if ((http = _papplMainloopConnect(base_name, true)) == NULL) { return (1); } if ((printer_name = cupsGetOption("printer-name", num_options, options)) == NULL) { char default_printer[256]; // Default printer if (_papplMainloopGetDefaultPrinter(http, default_printer, sizeof(default_printer))) puts(default_printer); else puts("No default printer set"); httpClose(http); return (0); } // OK, setting the default printer so get the printer-id for it... request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); if (printer_uri) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "printer-id"); response = cupsDoRequest(http, request, resource); printer_id = ippGetInteger(ippFindAttribute(response, "printer-id", IPP_TAG_INTEGER), 0); ippDelete(response); if (printer_id == 0) { fprintf(stderr, "%s: Unable to get information for '%s' - %s\n", base_name, printer_name, cupsLastErrorString()); httpClose(http); return (1); } // Now that we have the printer-id, set the system-default-printer-id // attribute for the system service... request = ippNewRequest(IPP_OP_SET_SYSTEM_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddInteger(request, IPP_TAG_SYSTEM, IPP_TAG_INTEGER, "system-default-printer-id", printer_id); ippDelete(cupsDoRequest(http, request, "/ipp/system")); httpClose(http); if (cupsLastError() != IPP_STATUS_OK) { fprintf(stderr, "%s: Unable to set default printer - %s\n", base_name, cupsLastErrorString()); return (1); } return (0); } // // '_papplMainloopModifyPrinter()' - Modify printer. // int // O - Exit status _papplMainloopModifyPrinter( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { http_t *http; // Connection to server ipp_t *request, // Set-Printer-Attributes request *supported; // Supported attributes const char *printer_uri, // Printer URI *printer_name; // Name of printer char resource[1024]; // Resource path // Open a connection to the server... if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else if ((http = _papplMainloopConnect(base_name, true)) == NULL) { return (1); } else if ((printer_name = cupsGetOption("printer-name", num_options, options)) == NULL) { fprintf(stderr, "%s: Missing '-d PRINTER'.\n", base_name); return (1); } // Send a Get-Printer-Attributes request... request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); if (printer_uri) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); supported = cupsDoRequest(http, request, resource); // Send a Set-Printer-Attributes request to the server... request = ippNewRequest(IPP_OP_SET_PRINTER_ATTRIBUTES); if (printer_uri) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); _papplMainloopAddOptions(request, num_options, options, supported); ippDelete(supported); ippDelete(cupsDoRequest(http, request, resource)); httpClose(http); if (cupsLastError() != IPP_STATUS_OK) { fprintf(stderr, "%s: Unable to modify printer: %s\n", base_name, cupsLastErrorString()); return (1); } return (0); } // // '_papplMainloopRunServer()' - Run server. // int // O - Exit status _papplMainloopRunServer( const char *base_name, // I - Base name const char *version, // I - Version number const char *footer_html, // I - Footer HTML or `NULL` for none int num_drivers, // I - Number of drivers pappl_pr_driver_t *drivers, // I - Drivers pappl_pr_autoadd_cb_t autoadd_cb, // I - Auto-add callback pappl_pr_driver_cb_t driver_cb, // I - Driver callback int num_options, // I - Number of options cups_option_t *options, // I - Options pappl_ml_system_cb_t system_cb, // I - System callback void *data) // I - Callback data { pappl_system_t *system; // System object char sockname[1024], // Socket filename statename[1024];// State filename // Create the system object... if (system_cb) { system = (system_cb)(num_options, options, data); } else { const char *directory = cupsGetOption("spool-directory", num_options, options), // Spool directory *logfile = cupsGetOption("log-file", num_options, options), // Log file *server_name = cupsGetOption("server-name", num_options, options), // Hostname *value; // Other option pappl_loglevel_t loglevel = PAPPL_LOGLEVEL_WARN; // Log level int port = 0; // Port // Collect standard options... if ((value = cupsGetOption("log-level", num_options, options)) != NULL) { if (!strcmp(value, "fatal")) loglevel = PAPPL_LOGLEVEL_FATAL; else if (!strcmp(value, "error")) loglevel = PAPPL_LOGLEVEL_ERROR; else if (!strcmp(value, "warn")) loglevel = PAPPL_LOGLEVEL_WARN; else if (!strcmp(value, "info")) loglevel = PAPPL_LOGLEVEL_INFO; else if (!strcmp(value, "debug")) loglevel = PAPPL_LOGLEVEL_DEBUG; } if ((value = cupsGetOption("server-port", num_options, options)) != NULL) { char *end; // End of value port = (int)strtol(value, &end, 10); if (port < 0 || errno == ERANGE || *end) { fprintf(stderr, "%s: Bad 'server-port' value.\n", base_name); return (1); } } // Create the system object... system = papplSystemCreate(PAPPL_SOPTIONS_MULTI_QUEUE | PAPPL_SOPTIONS_WEB_INTERFACE, base_name, port, "_print,_universal", directory, logfile, loglevel, cupsGetOption("auth-service", num_options, options), false); // Set any admin group and listen for network connections... if ((value = cupsGetOption("admin-group", num_options, options)) != NULL) papplSystemSetAdminGroup(system, value); if (!cupsGetOption("private-server", num_options, options)) papplSystemAddListeners(system, server_name); } if (!system) { fprintf(stderr, "%s: Failed to create a system.\n", base_name); return (1); } // Set the version number as needed... if (system->num_versions == 0 && version) { pappl_version_t sysversion; // System version memset(&sysversion, 0, sizeof(sysversion)); strlcpy(sysversion.name, base_name, sizeof(sysversion.name)); strlcpy(sysversion.sversion, version, sizeof(sysversion.sversion)); sscanf(version, "%hu.%hu.%hu.%hu", sysversion.version + 0, sysversion.version + 1, sysversion.version + 2, sysversion.version + 3); papplSystemSetVersions(system, 1, &sysversion); } // Set the footer HTML as needed... if (!system->footer_html && footer_html) papplSystemSetFooterHTML(system, footer_html); // Set the driver info as needed... if (system->num_drivers == 0 && num_drivers > 0 && drivers && driver_cb) papplSystemSetPrinterDrivers(system, num_drivers, drivers, autoadd_cb, /* create_cb */NULL, driver_cb, data); // Listen for connections... papplSystemAddListeners(system, _papplMainloopGetServerPath(base_name, sockname, sizeof(sockname))); // Finish initialization... if (!system->save_cb) { const char *tmpdir = getenv("TMPDIR"); // Temporary directory // Register a callback for saving state information, then load any // previous state... #ifdef __APPLE__ if (!tmpdir) tmpdir = "/private/tmp"; #else if (!tmpdir) tmpdir = "/tmp"; #endif // __APPLE__ snprintf(statename, sizeof(statename), "%s/%s%d.state", tmpdir, base_name, (int)getuid()); papplSystemLoadState(system, statename); papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState, (void *)statename); } // Run the system until shutdown... papplSystemRun(system); papplSystemDelete(system); return (0); } // // '_papplMainlooploopShowDevices()' - Show available devices. // int // O - Exit status _papplMainloopShowDevices( const char *base_name, // I - Basename of application int num_options, // I - Number of options cups_option_t *options) // I - Options { papplDeviceList(PAPPL_DEVTYPE_ALL, (pappl_device_cb_t)device_list_cb, (void *)cupsGetOption("verbose", num_options, options), (pappl_deverror_cb_t)device_error_cb, (void *)base_name); return (0); } // // '_papplMainlooploopShowDrivers()' - Show available drivers. // int // O - Exit status _papplMainloopShowDrivers( const char *base_name, // I - Basename of application int num_options, // I - Number of options cups_option_t *options, // I - Options pappl_ml_system_cb_t system_cb, // I - System callback void *data) // I - Callback data { int i; // Looping variable pappl_system_t *system; // System object if (!system_cb) { fprintf(stderr, "%s: No system callback specified.\n", base_name); return (1); } if ((system = (system_cb)(num_options, options, data)) == NULL) { fprintf(stderr, "%s: Failed to create a system.\n", base_name); return (1); } for (i = 0; i < system->num_drivers; i ++) printf("%-39s %s\n", system->drivers[i].name, system->drivers[i].description); papplSystemDelete(system); return (0); } // // '_papplMainloopShowJobs()' - Show pending printer jobs. // int // O - Exit status _papplMainloopShowJobs( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { const char *printer_uri, // Printer URI *printer_name; // Printer name char default_printer[256], // Default printer resource[1024]; // Resource path http_t *http; // Server connection ipp_t *request, // IPP request *response; // IPP response ipp_attribute_t *attr; // Current attribute const char *attrname; // Attribute name int job_id, // Current job-id job_state; // Current job-state const char *job_name, // Current job-name *job_user; // Current job-originating-user-name if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else { // Connect to/start up the server and get the destination printer... if ((http = _papplMainloopConnect(base_name, true)) == NULL) return (1); if ((printer_name = cupsGetOption("printer-name", num_options, options)) == NULL) { if ((printer_name = _papplMainloopGetDefaultPrinter(http, default_printer, sizeof(default_printer))) == NULL) { fprintf(stderr, "%s: No default printer available.\n", base_name); httpClose(http); return (1); } } } // Send a Get-Jobs request... request = ippNewRequest(IPP_OP_GET_JOBS); if (printer_uri) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs", NULL, "all"); response = cupsDoRequest(http, request, resource); for (attr = ippFirstAttribute(response); attr; attr = ippNextAttribute(response)) { if (ippGetGroupTag(attr) == IPP_TAG_OPERATION) continue; job_id = 0; job_state = IPP_JSTATE_PENDING; job_name = "(none)"; job_user = "(unknown)"; while (ippGetGroupTag(attr) == IPP_TAG_JOB) { attrname = ippGetName(attr); if (!strcmp(attrname, "job-id")) job_id = ippGetInteger(attr, 0); else if (!strcmp(attrname, "job-name")) job_name = ippGetString(attr, 0, NULL); else if (!strcmp(attrname, "job-originating-user-name")) job_user = ippGetString(attr, 0, NULL); else if (!strcmp(attrname, "job-state")) job_state = ippGetInteger(attr, 0); attr = ippNextAttribute(response); } printf("%d %-12s %-16s %s\n", job_id, ippEnumString("job-state", job_state), job_user, job_name); } ippDelete(response); httpClose(http); return (0); } // // '_papplMainloopShowOptions()' - Show supported option. // int // O - Exit status _papplMainloopShowOptions( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { const char *printer_uri, // Printer URI *printer_name; // Printer name char default_printer[256]; // Default printer name http_t *http; // Server connection ipp_t *request, // IPP request *response; // IPP response char resource[1024]; // Resource path if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else { // Connect to/start up the server and get the destination printer... if ((http = _papplMainloopConnect(base_name, true)) == NULL) return (1); if ((printer_name = cupsGetOption("printer-name", num_options, options)) == NULL) { if ((printer_name = _papplMainloopGetDefaultPrinter(http, default_printer, sizeof(default_printer))) == NULL) { fprintf(stderr, "%s: No default printer available.\n", base_name); httpClose(http); return (1); } } } // Get the xxx-supported and xxx-default attributes request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); if (printer_uri) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, resource); if (cupsLastError() != IPP_STATUS_OK) { fprintf(stderr, "%s: Unable to get printer options: %s\n", base_name, cupsLastErrorString()); ippDelete(response); httpClose(http); return (1); } printf("Print job options:\n"); printf(" -c copies\n"); print_option(response, "media"); print_option(response, "media-source"); print_option(response, "media-top-offset"); print_option(response, "media-tracking"); print_option(response, "media-type"); print_option(response, "orientation-requested"); print_option(response, "print-color-mode"); print_option(response, "print-content-optimize"); if (ippFindAttribute(response, "print-darkness-supported", IPP_TAG_ZERO)) printf(" -o print-darkness=-100 to 100\n"); print_option(response, "print-quality"); print_option(response, "print-speed"); print_option(response, "printer-resolution"); printf("\n"); printf("Printer options:\n"); print_option(response, "label-mode"); print_option(response, "label-tear-offset"); if (ippFindAttribute(response, "printer-darkness-supported", IPP_TAG_ZERO)) printf(" -o printer-darkness=0 to 100\n"); printf(" -o printer-geo-location='geo:LATITUDE,LONGITUDE'\n"); printf(" -o printer-location='LOCATION'\n"); printf(" -o printer-organization='ORGANIZATION'\n"); printf(" -o printer-organizational-unit='UNIT/SECTION'\n"); ippDelete(response); httpClose(http); return (0); } // // 'papplMainShowPrinters()' - Show printer queues. // int // O - Exit status _papplMainloopShowPrinters( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { http_t *http; // Server connection ipp_t *request, // IPP request *response; // IPP response ipp_attribute_t *attr; // Current attribute (void)num_options; (void)options; // Connect to/start up the server and get the list of printers... if ((http = _papplMainloopConnect(base_name, true)) == NULL) return (1); request = ippNewRequest(IPP_OP_GET_PRINTERS); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, "/ipp/system"); for (attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME); attr; attr = ippFindNextAttribute(response, "printer-name", IPP_TAG_NAME)) puts(ippGetString(attr, 0, NULL)); ippDelete(response); httpClose(http); return (0); } // // '_papplMainloopShowStatus()' - Show system/printer status. // int // O - Exit status _papplMainloopShowStatus( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { http_t *http; // HTTP connection const char *printer_uri, // Printer URI *printer_name; // Printer name char resource[1024]; // Resource path ipp_t *request, // IPP request *response; // IPP response int i, // Looping var count, // Number of reasons state; // *-state value ipp_attribute_t *state_reasons; // *-state-reasons attribute time_t state_time; // *-state-change-time value const char *reason; // *-state-reasons value static const char * const states[] = // *-state strings { "idle", "processing jobs", "stopped" }; static const char * const pattrs[] = { // Requested printer attributes "printer-state", "printer-state-change-date-time", "printer-state-reasons" }; static const char * const sysattrs[] = { // Requested system attributes "system-state", "system-state-change-date-time", "system-state-reasons" }; if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else { // Connect to the server... if ((http = _papplMainloopConnect(base_name, false)) == NULL) { puts("Server is not running."); return (0); } } if (printer_uri || (printer_name = cupsGetOption("printer-name", num_options, options)) != NULL) { // Get the printer's status request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); if (printer_uri) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs); response = cupsDoRequest(http, request, resource); state = ippGetInteger(ippFindAttribute(response, "printer-state", IPP_TAG_ENUM), 0); state_time = ippDateToTime(ippGetDate(ippFindAttribute(response, "printer-state-change-date-time", IPP_TAG_DATE), 0)); state_reasons = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD); } else { // Get the system status request = ippNewRequest(IPP_OP_GET_SYSTEM_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(sysattrs) / sizeof(sysattrs[0])), NULL, sysattrs); response = cupsDoRequest(http, request, "/ipp/system"); state = ippGetInteger(ippFindAttribute(response, "system-state", IPP_TAG_ENUM), 0); state_time = ippDateToTime(ippGetDate(ippFindAttribute(response, "system-state-change-date-time", IPP_TAG_DATE), 0)); state_reasons = ippFindAttribute(response, "system-state-reasons", IPP_TAG_KEYWORD); } if (state < IPP_PSTATE_IDLE) state = IPP_PSTATE_IDLE; else if (state > IPP_PSTATE_STOPPED) state = IPP_PSTATE_STOPPED; printf("Running, %s since %s\n", states[state - IPP_PSTATE_IDLE], httpGetDateString(state_time)); if (state_reasons) { for (i = 0, count = ippGetCount(state_reasons); i < count; i ++) { reason = ippGetString(state_reasons, i, NULL); if (strcmp(reason, "none")) puts(reason); } } ippDelete(response); return (0); } // // '_papplMainloopShutdownServer()' - Shutdown the server. // int // O - Exit status _papplMainloopShutdownServer( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options) // I - Options { http_t *http; // HTTP connection ipp_t *request; // IPP request (void)num_options; (void)options; // Try connecting to the server... if ((http = _papplMainloopConnect(base_name, false)) == NULL) { fprintf(stderr, "%s: Server is not running.\n", base_name); return (1); } request = ippNewRequest(IPP_OP_SHUTDOWN_ALL_PRINTERS); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippDelete(cupsDoRequest(http, request, "/ipp/system")); if (cupsLastError() != IPP_STATUS_OK) { fprintf(stderr, "%s: Unable to shutdown server: %s\n", base_name, cupsLastErrorString()); return (1); } return (0); } // // '_papplMainloopSubmitJob()' - Submit job(s). // int // O - Exit status _papplMainloopSubmitJob( const char *base_name, // I - Base name int num_options, // I - Number of options cups_option_t *options, // I - Options int num_files, // I - Number of files char **files) // I - Files { const char *document_format, // Document format *document_name, // Document name *filename, // Current print filename *job_name, // Job name *printer_name, // Printer name *printer_uri; // Printer URI http_t *http; // Server connection ipp_t *request, // IPP request *response, // IPP response *supported; // Supported attributes char default_printer[256], // Default printer name resource[1024], // Resource path tempfile[1024] = ""; // Temporary file int i; // Looping var char *stdin_file; // Dummy filename for passive stdin jobs ipp_attribute_t *job_id; // job-id for created job // If there are no input files and stdin is not a TTY, treat that as an // implicit request to print from stdin... if (num_files == 0 && !isatty(0)) { stdin_file = (char *)"-"; files = &stdin_file; num_files = 1; } if (num_files == 0) { fprintf(stderr, "%s: No files to print.\n", base_name); return (1); } if ((printer_uri = cupsGetOption("printer-uri", num_options, options)) != NULL) { // Connect to the remote printer... if ((http = _papplMainloopConnectURI(base_name, printer_uri, resource, sizeof(resource))) == NULL) return (1); } else { // Connect to/start up the server and get the destination printer... if ((http = _papplMainloopConnect(base_name, true)) == NULL) return (1); if ((printer_name = cupsGetOption("printer-name", num_options, options)) == NULL) { if ((printer_name = _papplMainloopGetDefaultPrinter(http, default_printer, sizeof(default_printer))) == NULL) { fprintf(stderr, "%s: No default printer available.\n", base_name); httpClose(http); return (1); } } } // Loop through the print files job_name = cupsGetOption("job-name", num_options, options); document_format = cupsGetOption("document-format", num_options, options); for (i = 0; i < num_files; i ++) { // Get the current print file... if (!strcmp(files[i], "-")) { if (!copy_stdin(base_name, tempfile, sizeof(tempfile))) { httpClose(http); return (1); } filename = tempfile; document_name = "(stdin)"; } else { filename = files[i]; if ((document_name = strrchr(filename, '/')) != NULL) document_name ++; else document_name = filename; } // Send a Get-Printer-Attributes request... request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); if (printer_uri) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); supported = cupsDoRequest(http, request, resource); // Send a Print-Job request... request = ippNewRequest(IPP_OP_PRINT_JOB); if (printer_uri) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); else _papplMainloopAddPrinterURI(request, printer_name, resource, sizeof(resource)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, job_name ? job_name : document_name); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", NULL, document_name); if (document_format) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, document_format); _papplMainloopAddOptions(request, num_options, options, supported); ippDelete(supported); response = cupsDoFileRequest(http, request, resource, filename); if ((job_id = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL) { fprintf(stderr, "%s: Unable to print '%s': %s\n", base_name, filename, cupsLastErrorString()); ippDelete(response); httpClose(http); return (1); } if (printer_uri) printf("%d\n", ippGetInteger(job_id, 0)); else printf("%s-%d\n", printer_name, ippGetInteger(job_id, 0)); ippDelete(response); if (tempfile[0]) unlink(tempfile); } httpClose(http); return (0); } // // 'compare_printers()' - Compare two mainloop printers. // static int // O - Result of comparison compare_printers( _pappl_ml_printer_t *a, // I - First printer _pappl_ml_printer_t *b) // I - Second printer { return (strcmp(a->device_uri, b->device_uri)); } // // 'copy_printer()' - Copy a mainloop printer. // static _pappl_ml_printer_t * // O - New printer copy_printer(_pappl_ml_printer_t *p) // I - Printer to copy { _pappl_ml_printer_t *np; // New printer if ((np = (_pappl_ml_printer_t *)calloc(1, sizeof(_pappl_ml_printer_t))) != NULL) { np->name = p->name ? strdup(p->name) : NULL; np->seen = p->seen; np->device_uri = strdup(p->device_uri); np->device_id = p->device_id ? strdup(p->device_id) : NULL; if (!np->device_uri || (p->name && !np->name) || (p->device_id && !np->device_id)) { free_printer(np); return (NULL); } } return (np); } // // 'copy_stdin()' - Copy print data from the standard input. // static char * // O - Temporary filename or `NULL` on error copy_stdin( const char *base_name, // I - Printer application name char *name, // I - Filename buffer size_t namesize) // I - Size of filename buffer { int tempfd; // Temporary file descriptor size_t total = 0; // Total bytes read/written ssize_t bytes; // Number of bytes read/written char buffer[65536]; // Copy buffer // Create a temporary file for printing... if ((tempfd = cupsTempFd(name, (int)namesize)) < 0) { fprintf(stderr, "%s: Unable to create temporary file: %s\n", base_name, strerror(errno)); return (NULL); } // Read from stdin until we see EOF... while ((bytes = read(0, buffer, sizeof(buffer))) > 0) { if (write(tempfd, buffer, (size_t)bytes) < 0) { fprintf(stderr, "%s: Unable to write to temporary file: %s\n", base_name, strerror(errno)); goto fail; } total += (size_t)bytes; } // Only allow non-empty files... if (total == 0) { fprintf(stderr, "%s: Empty print file received on the standard input.\n", base_name); goto fail; } // Close the temporary file and return it... close(tempfd); return (name); // If we get here, something went wrong... fail: // Close and remove the temporary file... close(tempfd); unlink(name); // Return NULL and an empty filename... *name = '\0'; return (NULL); } // // 'device_autoadd_cb()' - Device callback. // static bool // O - `true` to stop, `false` to continue device_autoadd_cb( const char *device_info, // I - Device description const char *device_uri, // I - Device URI const char *device_id, // I - IEEE-1284 device ID void *data) // I - Driver callback { _pappl_ml_printer_t key, // Key *printer; // Matching printer _pappl_ml_autoadd_t *autoadd = (_pappl_ml_autoadd_t *)data; // Auto-add data ipp_t *request; // IPP request // See if the printer has already been added... key.device_uri = (char *)device_uri; if ((printer = cupsArrayFind(autoadd->printers, &key)) != NULL) { // Printer already added, mark it as seen... printer->seen = true; } else { // Printer not already added, see if we have a driver... request = ippNewRequest(IPP_OP_CREATE_PRINTER); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "printer-service-type", NULL, "print"); ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, device_info); ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id); ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "smi2699-device-command", NULL, "auto"); ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "smi2699-device-uri", NULL, device_uri); ippDelete(cupsDoRequest(autoadd->http, request, "/ipp/system")); if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST && cupsLastError() != IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES) fprintf(stderr, "%s: Unable to add '%s' - %s\n", autoadd->base_name, device_info, cupsLastErrorString()); } // Continue... return (false); } // // 'device_error_cb()' - Show a device error message. // static void device_error_cb(const char *message, // I - Error message void *data) // I - Callback data (application name) { printf("%s: %s\n", (char *)data, message); } // // 'device_list_cb()' - List a device. // static bool // O - `true` to stop, `false` to continue device_list_cb(const char *device_info, // I - Device description const char *device_uri, // I - Device URI const char *device_id, // I - IEEE-1284 device ID void *data) // I - Callback data (NULL for plain, "verbose" for verbose output) { puts(device_uri); if (device_info && data) printf(" %s\n", device_info); if (device_id && data) printf(" %s\n", device_id); return (false); } // // 'free_printer()' - Free a mainloop printer. // static void free_printer(_pappl_ml_printer_t *p) // I - Printer { free(p->name); free(p->device_uri); free(p->device_id); free(p); } // // 'get_value()' - Get the string representation of an attribute value. // static char * // O - String value get_value(ipp_attribute_t *attr, // I - Attribute const char *name, // I - Base name of attribute int element, // I - Value index char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { const char *value; // String value int intvalue; // Integer value int lower, // Lower range value upper; // Upper range value int xres, // X resolution yres; // Y resolution ipp_res_t units; // Resolution units *buffer = '\0'; switch (ippGetValueTag(attr)) { default : case IPP_TAG_KEYWORD : if ((value = ippGetString(attr, element, NULL)) != NULL) { if (!strcmp(name, "media")) { pwg_media_t *pwg = pwgMediaForPWG(value); // Media size if ((pwg->width % 100) == 0) snprintf(buffer, bufsize, "%s (%dx%dmm or %.2gx%.2gin)", value, pwg->width / 100, pwg->length / 100, pwg->width / 2540.0, pwg->length / 2540.0); else snprintf(buffer, bufsize, "%s (%.2gx%.2gin or %dx%dmm)", value, pwg->width / 2540.0, pwg->length / 2540.0, pwg->width / 100, pwg->length / 100); } else { strlcpy(buffer, value, bufsize); } } break; case IPP_TAG_ENUM : strlcpy(buffer, ippEnumString(name, ippGetInteger(attr, element)), bufsize); break; case IPP_TAG_INTEGER : intvalue = ippGetInteger(attr, element); if (!strcmp(name, "label-tear-offset") || !strcmp(name, "media-top-offset") || !strcmp(name, "print-speed")) { if ((intvalue % 635) == 0) snprintf(buffer, bufsize, "%.2gin", intvalue / 2540.0); else snprintf(buffer, bufsize, "%.2gmm", intvalue * 0.01); } else snprintf(buffer, bufsize, "%d", intvalue); break; case IPP_TAG_RANGE : lower = ippGetRange(attr, element, &upper); if (!strcmp(name, "label-tear-offset") || !strcmp(name, "media-top-offset") || !strcmp(name, "print-speed")) { if ((upper % 635) == 0) snprintf(buffer, bufsize, "%.2gin to %.2gin", lower / 2540.0, upper / 2540.0); else snprintf(buffer, bufsize, "%.2gmm to %.2gmm", lower * 0.01, upper * 0.01); } else snprintf(buffer, bufsize, "%d to %d", lower, upper); break; case IPP_TAG_RESOLUTION : xres = ippGetResolution(attr, element, &yres, &units); if (xres == yres) snprintf(buffer, bufsize, "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm"); else snprintf(buffer, bufsize, "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm"); break; } return (buffer); } // // 'print_option()' - Print the supported and default value for an option. // static void print_option(ipp_t *response, // I - Get-Printer-Attributes response const char *name) // I - Attribute name { char defname[256], // xxx-default/xxx-configured name supname[256]; // xxx-supported name ipp_attribute_t *defattr, // xxx-default/xxx-configured attribute *supattr; // xxx-supported attribute int i, // Looping var count; // Number of values char defvalue[256], // xxx-default/xxx-configured value supvalue[256]; // xxx-supported value // Get the default and supported attributes... snprintf(supname, sizeof(supname), "%s-supported", name); if ((supattr = ippFindAttribute(response, supname, IPP_TAG_ZERO)) == NULL) return; if (!strncmp(name, "media-", 6)) snprintf(defname, sizeof(defname), "media-col-default/%s", name); else snprintf(defname, sizeof(defname), "%s-default", name); if ((defattr = ippFindAttribute(response, defname, IPP_TAG_ZERO)) == NULL) { snprintf(defname, sizeof(defname), "%s-configured", name); defattr = ippFindAttribute(response, defname, IPP_TAG_ZERO); } get_value(defattr, name, 0, defvalue, sizeof(defvalue)); // Show the option with its values... if (defvalue[0]) printf(" -o %s=%s (default)\n", name, defvalue); for (i = 0, count = ippGetCount(supattr); i < count; i ++) { if (strcmp(defvalue, get_value(supattr, name, i, supvalue, sizeof(supvalue)))) printf(" -o %s=%s\n", name, supvalue); } } pappl-1.0.3/pappl/mainloop-support.c000066400000000000000000000421761403603036100174450ustar00rootroot00000000000000// // papplMainloop support functions for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers // # include "pappl-private.h" # include # include // // Globals... // char *_papplMainloopPath = NULL; // Path to self // // Local functions // static int get_length(const char *value); // // '_papplMainloopAddOptions()' - Add default/job template attributes from options. // void _papplMainloopAddOptions( ipp_t *request, // I - IPP request int num_options, // I - Number of options cups_option_t *options, // I - Options ipp_t *supported) // I - Supported attributes { ipp_attribute_t *job_attrs; // job-creation-attributes-supported int is_default; // Adding xxx-default attributes? ipp_tag_t group_tag; // Group to add to char *end; // End of value const char *value; // String value int intvalue; // Integer value const char *media_left_offset = cupsGetOption("media-left-offset", num_options, options), *media_source = cupsGetOption("media-source", num_options, options), *media_top_offset = cupsGetOption("media-top-offset", num_options, options), *media_tracking = cupsGetOption("media-tracking", num_options, options), *media_type = cupsGetOption("media-type", num_options, options); // media-xxx member values // Determine what kind of options we are adding... group_tag = ippGetOperation(request) == IPP_PRINT_JOB ? IPP_TAG_JOB : IPP_TAG_PRINTER; is_default = (group_tag == IPP_TAG_PRINTER); if (is_default) { // Add Printer Description attributes... if ((value = cupsGetOption("label-mode-configured", num_options, options)) != NULL) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "label-mode-configured", NULL, value); if ((value = cupsGetOption("label-tear-offset-configured", num_options, options)) != NULL) ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "label-tear-offset-configured", get_length(value)); if ((value = cupsGetOption("media-ready", num_options, options)) != NULL) { int num_values; // Number of values char *values[PAPPL_MAX_SOURCE], // Pointers to size strings *cvalue, // Copied value *cptr; // Pointer into copy num_values = 0; cvalue = strdup(value); cptr = cvalue; while (num_values < PAPPL_MAX_SOURCE && (values[num_values] = strsep(&cptr, ",")) != NULL) num_values ++; if (num_values > 0) ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_values, NULL, (const char * const *)values); free(cvalue); } if ((value = cupsGetOption("printer-darkness-configured", num_options, options)) != NULL) { if ((intvalue = (int)strtol(value, &end, 10)) >= 0 && intvalue <= 100 && errno != ERANGE && !*end) ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-darkness-configured", intvalue); } if ((value = cupsGetOption("printer-geo-location", num_options, options)) != NULL) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-geo-location", NULL, value); if ((value = cupsGetOption("printer-location", num_options, options)) != NULL) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, value); if ((value = cupsGetOption("printer-organization", num_options, options)) != NULL) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-organization", NULL, value); if ((value = cupsGetOption("printer-organizational-unit", num_options, options)) != NULL) ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-organizational-unit", NULL, value); } if ((value = cupsGetOption("copies", num_options, options)) == NULL) value = cupsGetOption("copies-default", num_options, options); if (value && (intvalue = (int)strtol(value, &end, 10)) >= 1 && intvalue <= 9999 && errno != ERANGE && !*end) ippAddInteger(request, group_tag, IPP_TAG_INTEGER, is_default ? "copies-default" : "copies", intvalue); value = cupsGetOption("media", num_options, options); if (media_left_offset || media_source || media_top_offset || media_tracking || media_type) { // Add media-col ipp_t *media_col = ippNew(); // media-col value pwg_media_t *pwg = pwgMediaForPWG(value); // Size if (pwg) { ipp_t *media_size = ippNew(); // media-size value ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "x-dimension", pwg->width); ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "y-dimension", pwg->length); ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size); ippDelete(media_size); } if (media_left_offset) ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-offset", get_length(media_left_offset)); if (media_source) ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media_source); if (media_top_offset) ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-offset", get_length(media_top_offset)); if (media_tracking) ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-tracking", NULL, media_tracking); if (media_type) ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-type", NULL, media_type); ippAddCollection(request, group_tag, is_default ? "media-col-default" : "media-col", media_col); ippDelete(media_col); } else if (value) { // Add media ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, is_default ? "media-default" : "media", NULL, value); } if ((value = cupsGetOption("orientation-requested", num_options, options)) == NULL) value = cupsGetOption("orientation-requested-default", num_options, options); if (value) { if ((intvalue = ippEnumValue("orientation-requested", value)) != 0) ippAddInteger(request, group_tag, IPP_TAG_ENUM, is_default ? "orientation-requested-default" : "orientation-requested", intvalue); else if ((intvalue = (int)strtol(value, &end, 10)) >= IPP_ORIENT_PORTRAIT && intvalue <= IPP_ORIENT_NONE && errno != ERANGE && !*end) ippAddInteger(request, group_tag, IPP_TAG_ENUM, is_default ? "orientation-requested-default" : "orientation-requested", intvalue); } if ((value = cupsGetOption("print-color-mode", num_options, options)) == NULL) value = cupsGetOption("print-color-mode-default", num_options, options); if (value) ippAddString(request, group_tag, IPP_TAG_KEYWORD, is_default ? "print-color-mode-default" : "print-color-mode", NULL, value); if ((value = cupsGetOption("print-content-optimize", num_options, options)) == NULL) value = cupsGetOption("print-content-optimize-default", num_options, options); if (value) ippAddString(request, group_tag, IPP_TAG_KEYWORD, is_default ? "print-content-optimize-mode-default" : "print-content-optimize", NULL, value); if ((value = cupsGetOption("print-darkness", num_options, options)) == NULL) value = cupsGetOption("print-darkness-default", num_options, options); if (value && (intvalue = (int)strtol(value, &end, 10)) >= -100 && intvalue <= 100 && errno != ERANGE && !*end) ippAddInteger(request, group_tag, IPP_TAG_INTEGER, is_default ? "print-darkness-default" : "print-darkness", intvalue); if ((value = cupsGetOption("print-quality", num_options, options)) == NULL) value = cupsGetOption("print-quality-default", num_options, options); if (value) { if ((intvalue = ippEnumValue("print-quality", value)) != 0) ippAddInteger(request, group_tag, IPP_TAG_ENUM, is_default ? "print-quality-default" : "print-quality", intvalue); else if ((intvalue = (int)strtol(value, &end, 10)) >= IPP_QUALITY_DRAFT && intvalue <= IPP_QUALITY_HIGH && errno != ERANGE && !*end) ippAddInteger(request, group_tag, IPP_TAG_ENUM, is_default ? "print-quality-default" : "print-quality", intvalue); } if ((value = cupsGetOption("print-scaling", num_options, options)) == NULL) value = cupsGetOption("print-scaling-default", num_options, options); if (value) ippAddString(request, group_tag, IPP_TAG_KEYWORD, is_default ? "print-scaling-default" : "print-scaling", NULL, value); if ((value = cupsGetOption("print-speed", num_options, options)) == NULL) value = cupsGetOption("print-speed-default", num_options, options); if (value) ippAddInteger(request, group_tag, IPP_TAG_INTEGER, is_default ? "print-speed-default" : "print-speed", get_length(value)); if ((value = cupsGetOption("printer-resolution", num_options, options)) == NULL) value = cupsGetOption("printer-resolution-default", num_options, options); if (value) { int xres, yres; // Resolution values char units[32]; // Resolution units if (sscanf(value, "%dx%d%31s", &xres, &yres, units) != 3) { if (sscanf(value, "%d%31s", &xres, units) != 2) { xres = 300; strlcpy(units, "dpi", sizeof(units)); } yres = xres; } ippAddResolution(request, group_tag, is_default ? "printer-resolution-default" : "printer-resolution", !strcmp(units, "dpi") ? IPP_RES_PER_INCH : IPP_RES_PER_CM, xres, yres); } // Vendor attributes/options if ((job_attrs = ippFindAttribute(supported, "job-creation-attributes-supported", IPP_TAG_KEYWORD)) != NULL) { int i, // Looping var count; // Count const char *name; // Attribute name char defname[128], // xxx-default name supname[128]; // xxx-supported name ipp_attribute_t *attr; // Attribute for (i = 0, count = ippGetCount(job_attrs); i < count; i ++) { name = ippGetString(job_attrs, i, NULL); snprintf(defname, sizeof(defname), "%s-default", name); snprintf(supname, sizeof(supname), "%s-supported", name); if ((value = cupsGetOption(name, num_options, options)) == NULL) value = cupsGetOption(defname, num_options, options); if (!value) continue; if (!strcmp(name, "copies") || !strcmp(name, "finishings") || !strcmp(name, "media") || !strcmp(name, "orientation-requested") || !strcmp(name, "print-color-mode") || !strcmp(name, "print-content-optimize") || !strcmp(name, "print-darkness") || !strcmp(name, "print-quality") || !strcmp(name, "print-scaling") || !strcmp(name, "print-speed") || !strcmp(name, "printer-resolution")) continue; if ((attr = ippFindAttribute(supported, supname, IPP_TAG_ZERO)) != NULL) { switch (ippGetValueTag(attr)) { case IPP_TAG_BOOLEAN : ippAddBoolean(request, group_tag, is_default ? defname : name, !strcmp(value, "true")); break; case IPP_TAG_INTEGER : case IPP_TAG_RANGE : intvalue = (int)strtol(value, &end, 10); if (errno != ERANGE && !*end) ippAddInteger(request, group_tag, IPP_TAG_INTEGER, is_default ? defname : name, intvalue); break; case IPP_TAG_KEYWORD : ippAddString(request, group_tag, IPP_TAG_KEYWORD, is_default ? defname : name, NULL, value); break; default : break; } } else { ippAddString(request, group_tag, IPP_TAG_TEXT, is_default ? defname : name, NULL, value); } } } } // // '_papplMainloopAddPrinterURI()' - Add the printer-uri attribute and return a // resource path. // void _papplMainloopAddPrinterURI( ipp_t *request, // I - IPP request const char *printer_name, // I - Printer name char *resource, // I - Resource path buffer size_t rsize) // I - Size of buffer { char uri[1024]; // printer-uri value snprintf(resource, rsize, "/ipp/print/%s", printer_name); httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, "localhost", 0, resource); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); } // // '_papplMainloopConnect()' - Connect to the local server. // http_t * // O - HTTP connection _papplMainloopConnect( const char *base_name, // I - Printer application name bool auto_start) // I - `true` to start server if not running { http_t *http; // HTTP connection char sockname[1024]; // Socket filename // See if the server is running... http = httpConnect2(_papplMainloopGetServerPath(base_name, sockname, sizeof(sockname)), 0, NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL); if (!http && auto_start) { // Nope, start it now... pid_t server_pid; // Server process ID posix_spawnattr_t server_attrs; // Server process attributes char * const server_argv[] = // Server command-line { _papplMainloopPath, "server", "-o", "private-server=true", NULL }; posix_spawnattr_init(&server_attrs); posix_spawnattr_setpgroup(&server_attrs, 0); if (posix_spawn(&server_pid, _papplMainloopPath, NULL, &server_attrs, server_argv, environ)) { fprintf(stderr, "%s: Unable to start server: %s\n", base_name, strerror(errno)); posix_spawnattr_destroy(&server_attrs); return (NULL); } posix_spawnattr_destroy(&server_attrs); // Wait for it to start... do { usleep(250000); } while (access(sockname, 0)); http = httpConnect2(sockname, 0, NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL); if (!http) fprintf(stderr, "%s: Unable to connect to server: %s\n", base_name, cupsLastErrorString()); } return (http); } // // '_papplMainloopConnectURI()' - Connect to an IPP printer directly. // http_t * // O - HTTP connection or `NULL` on error _papplMainloopConnectURI( const char *base_name, // I - Base Name const char *printer_uri, // I - Printer URI char *resource, // I - Resource path buffer size_t rsize) // I - Size of buffer { char scheme[32], // Scheme (ipp or ipps) userpass[256], // Username/password (unused) hostname[256]; // Hostname int port; // Port number http_encryption_t encryption; // Type of encryption to use http_t *http; // HTTP connection // First extract the components of the URI... if (httpSeparateURI(HTTP_URI_CODING_ALL, printer_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, (int)rsize) < HTTP_URI_STATUS_OK) { fprintf(stderr, "%s: Bad printer URI '%s'.\n", base_name, printer_uri); return (NULL); } if (strcmp(scheme, "ipp") && strcmp(scheme, "ipps")) { fprintf(stderr, "%s: Unsupported URI scheme '%s'.\n", base_name, scheme); return (NULL); } if (userpass[0]) fprintf(stderr, "%s: Warning - user credentials are not supported in URIs.\n", base_name); if (!strcmp(scheme, "ipps") || port == 443) encryption = HTTP_ENCRYPTION_ALWAYS; else encryption = HTTP_ENCRYPTION_IF_REQUESTED; if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) fprintf(stderr, "%s: Unable to connect to printer at '%s:%d': %s\n", base_name, hostname, port, cupsLastErrorString()); return (http); } // // '_papplMainloopGetDefaultPrinter' - Get the default printer. // char * // O - Default printer or `NULL` for none _papplMainloopGetDefaultPrinter( http_t *http, // I - HTTP connection char *buffer, // I - Buffer for printer name size_t bufsize) // I - Size of buffer { ipp_t *request, // IPP request *response; // IPP response const char *printer_name; // Printer name // Ask the server for its default printer request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "printer-name"); response = cupsDoRequest(http, request, "/ipp/system"); if ((printer_name = ippGetString(ippFindAttribute(response, "printer-name", IPP_TAG_NAME), 0, NULL)) != NULL) strlcpy(buffer, printer_name, bufsize); else *buffer = '\0'; ippDelete(response); return (*buffer ? buffer : NULL); } // // '_papplMainloopGetServerPath()' - Get the UNIX domain socket for the server. // char * // O - Socket filename _papplMainloopGetServerPath( const char *base_name, // I - Base name char *buffer, // I - Buffer for filename size_t bufsize) // I - Size of buffer { const char *tmpdir = getenv("TMPDIR"); // Temporary directory #ifdef __APPLE__ if (!tmpdir) tmpdir = "/private/tmp"; #else if (!tmpdir) tmpdir = "/tmp"; #endif // __APPLE__ snprintf(buffer, bufsize, "%s/%s%d.sock", tmpdir, base_name, (int)getuid()); _PAPPL_DEBUG("Creating domain socket as '%s'.\n", buffer); return (buffer); } // // 'get_length()' - Get a length in hundreths of millimeters. // static int // O - Length value get_length(const char *value) // I - Length string { double n; // Number char *units; // Pointer to units n = strtod(value, &units); if (units && !strcmp(units, "cm")) return ((int)(n * 1000.0)); if (units && !strcmp(units, "in")) return ((int)(n * 2540.0)); else if (units && !strcmp(units, "mm")) return ((int)(n * 100.0)); else if (units && !strcmp(units, "m")) return ((int)(n * 100000.0)); else return ((int)n); } pappl-1.0.3/pappl/mainloop.c000066400000000000000000000334001403603036100157210ustar00rootroot00000000000000// // Command line utilities for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers // # include "pappl-private.h" # include # include // // Local functions // static void usage(const char *base_name, bool with_autoadd); // // 'papplMainloop()' - Run a standard main loop for printer applications. // // This function runs a standard main loop for a printer application. The // "argc" and "argv" arguments are those provided to the `main` function. // // The "version" argument specifies a numeric version number for the printer // application that conforms to semantic versioning guidelines with up to four // numbers, for example "1.2.3.4". // // The "footer_html" argument specifies HTML text to use in the footer of the // web interface. If `NULL`, the footer is omitted. // // The "num_drivers", "drivers", and "driver_cb" arguments specify a list of // drivers and the driver callback for printers. Specify `0` and `NULL` if // the drivers are configured in the system callback. The "autoadd_cb" // argument specifies a callback for automatically adding new printers with the // "autoadd" sub-command and for auto-detecting the driver when adding manually. // // The "usage_cb" argument specifies a callback that displays a usage/help // summary. If `NULL`, a generic summary is shown as needed. // // The "subcmd_name" and "subcmd_cb" arguments specify the name and a callback // for a custom sub-command. If `NULL`, no custom sub-commands will be // supported. // // The "system_cb" argument specifies a function that will create a new // `pappl_system_t` object. If `NULL`, a default system object is created. // // The "data" argument provides application-specific data for each of the // callbacks. // int // O - Exit status papplMainloop( int argc, // I - Number of command line arguments char *argv[], // I - Command line arguments const char *version, // I - Version number const char *footer_html, // I - Footer HTML or `NULL` for none int num_drivers, // I - Number of drivers pappl_pr_driver_t *drivers, // I - Drivers pappl_pr_autoadd_cb_t autoadd_cb, // I - Auto-add callback or `NULL` for none pappl_pr_driver_cb_t driver_cb, // I - Driver callback const char *subcmd_name, // I - Sub-command name or `NULL` for none pappl_ml_subcmd_cb_t subcmd_cb, // I - Sub-command callback or `NULL` for none pappl_ml_system_cb_t system_cb, // I - System callback or `NULL` for default pappl_ml_usage_cb_t usage_cb, // I - Usage callback or `NULL` for default void *data) // I - Context pointer { const char *base_name; // Base Name int i, j; // Looping vars const char *opt; // Option character const char *subcommand = NULL; // Sub-command int num_files = 0; // File count char *files[1000]; // Files array int num_options = 0; // Number of options cups_option_t *options = NULL; // Options static const char * const subcommands[] = { // List of standard sub-commands "add", "autoadd", "cancel", "default", "delete", "devices", "drivers", "jobs", "modify", "options", "printers", "server", "shutdown", "status", "submit" }; // Range check input... if (argc < 1 || !argv) { fputs("ERROR: No command-line arguments were passed to papplMainloop.\n", stderr); return (1); } if (!version) { fputs("ERROR: No version number string was passed to papplMainloop.\n", stderr); return (1); } // Save the path to the printer application and get the base name. _papplMainloopPath = argv[0]; base_name = basename(argv[0]); // Parse the command-line... for (i = 1; i < argc; i ++) { if (!strcmp(argv[i], "--help")) { if (usage_cb) (*usage_cb)(data); else usage(base_name, autoadd_cb != NULL); return (0); } else if (!strcmp(argv[i], "--version")) { puts(version); return (0); } else if (!strcmp(argv[i], "--")) { // Filename follows i ++; if (i >= argc) { fprintf(stderr, "%s: Missing filename after '--'.\n", base_name); return (1); } else if (num_files >= (int)(sizeof(files) / sizeof(files[0]))) { fprintf(stderr, "%s: Too many files.\n", base_name); return (1); } files[num_files ++] = argv[i]; } else if (!strncmp(argv[i], "--", 2)) { fprintf(stderr, "%s: Unknown option '%s'.\n", base_name, argv[i]); return (1); } else if (argv[i][0] == '-' && argv[i][1]) { for (opt = argv[i] + 1; *opt; opt ++) { switch (argv[i][1]) { case 'a': // -a (cancel-all) num_options = cupsAddOption("cancel-all", "true", num_options, &options); break; case 'd': // -d PRINTER i ++; if (i >= argc) { fprintf(stderr, "%s: Missing printer name after '-d'.\n", base_name); return (1); } num_options = cupsAddOption("printer-name", argv[i], num_options, &options); break; case 'h': // -d HOST i ++; if (i >= argc) { fprintf(stderr, "%s: Missing hostname after '-h'.\n", base_name); return (1); } num_options = cupsAddOption("server-hostname", argv[i], num_options, &options); break; case 'j': // -j JOB-ID i ++; if (i >= argc) { fprintf(stderr, "%s: Missing job ID after '-j'.\n", base_name); return (1); } num_options = cupsAddOption("job-id", argv[i], num_options, &options); break; case 'm': // -m DRIVER-NAME i ++; if (i >= argc) { fprintf(stderr, "%s: Missing driver name after '-m'.\n", base_name); return (1); } num_options = cupsAddOption("smi2699-device-command", argv[i], num_options, &options); break; case 'n': // -n COPIES i ++; if (i >= argc) { fprintf(stderr, "%s: Missing copy count after '-n'.\n", base_name); return (1); } num_options = cupsAddOption("num-copies", argv[i], num_options, &options); break; case 'o': // -o "NAME=VALUE [... NAME=VALUE]" if (opt[1] && strchr(opt, '=')) { fprintf(stderr, "%s: Missing space after '-o'.\n", base_name); return (1); } i ++; if (i >= argc) { fprintf(stderr, "%s: Missing option(s) after '-o'.\n", base_name); return (1); } num_options = cupsParseOptions(argv[i], num_options, &options); break; case 't' : // -t TITLE i ++; if (i >= argc) { fprintf(stderr, "%s: Missing title after '-t'.\n", base_name); return (1); } num_options = cupsAddOption("job-name", argv[i], num_options, &options); break; case 'u': // -u PRINTER-URI i ++; if (i >= argc) { fprintf(stderr, "%s: Missing printer URI after '-u'.\n", base_name); return (1); } num_options = cupsAddOption("printer-uri", argv[i], num_options, &options); break; case 'v': // -v DEVICE-URI i ++; if (i >= argc) { fprintf(stderr, "%s: Missing device URI after '-v'.\n", base_name); return (1); } num_options = cupsAddOption("smi2699-device-uri", argv[i], num_options, &options); break; default: fprintf(stderr, "%s: Unknown option '-%c'.\n", base_name, *opt); return (1); } } } else { // Skip "-" as needed... if (!strcmp(argv[i], "-")) { i ++; if (i >= argc) break; } // See if this is a standard sub-command... for (j = 0; j < (int)(sizeof(subcommands) / sizeof(subcommands[0])); j ++) { if (!strcmp(argv[i], subcommands[j])) { if (subcommand) { fprintf(stderr, "%s: Cannot specify more than one sub-command.\n", base_name); return (1); } subcommand = argv[i]; break; } } if (j >= (int)(sizeof(subcommands) / sizeof(subcommands[0]))) { // Not a standard sub-command... if (subcmd_name && !strcmp(argv[i], subcmd_name)) { // Extension sub-command... if (subcommand) { fprintf(stderr, "%s: Cannot specify more than one sub-command.\n", base_name); return (1); } subcommand = argv[i]; } else if (num_files < (int)(sizeof(files) / sizeof(files[0]))) { // Filename... files[num_files ++] = argv[i]; } else { // Too many files... fprintf(stderr, "%s: Too many files.\n", base_name); return (1); } } } } // Process sub-commands if (!subcommand || !strcmp(subcommand, "submit")) { return (_papplMainloopSubmitJob(base_name, num_options, options, num_files, files)); } else if (subcmd_name && !strcmp(subcommand, subcmd_name)) { return ((subcmd_cb)(base_name, num_options, options, num_files, files, data)); } else if (num_files > 0) { fprintf(stderr, "%s: Sub-command '%s' does not accept files.\n", base_name, subcommand); return (1); } else if (!strcmp(subcommand, "add")) { return (_papplMainloopAddPrinter(base_name, num_options, options)); } else if (!strcmp(subcommand, "autoadd")) { if (autoadd_cb) { return (_papplMainloopAutoAddPrinters(base_name, num_options, options)); } else { fprintf(stderr, "%s: Sub-command 'autoadd' is not supported.\n", base_name); return (1); } } else if (!strcmp(subcommand, "cancel")) { return (_papplMainloopCancelJob(base_name, num_options, options)); } else if (!strcmp(subcommand, "default")) { return (_papplMainloopGetSetDefaultPrinter(base_name, num_options, options)); } else if (!strcmp(subcommand, "delete")) { return (_papplMainloopDeletePrinter(base_name, num_options, options)); } else if (!strcmp(subcommand, "devices")) { return (_papplMainloopShowDevices(base_name, num_options, options)); } else if (!strcmp(subcommand, "drivers")) { return (_papplMainloopShowDrivers(base_name, num_options, options, system_cb, data)); } else if (!strcmp(subcommand, "jobs")) { return (_papplMainloopShowJobs(base_name, num_options, options)); } else if (!strcmp(subcommand, "modify")) { return (_papplMainloopModifyPrinter(base_name, num_options, options)); } else if (!strcmp(subcommand, "options")) { return (_papplMainloopShowOptions(base_name, num_options, options)); } else if (!strcmp(subcommand, "printers")) { return (_papplMainloopShowPrinters(base_name, num_options, options)); } else if (!strcmp(subcommand, "server")) { return (_papplMainloopRunServer(base_name, version, footer_html, num_drivers, drivers, autoadd_cb, driver_cb, num_options, options, system_cb, data)); } else if (!strcmp(subcommand, "shutdown")) { return (_papplMainloopShutdownServer(base_name, num_options, options)); } else if (!strcmp(subcommand, "status")) { return (_papplMainloopShowStatus(base_name, num_options, options)); } else { // This should never happen... fprintf(stderr, "%s: Unknown sub-command '%s'.\n", base_name, subcommand); return (1); } } // // 'usage()' - Show default usage. // static void usage(const char *base_name, // I - Base name of application bool with_autoadd) // I - `true` if autoadd command is supported { printf("Usage: %s SUB-COMMAND [OPTIONS] [FILENAME]\n", base_name); printf(" %s [OPTIONS] [FILENAME]\n", base_name); printf(" %s [OPTIONS] -\n", base_name); puts(""); puts("Sub-commands:"); puts(" add PRINTER Add a printer."); if (with_autoadd) puts(" autoadd Automatically add supported printers."); puts(" cancel Cancel one or more jobs."); puts(" default Set the default printer."); puts(" delete Delete a printer."); puts(" devices List devices."); puts(" drivers List drivers."); puts(" jobs List jobs."); puts(" modify Modify a printer."); puts(" options List printer options."); puts(" printers List printers."); puts(" server Run a server."); puts(" shutdown Shutdown a running server."); puts(" status Show server/printer/job status."); puts(" submit Submit a file for printing."); puts(""); puts("Options:"); puts(" -a Cancel all jobs (cancel)."); puts(" -d PRINTER Specify printer."); puts(" -j JOB-ID Specify job ID (cancel)."); puts(" -m DRIVER-NAME Specify driver (add/modify)."); puts(" -n COPIES Specify number of copies (submit)."); puts(" -o NAME=VALUE Specify option (add,modify,server,submit)."); puts(" -u URI Specify ipp: or ipps: printer/server."); puts(" -v DEVICE-URI Specify socket: or usb: device (add/modify)."); } pappl-1.0.3/pappl/mainloop.h000066400000000000000000000024271403603036100157330ustar00rootroot00000000000000// // Mainloop header file for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_MAINLOOP_H_ # define _PAPPL_MAINLOOP_H_ // // Include necessary headers // # include "base.h" # include "system.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Types... // typedef int (*pappl_ml_subcmd_cb_t)(const char *base_name, int num_options, cups_option_t *options, int num_files, char **files, void *data); // Sub-command callback typedef pappl_system_t *(*pappl_ml_system_cb_t)(int num_options, cups_option_t *options, void *data); // System callback typedef void (*pappl_ml_usage_cb_t)(void *data); // Program usage callback // // Functions... // extern int papplMainloop(int argc, char *argv[], const char *version, const char *footer_html, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_driver_cb_t driver_cb, const char *subcmd_name, pappl_ml_subcmd_cb_t subcmd_cb, pappl_ml_system_cb_t system_cb, pappl_ml_usage_cb_t usage_cb, void *data) _PAPPL_PUBLIC; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_MAINLOOP_H_ pappl-1.0.3/pappl/makeresheader.sh000077500000000000000000000014371403603036100171030ustar00rootroot00000000000000#!/bin/sh # # Resource generating script for the Printer Application Framework # # Copyright © 2019-2021 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # # Usage: # # pappl-makeresheader filename [ ... filename ] >filename.h # for file in "$@"; do varname=$(echo "$file" | sed -e '1,$s/[ -.]/_/g') echo "/* $file */" case $file in *.icc | *.jpg | *.otf | *.otc | *.png | *.ttc | *.ttf | *.woff | *.woff2) echo "static unsigned char $varname[] = {" od -t u1 -A n -v "$file" | awk '{for (i = 1; i <= NF; i ++) printf("%s,", $i); print "";}' echo "};" ;; *) echo "static const char * const $varname =" sed -e '1,$s/\\/\\\\/g' -e '1,$s/"/\\"/g' "$file" | awk '{print "\"" $0 "\\n\""}' echo ";" ;; esac done pappl-1.0.3/pappl/pappl-private.h000066400000000000000000000011531403603036100166740ustar00rootroot00000000000000// // Private header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_PAPPL_PRIVATE_H_ # define _PAPPL_PAPPL_PRIVATE_H_ // // Include necessary headers... // # include "device.h" # include "dnssd-private.h" # include "system-private.h" # include "client-private.h" # include "printer-private.h" # include "job-private.h" # include "mainloop-private.h" # include "log-private.h" #endif // !_PAPPL_PAPPL_PRIVATE_H_ pappl-1.0.3/pappl/pappl.h000066400000000000000000000007351403603036100152310ustar00rootroot00000000000000// // Public header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_PAPPL_H_ # define _PAPPL_PAPPL_H_ // // Include necessary headers... // # include "device.h" # include "system.h" # include "client.h" # include "printer.h" # include "job.h" # include "log.h" # include "mainloop.h" #endif // !_PAPPL_PAPPL_H_ pappl-1.0.3/pappl/pappl.pc.in000066400000000000000000000005631403603036100160100ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: PAPPL Description: Printer Application Framework/Library Version: @PAPPL_VERSION@ URL: https://www.msweet.org/pappl Requires: @PKGCONFIG_GNUTLS@ @PKGCONFIG_LIBJPEG@ @PKGCONFIG_LIBPNG@ @PKGCONFIG_LIBUSB@ zlib >= 1.0 Libs: -L${libdir} -lpappl -lcups -lpam -lm Cflags: -I${includedir} pappl-1.0.3/pappl/printer-accessors.c000066400000000000000000000735321403603036100175630ustar00rootroot00000000000000// // Printer accessor functions for the Printer Application Framework // // Copyright © 2020-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "printer-private.h" #include "system-private.h" // // 'papplPrinterCloseDevice()' - Close the device associated with the printer. // // This function closes the device for a printer. The device must have been // previously opened using the @link papplPrinterOpenDevice@ function. // void papplPrinterCloseDevice( pappl_printer_t *printer) // I - Printer { if (!printer || !printer->device || !printer->device_in_use || printer->processing_job) return; pthread_rwlock_wrlock(&printer->rwlock); papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Closing device."); papplDeviceClose(printer->device); printer->device = NULL; printer->device_in_use = false; papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Device closed."); pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterGetContact()' - Get the "printer-contact" value. // // This function copies the current printer contact information to the buffer // pointed to by the "contact" argument. // pappl_contact_t * // O - Contact papplPrinterGetContact( pappl_printer_t *printer, // I - Printer pappl_contact_t *contact) // O - Contact { if (!printer || !contact) { if (contact) memset(contact, 0, sizeof(pappl_contact_t)); return (contact); } pthread_rwlock_rdlock(&printer->rwlock); *contact = printer->contact; pthread_rwlock_unlock(&printer->rwlock); return (contact); } // // 'papplPrinterGetDeviceID()' - Get the IEEE-1284 device ID of the printer. // // This function returns the IEEE-1284 device ID of the printer. // const char * // O - IEEE-1284 device ID string papplPrinterGetDeviceID( pappl_printer_t *printer) // I - Printer { return (printer ? printer->device_id : NULL); } // // 'papplPrinterGetDeviceURI()' - Get the URI of the device associated with the // printer. // // This function returns the device URI for the printer. // const char * // O - Device URI string papplPrinterGetDeviceURI( pappl_printer_t *printer) // I - Printer { return (printer ? printer->device_uri : "file:///dev/null"); } // // 'papplPrinterGetDNSSDName()' - Get the current DNS-SD service name. // // This function copies the current DNS-SD service name to the buffer pointed // to by the "buffer" argument. // char * // O - DNS-SD service name or `NULL` for none papplPrinterGetDNSSDName( pappl_printer_t *printer, // I - Printer char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (!printer || !printer->dns_sd_name || !buffer || bufsize == 0) { if (buffer) *buffer = '\0'; return (NULL); } pthread_rwlock_rdlock(&printer->rwlock); strlcpy(buffer, printer->dns_sd_name, bufsize); pthread_rwlock_unlock(&printer->rwlock); return (buffer); } // // 'papplPrinterGetGeoLocation()' - Get the current geo-location as a "geo:" // URI. // // This function copies the currently configured geographic location as a "geo:" // URI to the buffer pointed to by the "buffer" argument. // char * // O - "geo:" URI or `NULL` for unknown papplPrinterGetGeoLocation( pappl_printer_t *printer, // I - Printer char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (!printer || !printer->geo_location || !buffer || bufsize == 0) { if (buffer) *buffer = '\0'; return (NULL); } pthread_rwlock_rdlock(&printer->rwlock); strlcpy(buffer, printer->geo_location, bufsize); pthread_rwlock_unlock(&printer->rwlock); return (buffer); } // // 'papplPrinterGetID()' - Get the printer ID. // // This function returns the printer's unique positive integer identifier. // int // O - "printer-id" value or `0` for none papplPrinterGetID( pappl_printer_t *printer) // I - Printer { return (printer ? printer->printer_id : 0); } // // 'papplPrinterGetImpressionsCompleted()' - Get the number of impressions // (sides) that have been printed. // // This function returns the number of impressions that have been printed. An // impression is one side of an output page. // int // O - Number of printed impressions/sides papplPrinterGetImpressionsCompleted( pappl_printer_t *printer) // I - Printer { return (printer ? printer->impcompleted : 0); } // // 'papplPrinterGetLocation()' - Get the location string. // // This function copies the printer's human-readable location to the buffer // pointed to by the "buffer" argument. // char * // O - Location or `NULL` for none papplPrinterGetLocation( pappl_printer_t *printer, // I - Printer char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (!printer || !printer->location || !buffer || bufsize == 0) { if (buffer) *buffer = '\0'; return (NULL); } pthread_rwlock_rdlock(&printer->rwlock); strlcpy(buffer, printer->location, bufsize); pthread_rwlock_unlock(&printer->rwlock); return (buffer); } // // 'papplPrinterGetMaxActiveJobs()' - Get the maximum number of active (queued) // jobs allowed by the printer. // // This function returns the maximum number of active jobs that the printer // supports, as configured by the @link papplPrinterSetMaxActiveJobs@ function. // int // O - Maximum number of active jobs papplPrinterGetMaxActiveJobs( pappl_printer_t *printer) // I - Printer { return (printer ? printer->max_active_jobs : 0); } // // 'papplPrinterGetMaxCompletedJobs()' - Get the maximum number of jobs retained // for history by the printer. // // This function returns the maximum number of jobs that are retained in the // job history as configured by the @link papplPrinterSetMaxCompletedJobs@ // function. // int // O - Maximum number of completed jobs papplPrinterGetMaxCompletedJobs( pappl_printer_t *printer) // I - Printer { return (printer ? printer->max_completed_jobs : 0); } // // 'papplPrinterGetName()' - Get the printer name. // // This function returns the printer's human-readable name. // const char * // O - Printer name papplPrinterGetName( pappl_printer_t *printer) // I - Printer { return (printer ? printer->name : NULL); } // // 'papplPrinterGetNextJobID()' - Get the next job ID. // // This function returns the positive integer identifier that will be used for // the next job that is created. // int // O - Next job ID or `0` for none papplPrinterGetNextJobID( pappl_printer_t *printer) // I - Printer { return (printer ? printer->next_job_id : 0); } // // 'papplPrinterGetNumberOfActiveJobs()' - Get the number of active print jobs. // // This function returns the number of print jobs that are either printing or // waiting to be printed. // int // O - Number of active print jobs papplPrinterGetNumberOfActiveJobs( pappl_printer_t *printer) // I - Printer { return (printer ? cupsArrayCount(printer->active_jobs) : 0); } // // 'papplPrinterGetNumberOfCompletedJobs()' - Get the number of completed print // jobs. // // This function returns the number of print jobs that have been aborted, // canceled, or completed. // int // O - Number of completed print jobs papplPrinterGetNumberOfCompletedJobs( pappl_printer_t *printer) // I - Printer { return (printer ? cupsArrayCount(printer->completed_jobs) : 0); } // // 'papplPrinterGetNumberOfJobs()' - Get the total number of print jobs. // // This function returns the number of print jobs that are printing, waiting // to be printed, have been aborted, have been canceled, or have completed. // int // O - Total number of print jobs papplPrinterGetNumberOfJobs( pappl_printer_t *printer) // I - Printer { return (printer ? cupsArrayCount(printer->all_jobs) : 0); } // // 'papplPrinterGetOrganization()' - Get the organization name. // // This function copies the printer's organization name to the buffer pointed // to by the "buffer" argument. // char * // O - Organization name or `NULL` for none papplPrinterGetOrganization( pappl_printer_t *printer, // I - Printer char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (!printer || !printer->organization || !buffer || bufsize == 0) { if (buffer) *buffer = '\0'; return (NULL); } pthread_rwlock_rdlock(&printer->rwlock); strlcpy(buffer, printer->organization, bufsize); pthread_rwlock_unlock(&printer->rwlock); return (buffer); } // // 'papplPrinterGetOrganizationalUnit()' - Get the organizational unit name. // // This function copies the printer's organizational unit name to the buffer // pointed to by the "buffer" argument. // char * // O - Organizational unit name or `NULL` for none papplPrinterGetOrganizationalUnit( pappl_printer_t *printer, // I - Printer char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (!printer || !printer->org_unit || !buffer || bufsize == 0) { if (buffer) *buffer = '\0'; return (NULL); } pthread_rwlock_rdlock(&printer->rwlock); strlcpy(buffer, printer->org_unit, bufsize); pthread_rwlock_unlock(&printer->rwlock); return (buffer); } // // 'papplPrinterGetPath()' - Get the URL path for a printer web page. // // This function generates and returns the URL path for the printer's web page. // The "subpath" argument specifies an optional sub-path for a specific printer // web page. // char * // O - URI path or `NULL` on error papplPrinterGetPath( pappl_printer_t *printer, // I - Printer const char *subpath, // I - Sub-path or `NULL` for none char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (!printer || !buffer || bufsize < 32) { if (buffer) *buffer = '\0'; return (NULL); } if (subpath) snprintf(buffer, bufsize, "%s/%s", printer->uriname, subpath); else strlcpy(buffer, printer->uriname, bufsize); return (buffer); } // // 'papplPrinterGetPrintGroup()' - Get the print authorization group, if any. // // This function copies the printer's authorization group name to the buffer // pointed to by the "buffer" argument. // char * // O - Print authorization group name or `NULL` for none papplPrinterGetPrintGroup( pappl_printer_t *printer, // I - Printer char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (!printer || !printer->print_group || !buffer || bufsize == 0) { if (buffer) *buffer = '\0'; return (NULL); } pthread_rwlock_rdlock(&printer->rwlock); strlcpy(buffer, printer->print_group, bufsize); pthread_rwlock_unlock(&printer->rwlock); return (buffer); } // // 'papplPrinterGetReasons()' - Get the current "printer-state-reasons" bit values. // // This function returns the current printer state reasons bitfield, which can // be updated by the printer driver and/or by the @link papplPrinterSetReasons@ // function. // pappl_preason_t // O - "printer-state-reasons" bit values papplPrinterGetReasons( pappl_printer_t *printer) // I - Printer { if (!printer) return (PAPPL_PREASON_NONE); if (!printer->device_in_use && !printer->processing_job && (time(NULL) - printer->status_time) > 1 && printer->driver_data.status_cb) { // Update printer status... (printer->driver_data.status_cb)(printer); printer->status_time = time(NULL); } return (printer->state_reasons); } // // 'papplPrinterGetState()' - Get the current "printer-state" value. // // This function returns the current printer state as an enumeration: // // - `IPP_PSTATE_IDLE`: The printer is idle and has no jobs to process. // - `IPP_PSTATE_PROCESSING`: The printer is processing a job and/or producing // output. // - `IPP_PSTATE_STOPPED`: The printer is stopped for maintenance. // ipp_pstate_t // O - "printer-state" value papplPrinterGetState( pappl_printer_t *printer) // I - Printer { return (printer ? printer->state : IPP_PSTATE_STOPPED); } // // 'papplPrinterGetSupplies()' - Get the current "printer-supplies" values. // // This function copies the current printer supply values to the specified // array. The "max_supplies" and "supplies" arguments can be `0` and `NULL` // to query the number of supplies used. // // The return value is the actual number of supplies used by the printer, // regardless of the size of the array. // int // O - Number of values papplPrinterGetSupplies( pappl_printer_t *printer, // I - Printer int max_supplies, // I - Maximum number of supplies pappl_supply_t *supplies) // I - Array for supplies { int count; // Number of supplies if (!printer || max_supplies < 0 || (max_supplies > 0 && !supplies)) return (0); if (max_supplies == 0) return (printer->num_supply); memset(supplies, 0, (size_t)max_supplies * sizeof(pappl_supply_t)); pthread_rwlock_rdlock(&printer->rwlock); if ((count = printer->num_supply) > max_supplies) count = max_supplies; memcpy(supplies, printer->supply, (size_t)count * sizeof(pappl_supply_t)); pthread_rwlock_unlock(&printer->rwlock); return (count); } // // 'papplPrinterGetSystem()' - Get the system associated with the printer. // // This function returns a pointer to the system object that contains the // printer. // pappl_system_t * // O - System papplPrinterGetSystem( pappl_printer_t *printer) // I - Printer { return (printer ? printer->system : NULL); } // // 'papplPrinterIterateActiveJobs()' - Iterate over the active jobs. // // This function iterates over jobs that are either printing or waiting to be // printed. The specified callback "cb" will be called once per job with the // data pointer "data". // // The "job_index" argument specifies the first job in the list to iterate, // where `1` is the first job, etc. The "limit" argument specifies the maximum // number of jobs to iterate - use `0` to iterate an unlimited number of jobs. // void papplPrinterIterateActiveJobs( pappl_printer_t *printer, // I - Printer pappl_job_cb_t cb, // I - Callback function void *data, // I - Callback data int job_index, // I - First job to iterate (1-based) int limit) // I - Maximum jobs to iterate or `0` for no limit { pappl_job_t *job; // Current job int j, // Looping var jcount, // Number of jobs count; // Number of jobs iterated if (!printer || !cb) return; pthread_rwlock_rdlock(&printer->rwlock); // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the active_jobs array. if (limit <= 0) limit = INT_MAX; for (count = 0, j = job_index - 1, jcount = cupsArrayCount(printer->active_jobs); j < jcount && count < limit; j ++, count ++) { job = (pappl_job_t *)cupsArrayIndex(printer->active_jobs, j); (cb)(job, data); } pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterIterateAllJobs()' - Iterate over all the jobs. // // This function iterates over all jobs. The specified callback "cb" will be // called once per job with the data pointer "data". // // The "job_index" argument specifies the first job in the list to iterate, // where `1` is the first job, etc. The "limit" argument specifies the maximum // number of jobs to iterate - use `0` to iterate an unlimited number of jobs. // void papplPrinterIterateAllJobs( pappl_printer_t *printer, // I - Printer pappl_job_cb_t cb, // I - Callback function void *data, // I - Callback data int job_index, // I - First job to iterate (1-based) int limit) // I - Maximum jobs to iterate, `0` for no limit { pappl_job_t *job; // Current job int j, // Looping var jcount, // Number of jobs count; // Number of jobs iterated if (!printer || !cb) return; pthread_rwlock_rdlock(&printer->rwlock); // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the all_jobs array. if (limit <= 0) limit = INT_MAX; for (count = 0, j = job_index - 1, jcount = cupsArrayCount(printer->all_jobs); j < jcount && count < limit; j ++, count ++) { job = (pappl_job_t *)cupsArrayIndex(printer->all_jobs, j); (cb)(job, data); } pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterIterateCompletedJobs()' - Iterate over the completed jobs. // // This function iterates over jobs that are aborted, canceled, or completed. // The specified callback "cb" will be called once per job with the data pointer // "data". // // The "job_index" argument specifies the first job in the list to iterate, // where `1` is the first job, etc. The "limit" argument specifies the maximum // number of jobs to iterate - use `0` to iterate an unlimited number of jobs. // void papplPrinterIterateCompletedJobs( pappl_printer_t *printer, // I - Printer pappl_job_cb_t cb, // I - Callback function void *data, // I - Callback data int job_index, // I - First job to iterate (1-based) int limit) // I - Maximum jobs to iterate, `0` for no limit { pappl_job_t *job; // Current job int j, // Looping var jcount, // Number of jobs count; // Number of jobs iterated if (!printer || !cb) return; pthread_rwlock_rdlock(&printer->rwlock); // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the completed_jobs array. if (limit <= 0) limit = INT_MAX; for (count = 0, j = job_index - 1, jcount = cupsArrayCount(printer->completed_jobs); j < jcount && count < limit; j ++, count ++) { job = (pappl_job_t *)cupsArrayIndex(printer->completed_jobs, j); (cb)(job, data); } pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterOpenDevice()' - Open the device associated with a printer. // // This function opens the printer's device. `NULL` is returned if the device // is already in use, for example while a job is being printed. // // The returned device must be closed using the @link papplPrinterCloseDevice@ // function. // pappl_device_t * // O - Device or `NULL` if not possible papplPrinterOpenDevice( pappl_printer_t *printer) // I - Printer { pappl_device_t *device = NULL; // Open device if (!printer || printer->device_in_use || printer->processing_job || !printer->device_uri) return (NULL); pthread_rwlock_wrlock(&printer->rwlock); if (!printer->device_in_use && !printer->processing_job) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Opening device."); printer->device = device = papplDeviceOpen(printer->device_uri, "printer", papplLogDevice, printer->system); printer->device_in_use = device != NULL; } if (device) papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Device open."); else papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Device not open."); pthread_rwlock_unlock(&printer->rwlock); return (device); } // // 'papplPrinterPause()' - Pause (stop) a printer. // // This function pauses a printer. If the printer is currently processing // (printing) a job, it will be completed before the printer is stopped. // void papplPrinterPause( pappl_printer_t *printer) // I - Printer { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); if (printer->processing_job) printer->is_stopped = true; else printer->state = IPP_PSTATE_STOPPED; pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterResume()' - Resume (start) a printer. // // This function resumes a printer and starts processing any pending jobs. // void papplPrinterResume( pappl_printer_t *printer) // I - Printer { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); printer->is_stopped = false; printer->state = IPP_PSTATE_IDLE; pthread_rwlock_unlock(&printer->rwlock); _papplPrinterCheckJobs(printer); } // // 'papplPrinterSetContact()' - Set the "printer-contact" value. // // This function sets the printer's contact information. // void papplPrinterSetContact( pappl_printer_t *printer, // I - Printer pappl_contact_t *contact) // I - Contact { if (!printer || !contact) return; pthread_rwlock_wrlock(&printer->rwlock); printer->contact = *contact; printer->config_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetDNSSDName()' - Set the DNS-SD service name. // // This function sets the printer's DNS-SD service name. If `NULL`, the printer // will stop advertising the printer. // void papplPrinterSetDNSSDName( pappl_printer_t *printer, // I - Printer const char *value) // I - DNS-SD service name or `NULL` for none { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); free(printer->dns_sd_name); printer->dns_sd_name = value ? strdup(value) : NULL; printer->dns_sd_collision = false; printer->dns_sd_serial = 0; printer->config_time = time(NULL); if (!value) _papplPrinterUnregisterDNSSDNoLock(printer); else _papplPrinterRegisterDNSSDNoLock(printer); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetGeoLocation()' - Set the geo-location value as a "geo:" URI. // // This function sets the printer's geographic location as a "geo:" URI. If // `NULL`, the location is cleared to the 'unknown' value. // void papplPrinterSetGeoLocation( pappl_printer_t *printer, // I - Printer const char *value) // I - "geo:" URI or `NULL` for unknown { float lat, lon; // Latitude and longitude from geo: URI if (!printer) return; // Validate geo-location - must be NULL or a "geo:" URI... if (value && sscanf(value, "geo:%f,%f", &lat, &lon) != 2) return; pthread_rwlock_wrlock(&printer->rwlock); free(printer->geo_location); printer->geo_location = value ? strdup(value) : NULL; printer->config_time = time(NULL); _papplPrinterRegisterDNSSDNoLock(printer); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetImpressionsCompleted()' - Add impressions (sides) to the // total count of printed impressions. // // This function adds to the printer's impressions counter. An impression is // one side of an output page. // void papplPrinterSetImpressionsCompleted( pappl_printer_t *printer, // I - Printer int add) // I - Number of impressions/sides to add { if (!printer || add <= 0) return; pthread_rwlock_wrlock(&printer->rwlock); printer->impcompleted += add; printer->state_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetLocation()' - Set the location string. // // This function sets the printer's human-readable location string. If `NULL`, // the location is cleared. // void papplPrinterSetLocation( pappl_printer_t *printer, // I - Printer const char *value) // I - Location ("Bob's Office", etc.) or `NULL` for none { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); free(printer->location); printer->location = value ? strdup(value) : NULL; printer->config_time = time(NULL); _papplPrinterRegisterDNSSDNoLock(printer); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetMaxActiveJobs()' - Set the maximum number of active jobs for // the printer. // // This function sets the maximum number of jobs that can be spooled on the // printer at one time. // // > Note: This limit does not apply to streaming raster formats such as PWG // > Raster since they are not spooled. // void papplPrinterSetMaxActiveJobs( pappl_printer_t *printer, // I - Printer int max_active_jobs) // I - Maximum number of active jobs, `0` for unlimited { if (!printer || max_active_jobs < 0) return; pthread_rwlock_wrlock(&printer->rwlock); printer->max_active_jobs = max_active_jobs; printer->config_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetMaxCompletedJobs()' - Set the maximum number of completed // jobs for the printer. // // This function sets the maximum number of aborted, canceled, or completed jobs // that are retained in the job history. // void papplPrinterSetMaxCompletedJobs( pappl_printer_t *printer, // I - Printer int max_completed_jobs) // I - Maximum number of completed jobs, `0` for unlimited { if (!printer || max_completed_jobs < 0) return; pthread_rwlock_wrlock(&printer->rwlock); printer->max_completed_jobs = max_completed_jobs; printer->config_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetNextJobID()' - Set the next "job-id" value. // // This function sets the next unique positive integer identifier that will be // used for a job. // // > Note: This function is normally only called once to restore the previous // > state of the printer. // void papplPrinterSetNextJobID( pappl_printer_t *printer, // I - Printer int next_job_id) // I - Next "job-id" value { if (!printer || next_job_id < 1) return; pthread_rwlock_wrlock(&printer->rwlock); printer->next_job_id = next_job_id; printer->config_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetOrganization()' - Set the organization name. // // This function sets the printer's organization name. If `NULL` the value is // cleared. // void papplPrinterSetOrganization( pappl_printer_t *printer, // I - Printer const char *value) // I - Organization name or `NULL` for none { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); free(printer->organization); printer->organization = value ? strdup(value) : NULL; printer->config_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetOrganizationalUnit()' - Set the organizational unit name. // // This function sets the printer's organizational unit name. If `NULL` the // value is cleared. // void papplPrinterSetOrganizationalUnit( pappl_printer_t *printer, // I - Printer const char *value) // I - Organizational unit name or `NULL` for none { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); free(printer->org_unit); printer->org_unit = value ? strdup(value) : NULL; printer->config_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetPrintGroup()' - Set the print authorization group, if any. // // This function sets the printer's authorization group. If `NULL`, the group // is cleared. // // > Note: The authorization group is only used if the system is created with a // > named authorization service. // void papplPrinterSetPrintGroup( pappl_printer_t *printer, // I - Printer const char *value) // I - Print authorization group or `NULL` for none { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); free(printer->print_group); printer->print_group = value ? strdup(value) : NULL; printer->config_time = time(NULL); if (printer->print_group && strcmp(printer->print_group, "none")) { char buffer[8192]; // Buffer for strings struct group grpbuf, // Group buffer *grp = NULL; // Print group if (getgrnam_r(printer->print_group, &grpbuf, buffer, sizeof(buffer), &grp) || !grp) papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to find print group '%s'.", printer->print_group); else printer->print_gid = grp->gr_gid; } else printer->print_gid = (gid_t)-1; pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); } // // 'papplPrinterSetReasons()' - Add or remove values from // "printer-state-reasons". // // This function updates the printer state reasons bitfield by clearing any bit // values in the "remove" argument and setting any bit values in the "add" // argument. // void papplPrinterSetReasons( pappl_printer_t *printer, // I - Printer pappl_preason_t add, // I - "printer-state-reasons" bit values to add or `PAPPL_PREASON_NONE` for none pappl_preason_t remove) // I - "printer-state-reasons" bit values to remove or `PAPPL_PREASON_NONE` for none { if (!printer) return; pthread_rwlock_wrlock(&printer->rwlock); printer->state_reasons &= ~remove; printer->state_reasons |= add; printer->state_time = printer->status_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); } // // 'papplPrinterSetSupplies()' - Set/update the supplies for a printer. // // This function updates the supply information for the printer. // void papplPrinterSetSupplies( pappl_printer_t *printer, // I - Printer int num_supplies, // I - Number of supplies pappl_supply_t *supplies) // I - Array of supplies { if (!printer || num_supplies < 0 || num_supplies > PAPPL_MAX_SUPPLY || (num_supplies > 0 && !supplies)) return; pthread_rwlock_wrlock(&printer->rwlock); printer->num_supply = num_supplies; memset(printer->supply, 0, sizeof(printer->supply)); if (supplies) memcpy(printer->supply, supplies, (size_t)num_supplies * sizeof(pappl_supply_t)); printer->state_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); } pappl-1.0.3/pappl/printer-driver.c000066400000000000000000001454301403603036100170660ustar00rootroot00000000000000// // Printer driver functions for the Printer Application Framework // // Copyright © 2020-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "printer-private.h" #include "system-private.h" // // Local functions... // static ipp_t *make_attrs(pappl_system_t *system, pappl_pr_driver_data_t *data); static bool validate_defaults(pappl_printer_t *printer, pappl_pr_driver_data_t *driver_data, pappl_pr_driver_data_t *data); static bool validate_driver(pappl_printer_t *printer, pappl_pr_driver_data_t *data); static bool validate_ready(pappl_printer_t *printer, pappl_pr_driver_data_t *driver_data, int num_ready, pappl_media_col_t *ready); // // 'papplPrinterGetDriverAttributes()' - Get a copy of the current driver // attributes. // // This function returns a copy the current driver attributes. Use the // `ippDelete` function to free the memory used for the attributes when you // are done. // ipp_t * // O - Copy of driver attributes papplPrinterGetDriverAttributes( pappl_printer_t *printer) // I - Printer { ipp_t *attrs; // Copy of driver attributes if (!printer) return (NULL); pthread_rwlock_rdlock(&printer->rwlock); attrs = ippNew(); ippCopyAttributes(attrs, printer->driver_attrs, 1, NULL, NULL); pthread_rwlock_unlock(&printer->rwlock); return (attrs); } // // 'papplPrinterGetDriverData()' - Get the current print driver data. // // This function copies the current print driver data, defaults, and ready // (loaded) media information into the specified buffer. // pappl_pr_driver_data_t * // O - Driver data or `NULL` if none papplPrinterGetDriverData( pappl_printer_t *printer, // I - Printer pappl_pr_driver_data_t *data) // I - Pointer to driver data structure to fill { if (!printer || !printer->driver_name || !data) { if (data) _papplPrinterInitDriverData(data); return (NULL); } memcpy(data, &printer->driver_data, sizeof(pappl_pr_driver_data_t)); return (data); } // // 'papplPrinterGetDriverName()' - Get the driver name for a printer. // // This function returns the driver name for the printer. // const char * // O - Driver name or `NULL` for none papplPrinterGetDriverName( pappl_printer_t *printer) // I - Printer { return (printer ? printer->driver_name : NULL); } // // '_papplPrinterInitDriverData()' - Initialize a print driver data structure. // void _papplPrinterInitDriverData( pappl_pr_driver_data_t *d) // I - Driver data { static const pappl_dither_t clustered = { // Clustered-Dot Dither Matrix { 96, 40, 48, 104, 140, 188, 196, 148, 97, 41, 49, 105, 141, 189, 197, 149 }, { 32, 0, 8, 56, 180, 236, 244, 204, 33, 1, 9, 57, 181, 237, 245, 205 }, { 88, 24, 16, 64, 172, 228, 252, 212, 89, 25, 17, 65, 173, 229, 253, 213 }, { 120, 80, 72, 112, 132, 164, 220, 156, 121, 81, 73, 113, 133, 165, 221, 157 }, { 136, 184, 192, 144, 100, 44, 52, 108, 137, 185, 193, 145, 101, 45, 53, 109 }, { 176, 232, 240, 200, 36, 4, 12, 60, 177, 233, 241, 201, 37, 5, 13, 61 }, { 168, 224, 248, 208, 92, 28, 20, 68, 169, 225, 249, 209, 93, 29, 21, 69 }, { 128, 160, 216, 152, 124, 84, 76, 116, 129, 161, 217, 153, 125, 85, 77, 117 }, { 98, 42, 50, 106, 142, 190, 198, 150, 99, 43, 51, 107, 143, 191, 199, 151 }, { 34, 2, 10, 58, 182, 238, 246, 206, 35, 3, 11, 59, 183, 239, 247, 207 }, { 90, 26, 18, 66, 174, 230, 254, 214, 91, 27, 19, 67, 175, 231, 254, 215 }, { 122, 82, 74, 114, 134, 166, 222, 158, 123, 83, 75, 115, 135, 167, 223, 159 }, { 138, 186, 194, 146, 102, 46, 54, 110, 139, 187, 195, 147, 103, 47, 55, 111 }, { 178, 234, 242, 202, 38, 6, 14, 62, 179, 235, 243, 203, 39, 7, 15, 63 }, { 170, 226, 250, 210, 94, 30, 22, 70, 171, 227, 251, 211, 95, 31, 23, 71 }, { 130, 162, 218, 154, 126, 86, 78, 118, 131, 163, 219, 155, 127, 87, 79, 119 } }; memset(d, 0, sizeof(pappl_pr_driver_data_t)); memcpy(d->gdither, clustered, sizeof(d->gdither)); memcpy(d->pdither, clustered, sizeof(d->pdither)); d->orient_default = IPP_ORIENT_NONE; d->content_default = PAPPL_CONTENT_AUTO; d->darkness_configured = 50; d->quality_default = IPP_QUALITY_NORMAL; d->scaling_default = PAPPL_SCALING_AUTO; d->sides_supported = PAPPL_SIDES_ONE_SIDED; d->sides_default = PAPPL_SIDES_ONE_SIDED; } // // 'papplPrinterSetDriverData()' - Set the driver data. // // This function validates and sets the driver data, including all defaults and // ready (loaded) media. // // > Note: This function regenerates all of the driver-specific capability // > attributes like "media-col-database", "sides-supported", and so forth. // > Use the @link papplPrinterSetDriverDefaults@ or // > @link papplPrinterSetReadyMedia@ functions to efficiently change the // > "xxx-default" or "xxx-ready" values, respectively. // bool // O - `true` on success, `false` on failure papplPrinterSetDriverData( pappl_printer_t *printer, // I - Printer pappl_pr_driver_data_t *data, // I - Driver data ipp_t *attrs) // I - Additional capability attributes or `NULL` for none { if (!printer || !data) return (false); // Validate data... if (!validate_defaults(printer, data, data) || !validate_driver(printer, data) || !validate_ready(printer, data, data->num_source, data->media_ready)) return (false); pthread_rwlock_wrlock(&printer->rwlock); // Copy driver data to printer memcpy(&printer->driver_data, data, sizeof(printer->driver_data)); // Create printer (capability) attributes based on driver data... ippDelete(printer->driver_attrs); printer->driver_attrs = make_attrs(printer->system, &printer->driver_data); if (attrs) ippCopyAttributes(printer->driver_attrs, attrs, 0, NULL, NULL); pthread_rwlock_unlock(&printer->rwlock); return (true); } // // 'papplPrinterSetDriverDefaults()' - Set the default print option values. // // This function validates and sets the printer's default print options. // // > Note: Unlike @link papplPrinterSetPrintDriverData@, this function only // > changes the "xxx_default" members of the driver data and is considered // > lightweight. // bool // O - `true` on success or `false` on failure papplPrinterSetDriverDefaults( pappl_printer_t *printer, // I - Printer pappl_pr_driver_data_t *data, // I - Driver data int num_vendor, // I - Number of vendor options cups_option_t *vendor) // I - Vendor options { int i; // Looping var const char *value; // Vendor value int intvalue; // Integer value char *end, // End of value defname[128], // xxx-default name supname[128]; // xxx-supported name ipp_attribute_t *supported; // xxx-supported attribute if (!printer || !data) return (false); if (!validate_defaults(printer, &printer->driver_data, data)) return (false); pthread_rwlock_wrlock(&printer->rwlock); // Copy xxx_default values... printer->driver_data.color_default = data->color_default; printer->driver_data.content_default = data->content_default; printer->driver_data.quality_default = data->quality_default; printer->driver_data.scaling_default = data->scaling_default; printer->driver_data.sides_default = data->sides_default; printer->driver_data.x_default = data->x_default; printer->driver_data.y_default = data->y_default; printer->driver_data.media_default = data->media_default; printer->driver_data.speed_default = data->speed_default; printer->driver_data.darkness_default = data->darkness_default; printer->driver_data.bin_default = data->bin_default; printer->driver_data.mode_configured = data->mode_configured; printer->driver_data.tear_offset_configured = data->tear_offset_configured; printer->driver_data.darkness_configured = data->darkness_configured; printer->driver_data.identify_default = data->identify_default; // Copy any vendor-specific xxx-default values... for (i = 0; i < data->num_vendor; i ++) { if ((value = cupsGetOption(data->vendor[i], num_vendor, vendor)) == NULL) continue; snprintf(defname, sizeof(defname), "%s-default", data->vendor[i]); snprintf(supname, sizeof(supname), "%s-supported", data->vendor[i]); ippDeleteAttribute(printer->driver_attrs, ippFindAttribute(printer->driver_attrs, defname, IPP_TAG_ZERO)); if ((supported = ippFindAttribute(printer->driver_attrs, supname, IPP_TAG_ZERO)) != NULL) { switch (ippGetValueTag(supported)) { case IPP_TAG_INTEGER : case IPP_TAG_RANGE : intvalue = (int)strtol(value, &end, 10); if (errno != ERANGE && !*end) ippAddInteger(printer->driver_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, defname, intvalue); break; case IPP_TAG_BOOLEAN : ippAddBoolean(printer->driver_attrs, IPP_TAG_PRINTER, defname, !strcmp(value, "true") || !strcmp(value, "on")); break; case IPP_TAG_KEYWORD : ippAddString(printer->driver_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, defname, NULL, value); break; default : papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver '%s' attribute syntax not supported, only boolean, integer, keyword, and rangeOfInteger are supported.", supname); break; } } else { // Default to simple text values... ippAddString(printer->driver_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, defname, NULL, value); } } printer->config_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); return (true); } // // 'papplPrinterSetReadyMedia()' - Set the ready (loaded) media. // // This function validates and sets the printer's ready (loaded) media. // bool // O - `true` on success or `false` on failure papplPrinterSetReadyMedia( pappl_printer_t *printer, // I - Printer int num_ready, // I - Number of ready media pappl_media_col_t *ready) // I - Array of ready media { if (!printer || num_ready <= 0 || !ready) return (false); if (!validate_ready(printer, &printer->driver_data, num_ready, ready)) return (false); pthread_rwlock_wrlock(&printer->rwlock); if (num_ready > printer->driver_data.num_source) num_ready = printer->driver_data.num_source; memset(printer->driver_data.media_ready, 0, sizeof(printer->driver_data.media_ready)); memcpy(printer->driver_data.media_ready, ready, (size_t)num_ready * sizeof(pappl_media_col_t)); printer->state_time = time(NULL); pthread_rwlock_unlock(&printer->rwlock); _papplSystemConfigChanged(printer->system); return (true); } // // 'make_attrs()' - Make the capability attributes for the given driver data. // static ipp_t * // O - Driver attributes make_attrs( pappl_system_t *system, // I - System pappl_pr_driver_data_t *data) // I - Driver data { ipp_t *attrs; // Driver attributes unsigned bit; // Current bit value int i, j, // Looping vars num_values; // Number of values const char *svalues[100]; // String values int ivalues[100]; // Integer values ipp_t *cvalues[PAPPL_MAX_MEDIA * 2]; // Collection values char fn[32], // FN (finishings) values *ptr; // Pointer into value const char *preferred; // "document-format-preferred" value const char *prefix; // Prefix string const char *max_name = NULL,// Maximum size *min_name = NULL;// Minimum size char output_tray[256];// "printer-output-tray" value _pappl_mime_filter_t *filter; // Current filter ipp_attribute_t *attr; // Attribute static const int fnvalues[] = // "finishings" values { IPP_FINISHINGS_PUNCH, IPP_FINISHINGS_STAPLE, IPP_FINISHINGS_TRIM }; static const char * const fnstrings[] = { // "finishing-template" values "punch", "staple", "trim" }; static const char * const job_creation_attributes[] = { // job-creation-attributes-supported values "copies", "document-format", "document-name", "ipp-attribute-fidelity", "job-name", "job-priority", "media", "media-col", "multiple-document-handling", "orientation-requested", "output-bin", "print-color-mode", "print-content-optimize", "print-quality", "printer-resolution", "sides" }; static const char * const media_col[] = { // media-col-supported values "media-bottom-margin", "media-left-margin", "media-right-margin", "media-size", "media-size-name", "media-top-margin" }; static const char * const printer_settable_attributes[] = { // printer-settable-attributes values "copies-default", "document-format-default", "label-mode-configured", "label-tear-off-configured", "media-col-default", "media-col-ready", "media-default", "media-ready", "multiple-document-handling-default", "orientation-requested-default", "print-color-mode-default", "print-content-optimize-default", "print-darkness-default", "print-quality-default", "print-speed-default", "printer-darkness-configured", "printer-geo-location", "printer-location", "printer-organization", "printer-organizational-unit", "printer-resolution-default" }; // Create an empty IPP message for the attributes... attrs = ippNew(); // color-supported ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", data->ppm_color > 0); // document-format-supported num_values = 0; svalues[num_values ++] = "application/octet-stream"; svalues[num_values ++] = "image/pwg-raster"; svalues[num_values ++] = "image/urf"; if (data->format && strcmp(data->format, "application/octet-stream")) svalues[num_values ++] = data->format; for (preferred = "image/urf", filter = (_pappl_mime_filter_t *)cupsArrayFirst(system->filters); filter; filter = (_pappl_mime_filter_t *)cupsArrayNext(system->filters)) { if ((data->format && !strcmp(filter->dst, data->format)) || !strcmp(filter->dst, "image/pwg-raster")) { for (i = 0; i < num_values; i ++) { if (!strcmp(filter->src, svalues[i])) break; } if (i >= num_values && num_values < (int)(sizeof(svalues) / sizeof(svalues[0]))) { svalues[num_values ++] = filter->src; if (!strcmp(filter->src, "application/pdf")) preferred = "application/pdf"; } } } ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-preferred", NULL, preferred); ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-supported", num_values, NULL, svalues); // Assemble finishings-xxx values... num_values = 0; cvalues[num_values ] = ippNew(); ippAddString(cvalues[num_values], IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none"); ivalues[num_values ] = IPP_FINISHINGS_NONE; svalues[num_values ++] = "none"; strlcpy(fn, "FN3", sizeof(fn)); for (ptr = fn + 3, i = 0, bit = PAPPL_FINISHINGS_PUNCH; bit <= PAPPL_FINISHINGS_TRIM; i ++, bit *= 2) { if (data->finishings & bit) { cvalues[num_values ] = ippNew(); ippAddString(cvalues[num_values], IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, fnstrings[i]); ivalues[num_values ] = fnvalues[i]; svalues[num_values ++] = fnstrings[i]; snprintf(ptr, sizeof(fn) - (size_t)(ptr - fn), "-%d", fnvalues[i]); ptr += strlen(ptr); } } *ptr = '\0'; // finishing-template-supported ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template-supported", num_values, NULL, svalues); // finishing-col-database ippAddCollections(attrs, IPP_TAG_PRINTER, "finishing-col-database", num_values, (const ipp_t **)cvalues); // finishing-col-default ippAddCollection(attrs, IPP_TAG_PRINTER, "finishing-col-default", cvalues[0]); // finishing-col-supported ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-col-supported", NULL, "finishing-template"); // finishings-default ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE); // finishings-supported ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", num_values, ivalues); for (i = 0; i < num_values; i ++) ippDelete(cvalues[i]); // identify-actions-supported for (num_values = 0, bit = PAPPL_IDENTIFY_ACTIONS_DISPLAY; bit <= PAPPL_IDENTIFY_ACTIONS_SPEAK; bit *= 2) { if (data->identify_supported & bit) svalues[num_values ++] = _papplIdentifyActionsString(bit); } if (num_values > 0) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-supported", num_values, NULL, svalues); // ipp-features-supported num_values = data->num_features; if (data->num_features > 0) memcpy(svalues, data->features, (size_t)data->num_features * sizeof(char *)); svalues[num_values ++] = "ipp-everywhere"; ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", num_values, NULL, svalues); // job-creation-attributes-supported memcpy(svalues, job_creation_attributes, sizeof(job_creation_attributes)); num_values = (int)(sizeof(job_creation_attributes) / sizeof(job_creation_attributes[0])); if (_papplSystemFindMIMEFilter(system, "application/pdf", "image/pwg-raster")) svalues[num_values ++] = "page-ranges"; if (data->darkness_supported) svalues[num_values ++] = "print-darkness"; if (data->speed_supported[1]) svalues[num_values ++] = "print-speed"; for (i = 0; i < data->num_vendor && i < (int)(sizeof(data->vendor) / sizeof(data->vendor[0])) && i < (int)(sizeof(svalues) / sizeof(svalues[0])); i ++) svalues[num_values ++] = data->vendor[i]; ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", num_values, NULL, svalues); // label-mode-supported for (num_values = 0, bit = PAPPL_LABEL_MODE_APPLICATOR; bit <= PAPPL_LABEL_MODE_TEAR_OFF; bit *= 2) { if (data->mode_supported & bit) svalues[num_values ++] = _papplLabelModeString((pappl_label_mode_t)bit); } if (num_values > 0) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "label-mode-supported", num_values, NULL, svalues); // label-tear-offset-supported if (data->tear_offset_supported[0] || data->tear_offset_supported[1]) ippAddRange(attrs, IPP_TAG_PRINTER, "label-tear-offset-supported", data->tear_offset_supported[0], data->tear_offset_supported[1]); // landscape-orientation-requested-preferred ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "landscape-orientation-requested-preferred", IPP_ORIENT_LANDSCAPE); // media-bottom-margin-supported num_values = 0; if (data->borderless) ivalues[num_values ++] = 0; ivalues[num_values ++] = data->bottom_top; ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_values, ivalues); // media-col-database for (i = 0, num_values = 0; i < data->num_media; i ++) { if (!strncmp(data->media[i], "custom_max_", 11) || !strncmp(data->media[i], "roll_max_", 9)) { max_name = data->media[i]; } else if (!strncmp(data->media[i], "custom_min_", 11) || !strncmp(data->media[i], "roll_min_", 9)) { min_name = data->media[i]; } else { pappl_media_col_t col; // Media collection pwg_media_t *pwg; // PWG media size info memset(&col, 0, sizeof(col)); strlcpy(col.size_name, data->media[i], sizeof(col.size_name)); if ((pwg = pwgMediaForPWG(data->media[i])) != NULL) { col.size_width = pwg->width; col.size_length = pwg->length; } if (data->borderless && data->bottom_top > 0 && data->left_right > 0) cvalues[num_values ++] = _papplMediaColExport(data, &col, true); col.bottom_margin = col.top_margin = data->bottom_top; col.left_margin = col.right_margin = data->left_right; if ((cvalues[num_values] = _papplMediaColExport(data, &col, true)) != NULL) num_values ++; } } if (min_name && max_name) { pwg_media_t *pwg, // Current media size info max_pwg, // PWG maximum media size info min_pwg; // PWG minimum media size info ipp_t *col; // media-size collection if ((pwg = pwgMediaForPWG(max_name)) != NULL) max_pwg = *pwg; else memset(&max_pwg, 0, sizeof(max_pwg)); if ((pwg = pwgMediaForPWG(min_name)) != NULL) min_pwg = *pwg; else memset(&min_pwg, 0, sizeof(min_pwg)); col = ippNew(); ippAddRange(col, IPP_TAG_PRINTER, "x-dimension", min_pwg.width, max_pwg.width); ippAddRange(col, IPP_TAG_PRINTER, "y-dimension", min_pwg.length, max_pwg.length); cvalues[num_values] = ippNew(); ippAddCollection(cvalues[num_values], IPP_TAG_PRINTER, "media-size", col); if (data->borderless && data->bottom_top > 0 && data->left_right > 0) { ippAddInteger(cvalues[num_values], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", 0); ippAddInteger(cvalues[num_values], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", 0); ippAddInteger(cvalues[num_values], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", 0); ippAddInteger(cvalues[num_values ++], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", 0); cvalues[num_values] = ippNew(); ippAddCollection(cvalues[num_values], IPP_TAG_PRINTER, "media-size", col); } ippAddInteger(cvalues[num_values], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", data->bottom_top); ippAddInteger(cvalues[num_values], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", data->left_right); ippAddInteger(cvalues[num_values], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", data->left_right); ippAddInteger(cvalues[num_values ++], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", data->bottom_top); ippDelete(col); } if (num_values > 0) { ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_values, (const ipp_t **)cvalues); for (i = 0; i < num_values; i ++) ippDelete(cvalues[i]); } // media-col-supported memcpy(svalues, media_col, sizeof(media_col)); num_values = (int)(sizeof(media_col) / sizeof(media_col[0])); if (data->left_offset_supported[1]) svalues[num_values ++] = "media-left-offset"; if (data->num_source) svalues[num_values ++] = "media-source"; if (data->top_offset_supported[1]) svalues[num_values ++] = "media-top-offset"; if (data->tracking_supported) svalues[num_values ++] = "media-tracking"; if (data->num_type) svalues[num_values ++] = "media-type"; ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-col-supported", num_values, NULL, svalues); // media-left-margin-supported num_values = 0; if (data->borderless) ivalues[num_values ++] = 0; ivalues[num_values ++] = data->left_right; ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_values, ivalues); // media-left-offset-supported if (data->left_offset_supported[1]) ippAddRange(attrs, IPP_TAG_PRINTER, "media-left-offset-supported", data->left_offset_supported[0], data->left_offset_supported[1]); // media-right-margin-supported num_values = 0; if (data->borderless) ivalues[num_values ++] = 0; ivalues[num_values ++] = data->left_right; ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_values, ivalues); // media-size-supported for (i = 0, num_values = 0; i < data->num_media; i ++) { pwg_media_t *pwg; // PWG media size info if (!strncmp(data->media[i], "custom_max_", 11) || !strncmp(data->media[i], "roll_max_", 9)) { max_name = data->media[i]; } else if (!strncmp(data->media[i], "custom_min_", 11) || !strncmp(data->media[i], "roll_min_", 9)) { min_name = data->media[i]; } else if ((pwg = pwgMediaForPWG(data->media[i])) != NULL) { cvalues[num_values] = ippNew(); ippAddInteger(cvalues[num_values], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", pwg->width); ippAddInteger(cvalues[num_values ++], IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", pwg->length); } } if (min_name && max_name) { pwg_media_t *pwg, // Current media size info max_pwg, // PWG maximum media size info min_pwg; // PWG minimum media size info if ((pwg = pwgMediaForPWG(max_name)) != NULL) max_pwg = *pwg; else memset(&max_pwg, 0, sizeof(max_pwg)); if ((pwg = pwgMediaForPWG(min_name)) != NULL) min_pwg = *pwg; else memset(&min_pwg, 0, sizeof(min_pwg)); cvalues[num_values] = ippNew(); ippAddRange(cvalues[num_values], IPP_TAG_PRINTER, "x-dimension", min_pwg.width, max_pwg.width); ippAddRange(cvalues[num_values ++], IPP_TAG_PRINTER, "y-dimension", min_pwg.length, max_pwg.length); } if (num_values > 0) { ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_values, (const ipp_t **)cvalues); for (i = 0; i < num_values; i ++) ippDelete(cvalues[i]); } // media-source-supported if (data->num_source) { memcpy(svalues, data->source, (size_t)data->num_source * sizeof(char *)); num_values = data->num_source; } else num_values = 0; svalues[num_values ++] = "auto"; ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", num_values, NULL, svalues); // media-supported if (data->num_media) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", data->num_media, NULL, data->media); // media-top-margin-supported num_values = 0; if (data->borderless) ivalues[num_values ++] = 0; ivalues[num_values ++] = data->bottom_top; ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_values, ivalues); // media-top-offset-supported if (data->top_offset_supported[1]) ippAddRange(attrs, IPP_TAG_PRINTER, "media-top-offset-supported", data->top_offset_supported[0], data->top_offset_supported[1]); // media-tracking-supported if (data->tracking_supported) { for (num_values = 0, bit = PAPPL_MEDIA_TRACKING_CONTINUOUS; bit <= PAPPL_MEDIA_TRACKING_WEB; bit *= 2) { if (data->tracking_supported & bit) svalues[num_values ++] = _papplMediaTrackingString((pappl_media_tracking_t)bit); } ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-tracking-supported", num_values, NULL, svalues); } // media-type-supported if (data->num_type) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", data->num_type, NULL, data->type); // mopria-certified (Mopria-specific attribute) if (!ippFindAttribute(attrs, "mopria-certified", IPP_TAG_ZERO)) ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "mopria-certified", NULL, "1.3"); // output-bin-supported if (data->num_bin) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", data->num_bin, NULL, data->bin); else if (data->output_face_up) ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up"); else ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down"); // pages-per-minute if (data->ppm > 0) ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", data->ppm); else ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", 1); // pages-per-minute-color if (data->ppm_color > 0) ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", data->ppm_color); // print-color-mode-supported for (num_values = 0, bit = PAPPL_COLOR_MODE_AUTO; bit <= PAPPL_COLOR_MODE_PROCESS_MONOCHROME; bit *= 2) { if (bit & data->color_supported) svalues[num_values ++] = _papplColorModeString(bit); } if (num_values > 0) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", num_values, NULL, svalues); // print-darkness-supported if (data->darkness_supported) ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "print-darkness-supported", 2 * data->darkness_supported); // print-speed-supported if (data->speed_supported[1]) ippAddRange(attrs, IPP_TAG_PRINTER, "print-speed-supported", data->speed_supported[0], data->speed_supported[1]); // printer-darkness-supported if (data->darkness_supported) ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-darkness-supported", data->darkness_supported); // printer-kind for (num_values = 0, bit = PAPPL_KIND_DISC; bit <= PAPPL_KIND_ROLL; bit *= 2) { if (bit & data->kind) svalues[num_values ++] = _papplKindString(bit); } if (num_values > 0) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-kind", num_values, NULL, svalues); // printer-make-and-model ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, data->make_and_model); // printer-output-tray if (data->num_bin > 0) { for (i = 0, attr = NULL; i < data->num_bin; i ++) { snprintf(output_tray, sizeof(output_tray), "type=unRemovableBin;maxcapacity=-2;remaining=-2;status=0;name=%s;%s", data->bin[i], data->output_face_up ? "stackingorder=lastToFirst;pagedelivery=faceUp;" : "stackingorder=firstToLast;pagedelivery=faceDown;"); if (attr) ippSetOctetString(attrs, &attr, ippGetCount(attr), output_tray, (int)strlen(output_tray)); else attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-output-tray", output_tray, (int)strlen(output_tray)); } } else if (data->output_face_up) { strlcpy(output_tray, "type=unRemovableBin;maxcapacity=-2;remaining=-2;status=0;name=face-up;stackingorder=lastToFirst;pagedelivery=faceUp;", sizeof(output_tray)); ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-output-tray", output_tray, (int)strlen(output_tray)); } else { strlcpy(output_tray, "type=unRemovableBin;maxcapacity=-2;remaining=-2;status=0;name=face-down;stackingorder=firstToLast;pagedelivery=faceDown;", sizeof(output_tray)); ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-output-tray", output_tray, (int)strlen(output_tray)); } // printer-resolution-supported if (data->num_resolution > 0) ippAddResolutions(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", data->num_resolution, IPP_RES_PER_INCH, data->x_resolution, data->y_resolution); // printer-settable-attributes ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-settable-attributes", (int)(sizeof(printer_settable_attributes) / sizeof(printer_settable_attributes[0])), NULL, printer_settable_attributes); // pwg-raster-document-resolution-supported if (data->num_resolution > 0) ippAddResolutions(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", data->num_resolution, IPP_RES_PER_INCH, data->x_resolution, data->y_resolution); // pwg-raster-document-sheet-back if (data->duplex) { static const char * const backs[] = // "pwg-raster-document-sheet-back" values { "normal", "flipped", "rotated", "manual-tumble" }; ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, backs[data->duplex - 1]); } // pwg-raster-document-type-supported for (num_values = 0, bit = PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_8; bit <= PAPPL_PWG_RASTER_TYPE_SRGB_16; bit *= 2) { if (data->raster_types & bit) svalues[num_values ++] = _papplRasterTypeString(bit); } if (num_values > 0) ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", num_values, NULL, svalues); // sides-supported if (data->sides_supported) { for (num_values = 0, bit = PAPPL_SIDES_ONE_SIDED; bit <= PAPPL_SIDES_TWO_SIDED_SHORT_EDGE; bit *= 2) { if (data->sides_supported & bit) svalues[num_values ++] = _papplSidesString(bit); } ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", num_values, NULL, svalues); } else ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided"); // urf-supported if (data->num_resolution > 0) { char dm[32], // DM (duplex mode) value is[256], // IS (media-source) values mt[256], // MT (media-type) values ob[256], // OB (output-bin) values rs[32]; // RS (resolution) values num_values = 0; svalues[num_values ++] = "V1.4"; svalues[num_values ++] = "W8"; if (data->raster_types & PAPPL_PWG_RASTER_TYPE_SRGB_8) svalues[num_values ++] = "SRGB24"; if (data->raster_types & PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_16) svalues[num_values ++] = "ADOBERGB24-48"; else if (data->raster_types & PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_8) svalues[num_values ++] = "ADOBERGB24"; if (data->raster_types & PAPPL_PWG_RASTER_TYPE_BLACK_16) svalues[num_values ++] = "DEVW8-16"; else if (data->raster_types & PAPPL_PWG_RASTER_TYPE_BLACK_8) svalues[num_values ++] = "DEVW8"; if (data->raster_types & PAPPL_PWG_RASTER_TYPE_RGB_16) svalues[num_values ++] = "DEVRGB24-48"; else if (data->raster_types & PAPPL_PWG_RASTER_TYPE_RGB_8) svalues[num_values ++] = "DEVRGB24"; if (data->raster_types & PAPPL_PWG_RASTER_TYPE_CMYK_16) svalues[num_values ++] = "DEVCMYK32-64"; else if (data->raster_types & PAPPL_PWG_RASTER_TYPE_CMYK_8) svalues[num_values ++] = "DEVCMYK32"; svalues[num_values ++] = "PQ3-4-5"; if (data->duplex) { snprintf(dm, sizeof(dm), "DM%d", (int)data->duplex); svalues[num_values ++] = dm; } else if (data->sides_supported & PAPPL_SIDES_TWO_SIDED_LONG_EDGE) svalues[num_values ++] = "DM1"; if (fn[0]) svalues[num_values ++] = fn; if (data->num_source) { static const char * const iss[] = // IS/"media-source" values { "auto", "main", "alternate", "large-capacity", "manual", "envelope", "disc", "photo", "hagaki", "main-roll", "alternate-roll", "top", "middle", "bottom", "side", "left", "right", "center", "rear", "by-pass-tray", // a.k.a. multi-purpose tray "tray-1", "tray-2", "tray-3", "tray-4", "tray-5", "tray-6", "tray-7", "tray-8", "tray-9", "tray-10", "tray-11", "tray-12", "tray-13", "tray-14", "tray-15", "tray-16", "tray-17", "tray-18", "tray-19", "tray-20", "roll-1", "roll-2", "roll-3", "roll-4", "roll-5", "roll-6", "roll-7", "roll-8", "roll-9", "roll-10" }; for (i = 0, ptr = is, *ptr = '\0', prefix = "IS"; i < data->num_source; i ++) { for (j = 0; j < (int)(sizeof(iss) / sizeof(iss[0])); j ++) { if (!strcmp(iss[j], data->source[i])) { snprintf(ptr, sizeof(is) - (size_t)(ptr - is), "%s%d", prefix, j); ptr += strlen(ptr); prefix = "-"; } } } if (is[0]) svalues[num_values ++] = is; } if (data->num_type) { static const char * const mts[] = // MT/"media-type" values { "auto", "stationery", "transparency", "envelope", "cardstock", "labels", "stationery-letterhead", "disc", "photographic-matte", "photographic-satin", "photographic-semi-gloss", "photographic-glossy", "photographic-high-gloss", "other" }; for (i = 0, ptr = mt, *ptr = '\0', prefix = "MT"; i < data->num_type; i ++) { for (j = 0; j < (int)(sizeof(mts) / sizeof(mts[0])); j ++) { if (!strcmp(mts[j], data->type[i])) { snprintf(ptr, sizeof(mt) - (size_t)(ptr - mt), "%s%d", prefix, j); ptr += strlen(ptr); prefix = "-"; } } } if (mt[0]) svalues[num_values ++] = mt; } if (data->num_bin) { static const char * const obs[] = // OB/"output-bin" values { "auto", "top", "middle", "bottom", "side", "left", "right", "center", "rear", "face-up", "face-down", "large-capacity", "stacker", "my-mailbox", "mailbox-1", "mailbox-2", "mailbox-3", "mailbox-4", "mailbox-5", "mailbox-6", "mailbox-7", "mailbox-8", "mailbox-9", "mailbox-10", "stacker-1", "stacker-2", "stacker-3", "stacker-4", "stacker-5", "stacker-6", "stacker-7", "stacker-8", "stacker-9", "stacker-10", "tray-1", "tray-2", "tray-3", "tray-4", "tray-5", "tray-6", "tray-7", "tray-8", "tray-9", "tray-10" }; for (i = 0, ptr = ob, *ptr = '\0', prefix = "OB"; i < data->num_bin; i ++) { for (j = 0; j < (int)(sizeof(obs) / sizeof(obs[0])); j ++) { if (!strcmp(obs[j], data->bin[i])) { snprintf(ptr, sizeof(ob) - (size_t)(ptr - ob), "%s%d", prefix, j); ptr += strlen(ptr); prefix = "-"; } } } if (ob[0]) svalues[num_values ++] = ob; } else if (data->output_face_up) svalues[num_values ++] = "OB9"; else svalues[num_values ++] = "OB10"; if (data->input_face_up) svalues[num_values ++] = "IFU0"; if (data->output_face_up) svalues[num_values ++] = "OFU0"; if (data->num_resolution == 1) snprintf(rs, sizeof(rs), "RS%d", data->x_resolution[0]); else snprintf(rs, sizeof(rs), "RS%d-%d", data->x_resolution[data->num_resolution - 2], data->x_resolution[data->num_resolution - 1]); svalues[num_values ++] = rs; ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_values, NULL, svalues); } return (attrs); } // // 'validate_defaults()' - Validate the printing defaults and supported values. // static bool // O - `true` if valid, `false` otherwise validate_defaults( pappl_printer_t *printer, // I - Printer pappl_pr_driver_data_t *driver_data,// I - Driver values pappl_pr_driver_data_t *data) // I - Default values { bool ret = true; // Return value int i; // Looping var int max_width = 0, // Maximum media width max_length = 0, // Maximum media length min_width = 99999999, // Minimum media width min_length = 99999999; // Minimum media length pwg_media_t *pwg; // PWG media size if (!(data->identify_default & driver_data->identify_supported) && driver_data->identify_supported) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported identify-actions-default=0x%04x", data->identify_default); ret = false; } else if (driver_data->identify_supported) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "identify-actions-default=0x%04x", data->identify_default); } for (i = 0; i < driver_data->num_media; i ++) { if (!strcmp(driver_data->media[i], data->media_default.size_name)) break; if ((pwg = pwgMediaForPWG(driver_data->media[i])) != NULL) { if (pwg->width > max_width) max_width = pwg->width; if (pwg->width < min_width) min_width = pwg->width; if (pwg->length > max_length) max_length = pwg->length; if (pwg->length < min_length) min_length = pwg->length; } } if (i < driver_data->num_media || (data->media_default.size_width >= min_width && data->media_default.size_width <= max_width && data->media_default.size_length >= min_length && data->media_default.size_length <= max_length)) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "media-default=%s", data->media_default.size_name); } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-default=%s", data->media_default.size_name); papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "width=%d, length=%d", data->media_default.size_width, data->media_default.size_length); papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "num_media=%d, min_width=%d, max_width=%d, min_length=%d, max_length=%d", driver_data->num_media, min_width, max_width, min_length, max_length); ret = false; } papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "orientation-requested-default=%d(%s)", data->orient_default, ippEnumString("orientation-requested", (int)data->orient_default)); if (!(data->color_default & driver_data->color_supported)) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported print-color-mode-default=%s(0x%04x)", _papplColorModeString(data->color_default), data->color_default); ret = false; } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "print-color-mode-default=%s(0x%04x)", _papplColorModeString(data->color_default), data->color_default); } papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "print-content-default=%s(0x%04x)", _papplContentString(data->content_default), data->content_default); papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "print-quality-default=%d(%s)", (int)data->quality_default, ippEnumString("print-quality", (int)data->quality_default)); papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "print-scaling-default=%s(0x%04x)", _papplScalingString(data->scaling_default), data->scaling_default); for (i = 0; i < driver_data->num_resolution; i ++) { if (data->x_default == driver_data->x_resolution[i] && data->y_default == driver_data->y_resolution[i]) break; } if (i >= driver_data->num_resolution) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported printer-resolution-default=%dx%ddpi", data->x_default, data->y_default); ret = false; } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "printer-resolution-default=%dx%ddpi", data->x_default, data->y_default); } if (!(data->sides_default & driver_data->sides_supported) && driver_data->sides_supported) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported sides-default=%s(0x%04x)", _papplSidesString(data->sides_default), data->sides_default); ret = false; } else if (driver_data->sides_supported) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "sides-default=%s(0x%04x)", _papplSidesString(data->sides_default), data->sides_default); } return (ret); } // // 'validate_driver()' - Validate the driver-specific values. // static bool // O - `true` if valid, `false` otherwise validate_driver( pappl_printer_t *printer, // I - Printer pappl_pr_driver_data_t *data) // I - Driver values { bool ret = true; // Return value int i, // Looping variable num_icons; // Number of printer icons const char *venptr; // Pointer into vendor name static const char * const icon_sizes[] = { // Icon sizes "small-48x48", "medium-128x128", "large-512x512" }; // Validate all driver fields and show debug/warning/fatal errors along the way. if (data->extension) papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Driver uses extension data (%p) and %sdelete function.", data->extension, data->delete_cb ? "" : "no "); if (!data->identify_cb) papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Driver does not support identification."); if (data->printfile_cb) { if (data->format) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Driver supports raw printing of '%s' files.", data->format); } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver supports raw printing but hasn't set the format."); ret = false; } } if (!data->rendjob_cb || !data->rendpage_cb || !data->rstartjob_cb || !data->rstartpage_cb || !data->rwriteline_cb) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver does not provide required raster printing callbacks."); ret = false; } if (!data->status_cb) papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Driver does not support status updates."); if (!data->testpage_cb) papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Driver does not support a self-test page."); if (!data->make_and_model[0]) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver does not provide a make_and_model string."); ret = false; } if (data->ppm <= 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver does not provide a valid ppm value (%d).", data->ppm); ret = false; } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Driver reports ppm %d.", data->ppm); } if (data->ppm_color < 0 || data->ppm_color > data->ppm) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver does not provide a valid ppm_color value (%d).", data->ppm_color); ret = false; } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Driver reports ppm_color %d.", data->ppm_color); } for (i = 0, num_icons = 0; i < 3; i ++) { if (data->icons[i].filename[0]) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Driver provides %s icon in file '%s'.", icon_sizes[i], data->icons[i].filename); num_icons ++; } else if (data->icons[i].data && data->icons[i].datalen > 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Driver provides %s icon in memory (%u bytes).", icon_sizes[i], (unsigned)data->icons[i].datalen); num_icons ++; } } if (num_icons == 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Driver does not provide printer icons, using defaults."); } if (!data->raster_types) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver does not provide required raster types."); ret = false; } if (data->num_resolution <= 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Driver does not provide required raster resolutions."); ret = false; } else { for (i = 0; i < data->num_resolution; i ++) { if (data->x_resolution[i] <= 0 || data->y_resolution[i] <= 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Invalid driver raster resolution %dx%ddpi.", data->x_resolution[i], data->y_resolution[i]); ret = false; } } } if (data->left_right < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Invalid driver left/right margins value %d.", data->left_right); ret = false; } if (data->bottom_top < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Invalid driver bottom/top margins value %d.", data->bottom_top); ret = false; } for (i = 0; i < data->num_media; i ++) { if (!pwgMediaForPWG(data->media[i])) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Invalid driver media value '%s'.", data->media[i]); ret = false; } } for (i = 0; i < data->num_vendor; i ++) { for (venptr = data->vendor[i]; *venptr; venptr ++) { int vench = *venptr & 255; // Current character if (!isalnum(vench) && vench != '-' && vench != '_') break; } if (*venptr) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Invalid vendor attribute name '%s'.", data->vendor[i]); ret = false; } } return (ret); } // // 'validate_ready()' - Validate the ready media values. // static bool // O - `true` if valid, `false` otherwise validate_ready( pappl_printer_t *printer, // I - Printer pappl_pr_driver_data_t *driver_data,// I - Driver data int num_ready, // I - Number of ready media values pappl_media_col_t *ready) // I - Ready media values { bool ret = true; // Return value int i, j; // Looping vars int max_width = 0, // Maximum media width max_length = 0, // Maximum media length min_width = 99999999, // Minimum media width min_length = 99999999; // Minimum media length pwg_media_t *pwg; // PWG media size if (num_ready > driver_data->num_source) return (false); // Determine the range of media sizes... for (i = 0; i < driver_data->num_media; i ++) { if ((pwg = pwgMediaForPWG(driver_data->media[i])) != NULL) { if (pwg->width > max_width) max_width = pwg->width; if (pwg->width < min_width) min_width = pwg->width; if (pwg->length > max_length) max_length = pwg->length; if (pwg->length < min_length) min_length = pwg->length; } } for (i = 0; i < num_ready; i ++) { if (!ready[i].size_name[0]) continue; if (!pwgMediaForPWG(ready[i].size_name)) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Invalid media-ready.media-size-name='%s'.", ready[i].size_name); ret = false; } else if (ready[i].size_width < min_width || ready[i].size_width > max_width || ready[i].size_length < min_length || ready[i].size_length > max_length) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-ready.media-size=%.2fx%.2fmm.", ready[i].size_width * 0.01, ready[i].size_length * 0.01); ret = false; } if (ready[i].left_margin < driver_data->left_right && !driver_data->borderless) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-ready.media-left-margin=%d.", ready[i].left_margin); ret = false; } if (ready[i].right_margin < driver_data->left_right && !driver_data->borderless) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-ready.media-right-margin=%d.", ready[i].right_margin); ret = false; } if (ready[i].top_margin < driver_data->bottom_top && !driver_data->borderless) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-ready.media-top-margin=%d.", ready[i].top_margin); ret = false; } if (ready[i].bottom_margin < driver_data->bottom_top && !driver_data->borderless) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-ready.media-bottom-margin=%d.", ready[i].bottom_margin); ret = false; } for (j = 0; j < driver_data->num_source; j ++) { if (!strcmp(ready[i].source, driver_data->source[j])) break; } if (j >= driver_data->num_source) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-ready.media-source='%s'.", ready[i].source); ret = false; } for (j = 0; j < driver_data->num_type; j ++) { if (!strcmp(ready[i].type, driver_data->type[j])) break; } if (j >= driver_data->num_type) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unsupported media-ready.media-type='%s'.", ready[i].type); ret = false; } } return (ret); } pappl-1.0.3/pappl/printer-ipp.c000066400000000000000000001704001403603036100163560ustar00rootroot00000000000000// // Printer IPP processing for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local type... // typedef struct _pappl_attr_s // Input attribute structure { const char *name; // Attribute name ipp_tag_t value_tag; // Value tag int max_count; // Max number of values } _pappl_attr_t; // // Local functions... // static pappl_job_t *create_job(pappl_client_t *client); static void ipp_cancel_current_job(pappl_client_t *client); static void ipp_cancel_jobs(pappl_client_t *client); static void ipp_create_job(pappl_client_t *client); static void ipp_get_jobs(pappl_client_t *client); static void ipp_get_printer_attributes(pappl_client_t *client); static void ipp_identify_printer(pappl_client_t *client); static void ipp_pause_printer(pappl_client_t *client); static void ipp_print_job(pappl_client_t *client); static void ipp_resume_printer(pappl_client_t *client); static void ipp_set_printer_attributes(pappl_client_t *client); static void ipp_validate_job(pappl_client_t *client); static bool valid_job_attributes(pappl_client_t *client); // // '_papplPrinterCopyAttributes()' - Copy printer attributes to a response... // void _papplPrinterCopyAttributes( pappl_client_t *client, // I - Client pappl_printer_t *printer, // I - Printer cups_array_t *ra, // I - Requested attributes const char *format) // I - "document-format" value, if any { int i, // Looping var num_values; // Number of values unsigned bit; // Current bit value const char *svalues[100]; // String values int ivalues[100]; // Integer values pappl_pr_driver_data_t *data = &printer->driver_data; // Driver data _papplCopyAttributes(client->response, printer->attrs, ra, IPP_TAG_ZERO, IPP_TAG_CUPS_CONST); _papplCopyAttributes(client->response, printer->driver_attrs, ra, IPP_TAG_ZERO, IPP_TAG_CUPS_CONST); _papplPrinterCopyState(client->response, printer, ra); if (!ra || cupsArrayFind(ra, "copies-supported")) { // Filter copies-supported value based on the document format... // (no copy support for streaming raster formats) if (format && (!strcmp(format, "image/pwg-raster") || !strcmp(format, "image/urf"))) ippAddRange(client->response, IPP_TAG_PRINTER, "copies-supported", 1, 1); else ippAddRange(client->response, IPP_TAG_PRINTER, "copies-supported", 1, 999); } if (!ra || cupsArrayFind(ra, "identify-actions-default")) { for (num_values = 0, bit = PAPPL_IDENTIFY_ACTIONS_DISPLAY; bit <= PAPPL_IDENTIFY_ACTIONS_SPEAK; bit *= 2) { if (data->identify_default & bit) svalues[num_values ++] = _papplIdentifyActionsString(bit); } if (num_values > 0) ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", num_values, NULL, svalues); else ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "none"); } if ((!ra || cupsArrayFind(ra, "label-mode-configured")) && data->mode_configured) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "label-mode-configured", NULL, _papplLabelModeString(data->mode_configured)); if ((!ra || cupsArrayFind(ra, "label-tear-offset-configured")) && data->tear_offset_supported[1] > 0) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "label-tear-offset-configured", data->tear_offset_configured); if (printer->num_supply > 0) { pappl_supply_t *supply = printer->supply; // Supply values... if (!ra || cupsArrayFind(ra, "marker-colors")) { for (i = 0; i < printer->num_supply; i ++) svalues[i] = _papplMarkerColorString(supply[i].color); ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "marker-colors", printer->num_supply, NULL, svalues); } if (!ra || cupsArrayFind(ra, "marker-high-levels")) { for (i = 0; i < printer->num_supply; i ++) ivalues[i] = supply[i].is_consumed ? 100 : 90; ippAddIntegers(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "marker-high-levels", printer->num_supply, ivalues); } if (!ra || cupsArrayFind(ra, "marker-levels")) { for (i = 0; i < printer->num_supply; i ++) ivalues[i] = supply[i].level; ippAddIntegers(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "marker-levels", printer->num_supply, ivalues); } if (!ra || cupsArrayFind(ra, "marker-low-levels")) { for (i = 0; i < printer->num_supply; i ++) ivalues[i] = supply[i].is_consumed ? 10 : 0; ippAddIntegers(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "marker-low-levels", printer->num_supply, ivalues); } if (!ra || cupsArrayFind(ra, "marker-names")) { for (i = 0; i < printer->num_supply; i ++) svalues[i] = supply[i].description; ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "marker-names", printer->num_supply, NULL, svalues); } if (!ra || cupsArrayFind(ra, "marker-types")) { for (i = 0; i < printer->num_supply; i ++) svalues[i] = _papplMarkerTypeString(supply[i].type); ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "marker-types", printer->num_supply, NULL, svalues); } } if ((!ra || cupsArrayFind(ra, "media-col-default")) && data->media_default.size_name[0]) { ipp_t *col = _papplMediaColExport(&printer->driver_data, &data->media_default, 0); // Collection value ippAddCollection(client->response, IPP_TAG_PRINTER, "media-col-default", col); ippDelete(col); } if (!ra || cupsArrayFind(ra, "media-col-ready")) { int j, // Looping var count; // Number of values ipp_t *col; // Collection value ipp_attribute_t *attr; // media-col-ready attribute pappl_media_col_t media; // Current media... for (i = 0, count = 0; i < data->num_source; i ++) { if (data->media_ready[i].size_name[0]) count ++; } if (data->borderless && (data->bottom_top != 0 || data->left_right != 0)) count *= 2; // Need to report ready media for borderless, too... if (count > 0) { attr = ippAddCollections(client->response, IPP_TAG_PRINTER, "media-col-ready", count, NULL); for (i = 0, j = 0; i < data->num_source && j < count; i ++) { if (data->media_ready[i].size_name[0]) { if (data->borderless && (data->bottom_top != 0 || data->left_right != 0)) { // Report both bordered and borderless media-col values... media = data->media_ready[i]; media.bottom_margin = media.top_margin = data->bottom_top; media.left_margin = media.right_margin = data->left_right; col = _papplMediaColExport(&printer->driver_data, &media, 0); ippSetCollection(client->response, &attr, j ++, col); ippDelete(col); media.bottom_margin = media.top_margin = 0; media.left_margin = media.right_margin = 0; col = _papplMediaColExport(&printer->driver_data, &media, 0); ippSetCollection(client->response, &attr, j ++, col); ippDelete(col); } else { // Just report the single media-col value... col = _papplMediaColExport(&printer->driver_data, data->media_ready + i, 0); ippSetCollection(client->response, &attr, j ++, col); ippDelete(col); } } } } } if ((!ra || cupsArrayFind(ra, "media-default")) && data->media_default.size_name[0]) ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, data->media_default.size_name); if (!ra || cupsArrayFind(ra, "media-ready")) { int j, // Looping vars count; // Number of values ipp_attribute_t *attr; // media-col-ready attribute for (i = 0, count = 0; i < data->num_source; i ++) { if (data->media_ready[i].size_name[0]) count ++; } if (count > 0) { attr = ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", count, NULL, NULL); for (i = 0, j = 0; i < data->num_source && j < count; i ++) { if (data->media_ready[i].size_name[0]) ippSetString(client->response, &attr, j ++, data->media_ready[i].size_name); } } } if (!ra || cupsArrayFind(ra, "multiple-document-handling-default")) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-document-handling-default", NULL, "separate-documents-collated-copies"); if (!ra || cupsArrayFind(ra, "orientation-requested-default")) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", (int)data->orient_default); if (!ra || cupsArrayFind(ra, "output-bin-default")) { if (data->num_bin > 0) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, data->bin[data->bin_default]); else if (data->output_face_up) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up"); else ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down"); } if ((!ra || cupsArrayFind(ra, "print-color-mode-default")) && data->color_default) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, _papplColorModeString(data->color_default)); if (!ra || cupsArrayFind(ra, "print-content-optimize-default")) { if (data->content_default) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, _papplContentString(data->content_default)); else ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto"); } if (!ra || cupsArrayFind(ra, "print-quality-default")) { if (data->quality_default) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", (int)data->quality_default); else ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL); } if (!ra || cupsArrayFind(ra, "print-scaling-default")) { if (data->scaling_default) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-scaling-default", NULL, _papplScalingString(data->scaling_default)); else ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-scaling-default", NULL, "auto"); } if (!ra || cupsArrayFind(ra, "printer-config-change-date-time")) ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time)); if (!ra || cupsArrayFind(ra, "printer-config-change-time")) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time)); if (!ra || cupsArrayFind(ra, "printer-contact-col")) { ipp_t *col = _papplContactExport(&printer->contact); ippAddCollection(client->response, IPP_TAG_PRINTER, "printer-contact-col", col); ippDelete(col); } if (!ra || cupsArrayFind(ra, "printer-current-time")) ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL))); if ((!ra || cupsArrayFind(ra, "printer-darkness-configured")) && data->darkness_supported > 0) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-darkness-configured", data->darkness_configured); if (!ra || cupsArrayFind(ra, "printer-dns-sd-name")) ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-dns-sd-name", NULL, printer->dns_sd_name ? printer->dns_sd_name : ""); pthread_rwlock_rdlock(&client->system->rwlock); _papplSystemExportVersions(client->system, client->response, IPP_TAG_PRINTER, ra); pthread_rwlock_unlock(&client->system->rwlock); if (!ra || cupsArrayFind(ra, "printer-geo-location")) { if (printer->geo_location) ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-geo-location", NULL, printer->geo_location); else ippAddOutOfBand(client->response, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location"); } if (!ra || cupsArrayFind(ra, "printer-icons")) { char uris[3][1024]; // Buffers for URIs const char *values[3]; // Values for attribute httpAssembleURIf(HTTP_URI_CODING_ALL, uris[0], sizeof(uris[0]), "https", NULL, client->host_field, client->host_port, "%s/icon-sm.png", printer->uriname); httpAssembleURIf(HTTP_URI_CODING_ALL, uris[1], sizeof(uris[1]), "https", NULL, client->host_field, client->host_port, "%s/icon-md.png", printer->uriname); httpAssembleURIf(HTTP_URI_CODING_ALL, uris[2], sizeof(uris[2]), "https", NULL, client->host_field, client->host_port, "%s/icon-lg.png", printer->uriname); values[0] = uris[0]; values[1] = uris[1]; values[2] = uris[2]; ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", 3, NULL, values); } if (!ra || cupsArrayFind(ra, "printer-impressions-completed")) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-impressions-completed", printer->impcompleted); if (!ra || cupsArrayFind(ra, "printer-input-tray")) { ipp_attribute_t *attr = NULL; // "printer-input-tray" attribute char value[256]; // Value for current tray pappl_media_col_t *media; // Media in the tray for (i = 0, media = data->media_ready; i < data->num_source; i ++, media ++) { const char *type; // Tray type if (!strcmp(data->source[i], "manual")) type = "sheetFeedManual"; else if (!strcmp(data->source[i], "by-pass-tray")) type = "sheetFeedAutoNonRemovableTray"; else type = "sheetFeedAutoRemovableTray"; snprintf(value, sizeof(value), "type=%s;mediafeed=%d;mediaxfeed=%d;maxcapacity=%d;level=-2;status=0;name=%s;", type, media->size_length, media->size_width, !strcmp(media->source, "manual") ? 1 : -2, media->source); if (attr) ippSetOctetString(client->response, &attr, ippGetCount(attr), value, (int)strlen(value)); else attr = ippAddOctetString(client->response, IPP_TAG_PRINTER, "printer-input-tray", value, (int)strlen(value)); } // The "auto" tray is a dummy entry... strlcpy(value, "type=other;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto;", sizeof(value)); ippSetOctetString(client->response, &attr, ippGetCount(attr), value, (int)strlen(value)); } if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs")) ippAddBoolean(client->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", !printer->system->shutdown_time); if (!ra || cupsArrayFind(ra, "printer-location")) ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, printer->location ? printer->location : ""); if (!ra || cupsArrayFind(ra, "printer-more-info")) { char uri[1024]; // URI value httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "https", NULL, client->host_field, client->host_port, "%s/", printer->uriname); ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, uri); } if (!ra || cupsArrayFind(ra, "printer-organization")) ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-organization", NULL, printer->organization ? printer->organization : ""); if (!ra || cupsArrayFind(ra, "printer-organizational-unit")) ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-organizational-unit", NULL, printer->org_unit ? printer->org_unit : ""); if (!ra || cupsArrayFind(ra, "printer-resolution-default")) ippAddResolution(client->response, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, data->x_default, data->y_default); if (!ra || cupsArrayFind(ra, "printer-speed-default")) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-speed-default", data->speed_default); if (!ra || cupsArrayFind(ra, "printer-state-change-date-time")) ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time)); if (!ra || cupsArrayFind(ra, "printer-state-change-time")) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time)); if (!ra || cupsArrayFind(ra, "printer-strings-languages-supported")) { _pappl_resource_t *r; // Current resource int rcount; // Number of resources pthread_rwlock_rdlock(&printer->system->rwlock); // Cannot use cupsArrayFirst/Last since other threads might be iterating // this array... for (i = 0, num_values = 0, rcount = cupsArrayCount(printer->system->resources); i < rcount && num_values < (int)(sizeof(svalues) / sizeof(svalues[0])); i ++) { r = (_pappl_resource_t *)cupsArrayIndex(printer->system->resources, i); if (r->language) svalues[num_values ++] = r->language; } pthread_rwlock_unlock(&printer->system->rwlock); if (num_values > 0) ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE, "printer-strings-languages-supported", num_values, NULL, svalues); } if (!ra || cupsArrayFind(ra, "printer-strings-uri")) { const char *lang = ippGetString(ippFindAttribute(client->request, "attributes-natural-language", IPP_TAG_LANGUAGE), 0, NULL); // Language char baselang[3], // Base language uri[1024]; // Strings file URI _pappl_resource_t *r; // Current resource int rcount; // Number of resources strlcpy(baselang, lang, sizeof(baselang)); pthread_rwlock_rdlock(&printer->system->rwlock); // Cannot use cupsArrayFirst/Last since other threads might be iterating // this array... for (i = 0, rcount = cupsArrayCount(printer->system->resources); i < rcount; i ++) { r = (_pappl_resource_t *)cupsArrayIndex(printer->system->resources, i); if (r->language && (!strcmp(r->language, lang) || !strcmp(r->language, baselang))) { httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "https", NULL, client->host_field, client->host_port, r->path); ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-strings-uri", NULL, uri); break; } } pthread_rwlock_unlock(&printer->system->rwlock); } if (printer->num_supply > 0) { pappl_supply_t *supply = printer->supply; // Supply values... if (!ra || cupsArrayFind(ra, "printer-supply")) { char value[256]; // "printer-supply" value ipp_attribute_t *attr = NULL; // "printer-supply" attribute for (i = 0; i < printer->num_supply; i ++) { snprintf(value, sizeof(value), "index=%d;type=%s;maxcapacity=100;level=%d;colorantname=%s;", i, _papplSupplyTypeString(supply[i].type), supply[i].level, _papplSupplyColorString(supply[i].color)); if (attr) ippSetOctetString(client->response, &attr, ippGetCount(attr), value, (int)strlen(value)); else attr = ippAddOctetString(client->response, IPP_TAG_PRINTER, "printer-supply", value, (int)strlen(value)); } } if (!ra || cupsArrayFind(ra, "printer-supply-description")) { for (i = 0; i < printer->num_supply; i ++) svalues[i] = supply[i].description; ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-supply-description", printer->num_supply, NULL, svalues); } } if (!ra || cupsArrayFind(ra, "printer-supply-info-uri")) { char uri[1024]; // URI value httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "https", NULL, client->host_field, client->host_port, "%s/supplies", printer->uriname); ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, uri); } if (!ra || cupsArrayFind(ra, "printer-up-time")) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time)); if (!ra || cupsArrayFind(ra, "printer-uri-supported")) { char uris[2][1024]; // Buffers for URIs const char *values[2]; // Values for attribute num_values = 0; if (!papplSystemGetTLSOnly(client->system)) { httpAssembleURI(HTTP_URI_CODING_ALL, uris[num_values], sizeof(uris[0]), "ipp", NULL, client->host_field, client->host_port, printer->resource); values[num_values] = uris[num_values]; num_values ++; } httpAssembleURI(HTTP_URI_CODING_ALL, uris[num_values], sizeof(uris[0]), "ipps", NULL, client->host_field, client->host_port, printer->resource); values[num_values] = uris[num_values]; num_values ++; ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", num_values, NULL, values); } if (!ra || cupsArrayFind(ra, "printer-xri-supported")) _papplPrinterCopyXRI(client, client->response, printer); if (!ra || cupsArrayFind(ra, "queued-job-count")) ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", cupsArrayCount(printer->active_jobs)); if (!ra || cupsArrayFind(ra, "sides-default")) { if (data->sides_default) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, _papplSidesString(data->sides_default)); else ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided"); } if (!ra || cupsArrayFind(ra, "uri-authentication-supported")) { // For each supported printer-uri value, report whether authentication is // supported. Since we only support authentication over a secure (TLS) // channel, the value is always 'none' for the "ipp" URI and either 'none' // or 'basic' for the "ipps" URI... if (papplSystemGetTLSOnly(client->system)) { if (papplSystemGetAuthService(client->system)) ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "basic"); else ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none"); } else if (papplSystemGetAuthService(client->system)) { static const char * const uri_authentication_basic[] = { // uri-authentication-supported values "none", "basic" }; ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_basic); } else { static const char * const uri_authentication_none[] = { // uri-authentication-supported values "none", "none" }; ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_none); } } } // // '_papplPrinterCopyState()' - Copy the printer-state-xxx attributes. // void _papplPrinterCopyState( ipp_t *ipp, // I - IPP message pappl_printer_t *printer, // I - Printer cups_array_t *ra) // I - Requested attributes { if (!ra || cupsArrayFind(ra, "printer-state")) ippAddInteger(ipp, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state); if (!ra || cupsArrayFind(ra, "printer-state-message")) { static const char * const messages[] = { "Idle.", "Printing.", "Stopped." }; ippAddString(ipp, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]); } if (!ra || cupsArrayFind(ra, "printer-state-reasons")) { if (printer->state_reasons == PAPPL_PREASON_NONE) { if (printer->is_stopped) ippAddString(ipp, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "moving-to-paused"); else if (printer->state == IPP_PSTATE_STOPPED) ippAddString(ipp, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "paused"); else ippAddString(ipp, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none"); } else { ipp_attribute_t *attr = NULL; // printer-state-reasons pappl_preason_t bit; // Reason bit for (bit = PAPPL_PREASON_OTHER; bit <= PAPPL_PREASON_TONER_LOW; bit *= 2) { if (printer->state_reasons & bit) { if (attr) ippSetString(ipp, &attr, ippGetCount(attr), _papplPrinterReasonString(bit)); else attr = ippAddString(ipp, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, _papplPrinterReasonString(bit)); } } if (printer->is_stopped) ippSetString(ipp, &attr, ippGetCount(attr), "moving-to-paused"); else if (printer->state == IPP_PSTATE_STOPPED) ippSetString(ipp, &attr, ippGetCount(attr), "paused"); } } } // // '_papplPrinterCopyXRI()' - Copy the "printer-xri-supported" attribute. // void _papplPrinterCopyXRI( pappl_client_t *client, // I - Client ipp_t *ipp, // I - IPP message pappl_printer_t *printer) // I - Printer { char uri[1024]; // URI value int i, // Looping var num_values = 0; // Number of values ipp_t *col, // Current collection value *values[2]; // Values for attribute if (!papplSystemGetTLSOnly(client->system)) { // Add ipp: URI... httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, client->host_field, client->host_port, printer->resource); col = ippNew(); ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "xri-authentication", NULL, "none"); ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "xri-security", NULL, "none"); ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_URI, "xri-uri", NULL, uri); values[num_values ++] = col; } // Add ipps: URI... httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipps", NULL, client->host_field, client->host_port, printer->resource); col = ippNew(); ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "xri-authentication", NULL, papplSystemGetAuthService(client->system) ? "basic" : "none"); ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "xri-security", NULL, "tls"); ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_URI, "xri-uri", NULL, uri); values[num_values ++] = col; ippAddCollections(ipp, IPP_TAG_PRINTER, "printer-xri-supported", num_values, (const ipp_t **)values); for (i = 0; i < num_values; i ++) ippDelete(values[i]); } // // '_papplPrinterProcessIPP()' - Process an IPP Printer request. // void _papplPrinterProcessIPP( pappl_client_t *client) // I - Client { switch (ippGetOperation(client->request)) { case IPP_OP_PRINT_JOB : ipp_print_job(client); break; case IPP_OP_VALIDATE_JOB : ipp_validate_job(client); break; case IPP_OP_CREATE_JOB : ipp_create_job(client); break; case IPP_OP_CANCEL_CURRENT_JOB : ipp_cancel_current_job(client); break; case IPP_OP_CANCEL_JOBS : case IPP_OP_CANCEL_MY_JOBS : ipp_cancel_jobs(client); break; case IPP_OP_GET_JOBS : ipp_get_jobs(client); break; case IPP_OP_GET_PRINTER_ATTRIBUTES : ipp_get_printer_attributes(client); break; case IPP_OP_SET_PRINTER_ATTRIBUTES : ipp_set_printer_attributes(client); break; case IPP_OP_IDENTIFY_PRINTER : ipp_identify_printer(client); break; case IPP_OP_PAUSE_PRINTER : ipp_pause_printer(client); break; case IPP_OP_RESUME_PRINTER : ipp_resume_printer(client); break; default : if (client->system->op_cb && (client->system->op_cb)(client, client->system->op_cbdata)) break; papplClientRespondIPP(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, "Operation not supported."); break; } } // // '_papplPrinterSetAttributes()' - Set printer attributes. // bool // O - `true` if OK, `false` otherwise _papplPrinterSetAttributes( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { int create_printer; // Create-Printer request? ipp_attribute_t *rattr; // Current request attribute ipp_tag_t value_tag; // Value tag int count; // Number of values const char *name; // Attribute name char defname[128], // xxx-default name value[1024]; // xxx-default value int i, j; // Looping vars pwg_media_t *pwg; // PWG media size data pappl_pr_driver_data_t driver_data; // Printer driver data bool do_defaults = false, // Update defaults? do_ready = false; // Update ready media? int num_vendor = 0; // Number of vendor defaults cups_option_t *vendor = NULL; // Vendor defaults pappl_contact_t contact; // printer-contact value bool do_contact = false; // Update contact? const char *geo_location = NULL, // printer-geo-location value *location = NULL, // printer-location value *organization = NULL, // printer-organization value *org_unit = NULL; // printer-organizational-unit value static _pappl_attr_t pattrs[] = // Settable printer attributes { { "label-mode-configured", IPP_TAG_KEYWORD, 1 }, { "label-tear-off-configured", IPP_TAG_INTEGER, 1 }, { "media-col-default", IPP_TAG_BEGIN_COLLECTION, 1 }, { "media-col-ready", IPP_TAG_BEGIN_COLLECTION, PAPPL_MAX_SOURCE }, { "media-default", IPP_TAG_KEYWORD, 1 }, { "media-ready", IPP_TAG_KEYWORD, PAPPL_MAX_SOURCE }, { "orientation-requested-default", IPP_TAG_ENUM, 1 }, { "print-color-mode-default", IPP_TAG_KEYWORD, 1 }, { "print-content-optimize-default", IPP_TAG_KEYWORD, 1 }, { "print-darkness-default", IPP_TAG_INTEGER, 1 }, { "print-quality-default", IPP_TAG_ENUM, 1 }, { "print-speed-default", IPP_TAG_INTEGER, 1 }, { "printer-contact-col", IPP_TAG_BEGIN_COLLECTION, 1 }, { "printer-darkness-configured", IPP_TAG_INTEGER, 1 }, { "printer-geo-location", IPP_TAG_URI, 1 }, { "printer-location", IPP_TAG_TEXT, 1 }, { "printer-organization", IPP_TAG_TEXT, 1 }, { "printer-organizational-unit", IPP_TAG_TEXT, 1 }, { "printer-resolution-default", IPP_TAG_RESOLUTION, 1 } }; // Preflight request attributes... create_printer = ippGetOperation(client->request) == IPP_OP_CREATE_PRINTER; papplPrinterGetDriverData(printer, &driver_data); for (rattr = ippFirstAttribute(client->request); rattr; rattr = ippNextAttribute(client->request)) { papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s %s %s%s ...", ippTagString(ippGetGroupTag(rattr)), ippGetName(rattr), ippGetCount(rattr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(rattr))); if (ippGetGroupTag(rattr) == IPP_TAG_OPERATION || (name = ippGetName(rattr)) == NULL) { continue; } else if (ippGetGroupTag(rattr) != IPP_TAG_PRINTER) { papplClientRespondIPPUnsupported(client, rattr); continue; } if (create_printer && (!strcmp(name, "printer-device-id") || !strcmp(name, "printer-name") || !strcmp(name, "smi2699-device-uri") || !strcmp(name, "smi2699-device-command"))) continue; // Validate syntax of provided attributes... value_tag = ippGetValueTag(rattr); count = ippGetCount(rattr); for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++) { if (!strcmp(name, pattrs[i].name) && value_tag == pattrs[i].value_tag && count <= pattrs[i].max_count) break; } if (i >= (int)(sizeof(pattrs) / sizeof(pattrs[0]))) { for (j = 0; j < printer->driver_data.num_vendor; j ++) { snprintf(defname, sizeof(defname), "%s-default", printer->driver_data.vendor[j]); if (!strcmp(name, defname)) { ippAttributeString(rattr, value, sizeof(value)); num_vendor = cupsAddOption(printer->driver_data.vendor[j], value, num_vendor, &vendor); do_defaults = true; break; } } if (j >= printer->driver_data.num_vendor) papplClientRespondIPPUnsupported(client, rattr); } // Then copy the xxx-default values to the if (!strcmp(name, "identify-actions-default")) { driver_data.identify_default = PAPPL_IDENTIFY_ACTIONS_NONE; for (i = 0, count = ippGetCount(rattr); i < count; i ++) driver_data.identify_default |= _papplIdentifyActionsValue(ippGetString(rattr, i, NULL)); do_defaults = true; } else if (!strcmp(name, "label-mode-configured")) { driver_data.mode_configured = _papplLabelModeValue(ippGetString(rattr, 0, NULL)); do_defaults = true; } else if (!strcmp(name, "label-tear-offset-configured")) { driver_data.tear_offset_configured = ippGetInteger(rattr, 0); do_defaults = true; } else if (!strcmp(name, "media-col-default")) { _papplMediaColImport(ippGetCollection(rattr, 0), &driver_data.media_default); do_defaults = true; } else if (!strcmp(name, "media-col-ready")) { count = ippGetCount(rattr); for (i = 0; i < count; i ++) _papplMediaColImport(ippGetCollection(rattr, i), driver_data.media_ready + i); for (; i < PAPPL_MAX_SOURCE; i ++) memset(driver_data.media_ready + i, 0, sizeof(pappl_media_col_t)); do_ready = true; } else if (!strcmp(name, "media-default")) { if ((pwg = pwgMediaForPWG(ippGetString(rattr, 0, NULL))) != NULL) { strlcpy(driver_data.media_default.size_name, pwg->pwg, sizeof(driver_data.media_default.size_name)); driver_data.media_default.size_width = pwg->width; driver_data.media_default.size_length = pwg->length; } do_defaults = true; } else if (!strcmp(name, "media-ready")) { count = ippGetCount(rattr); for (i = 0; i < count; i ++) { if ((pwg = pwgMediaForPWG(ippGetString(rattr, i, NULL))) != NULL) { strlcpy(driver_data.media_ready[i].size_name, pwg->pwg, sizeof(driver_data.media_ready[i].size_name)); driver_data.media_ready[i].size_width = pwg->width; driver_data.media_ready[i].size_length = pwg->length; } } for (; i < PAPPL_MAX_SOURCE; i ++) { driver_data.media_ready[i].size_name[0] = '\0'; driver_data.media_ready[i].size_width = 0; driver_data.media_ready[i].size_length = 0; } do_ready = true; } else if (!strcmp(name, "orientation-requested-default")) { driver_data.orient_default = (ipp_orient_t)ippGetInteger(rattr, 0); do_defaults = true; } else if (!strcmp(name, "print-color-mode-default")) { driver_data.color_default = _papplColorModeValue(ippGetString(rattr, 0, NULL)); do_defaults = true; } else if (!strcmp(name, "print-content-optimize-default")) { driver_data.content_default = _papplContentValue(ippGetString(rattr, 0, NULL)); do_defaults = true; } else if (!strcmp(name, "print-darkness-default")) { driver_data.darkness_default = ippGetInteger(rattr, 0); do_defaults = true; } else if (!strcmp(name, "print-quality-default")) { driver_data.quality_default = (ipp_quality_t)ippGetInteger(rattr, 0); do_defaults = true; } else if (!strcmp(name, "print-scaling-default")) { driver_data.scaling_default = _papplScalingValue(ippGetString(rattr, 0, NULL)); do_defaults = true; } else if (!strcmp(name, "print-speed-default")) { driver_data.speed_default = ippGetInteger(rattr, 0); do_defaults = true; } else if (!strcmp(name, "printer-contact-col")) { _papplContactImport(ippGetCollection(rattr, 0), &contact); do_defaults = true; } else if (!strcmp(name, "printer-darkness-configured")) { driver_data.darkness_configured = ippGetInteger(rattr, 0); do_defaults = true; } else if (!strcmp(name, "printer-geo-location")) { float geo_lat, geo_lon; // Latitude and longitude geo_location = ippGetString(rattr, 0, NULL); if (sscanf(geo_location, "geo:%f,%f", &geo_lat, &geo_lon) != 2 || geo_lat < -90.0 || geo_lat > 90.0 || geo_lon < -180.0 || geo_lon > 180.0) papplClientRespondIPPUnsupported(client, rattr); } else if (!strcmp(name, "printer-location")) { location = ippGetString(rattr, 0, NULL); } else if (!strcmp(name, "printer-organization")) { organization = ippGetString(rattr, 0, NULL); } else if (!strcmp(name, "printer-organization-unit")) { org_unit = ippGetString(rattr, 0, NULL); } else if (!strcmp(name, "printer-resolution-default")) { ipp_res_t units; // Resolution units driver_data.x_default = ippGetResolution(rattr, 0, &driver_data.y_default, &units); do_defaults = true; } } if (ippGetStatusCode(client->response) != IPP_STATUS_OK) { cupsFreeOptions(num_vendor, vendor); return (0); } // Now apply changes... if (do_defaults && !papplPrinterSetDriverDefaults(printer, &driver_data, num_vendor, vendor)) { papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "One or more attribute values were not supported."); cupsFreeOptions(num_vendor, vendor); return (0); } if (do_ready && !papplPrinterSetReadyMedia(printer, driver_data.num_source, driver_data.media_ready)) { papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "One or more attribute values were not supported."); cupsFreeOptions(num_vendor, vendor); return (0); } if (do_contact) papplPrinterSetContact(printer, &contact); if (geo_location) papplPrinterSetGeoLocation(printer, geo_location); if (location) papplPrinterSetGeoLocation(printer, location); if (organization) papplPrinterSetGeoLocation(printer, organization); if (org_unit) papplPrinterSetGeoLocation(printer, org_unit); cupsFreeOptions(num_vendor, vendor); return (1); } // // 'create_job()' - Create a new job object from a Print-Job or Create-Job // request. // static pappl_job_t * // O - Job create_job( pappl_client_t *client) // I - Client { ipp_attribute_t *attr; // Job attribute const char *job_name, // Job name *username; // Owner // Get the requesting-user-name, document format, and name... if (client->username[0]) username = client->username; else if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL) username = ippGetString(attr, 0, NULL); else username = "guest"; if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL) job_name = ippGetString(attr, 0, NULL); else job_name = "Untitled"; return (_papplJobCreate(client->printer, 0, username, NULL, job_name, client->request)); } // // 'ipp_cancel_current_job()' - Cancel the current job. // static void ipp_cancel_current_job( pappl_client_t *client) // I - Client { pappl_job_t *job; // Job information // Get the job... if ((job = client->printer->processing_job) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "No currently printing job."); return; } // See if the job is already completed, canceled, or aborted; if so, // we can't cancel... switch (job->state) { case IPP_JSTATE_CANCELED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is already canceled - can\'t cancel.", job->job_id); break; case IPP_JSTATE_ABORTED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is already aborted - can\'t cancel.", job->job_id); break; case IPP_JSTATE_COMPLETED : papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job #%d is already completed - can\'t cancel.", job->job_id); break; default : // Cancel the job... papplJobCancel(job); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); break; } } // // 'ipp_cancel_jobs()' - Cancel all jobs. // static void ipp_cancel_jobs(pappl_client_t *client) // I - Client { http_status_t auth_status; // Authorization status // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } // Cancel all jobs... papplPrinterCancelAllJobs(client->printer); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); } // // 'ipp_create_job()' - Create a job object. // static void ipp_create_job(pappl_client_t *client) // I - Client { pappl_job_t *job; // New job cups_array_t *ra; // Attributes to send in response // Do we have a file to print? if (_papplClientHaveDocumentData(client)) { _papplClientFlushDocumentData(client); papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request."); return; } // Validate print job attributes... if (!valid_job_attributes(client)) return; // Create the job... if ((job = create_job(client)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job."); return; } // Return the job info... papplClientRespondIPP(client, IPP_STATUS_OK, NULL); ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); cupsArrayAdd(ra, "job-id"); cupsArrayAdd(ra, "job-state"); cupsArrayAdd(ra, "job-state-message"); cupsArrayAdd(ra, "job-state-reasons"); cupsArrayAdd(ra, "job-uri"); _papplJobCopyAttributes(client, job, ra); cupsArrayDelete(ra); } // // 'ipp_get_jobs()' - Get a list of job objects. // static void ipp_get_jobs(pappl_client_t *client) // I - Client { ipp_attribute_t *attr; // Current attribute const char *which_jobs = NULL; // which-jobs values int job_comparison; // Job comparison ipp_jstate_t job_state; // job-state value int i, // Looping var limit, // Maximum number of jobs to return count; // Number of jobs that match const char *username; // Username cups_array_t *list; // Jobs list pappl_job_t *job; // Current job pointer cups_array_t *ra; // Requested attributes array // See if the "which-jobs" attribute have been specified... if ((attr = ippFindAttribute(client->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL) { which_jobs = ippGetString(attr, 0, NULL); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Get-Jobs \"which-jobs\"='%s'", which_jobs); } if (!which_jobs || !strcmp(which_jobs, "not-completed")) { job_comparison = -1; job_state = IPP_JSTATE_STOPPED; list = client->printer->active_jobs; } else if (!strcmp(which_jobs, "completed")) { job_comparison = 1; job_state = IPP_JSTATE_CANCELED; list = client->printer->completed_jobs; } else if (!strcmp(which_jobs, "all")) { job_comparison = 1; job_state = IPP_JSTATE_PENDING; list = client->printer->all_jobs; } else { papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "The \"which-jobs\" value '%s' is not supported.", which_jobs); ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD, "which-jobs", NULL, which_jobs); return; } // See if they want to limit the number of jobs reported... if ((attr = ippFindAttribute(client->request, "limit", IPP_TAG_INTEGER)) != NULL) { limit = ippGetInteger(attr, 0); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Get-Jobs \"limit\"='%d'", limit); } else limit = 0; // See if we only want to see jobs for a specific user... username = NULL; if ((attr = ippFindAttribute(client->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL) { int my_jobs = ippGetBoolean(attr, 0); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Get-Jobs \"my-jobs\"='%s'", my_jobs ? "true" : "false"); if (my_jobs) { if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Need \"requesting-user-name\" with \"my-jobs\"."); return; } username = ippGetString(attr, 0, NULL); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Get-Jobs \"requesting-user-name\"='%s'", username); } } // OK, build a list of jobs for this printer... ra = ippCreateRequestedArray(client->request); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); pthread_rwlock_rdlock(&(client->printer->rwlock)); count = cupsArrayCount(list); if (limit <= 0 || limit > count) limit = count; for (count = 0, i = 0; i < limit; i ++) { job = (pappl_job_t *)cupsArrayIndex(list, i); // Filter out jobs that don't match... if ((job_comparison < 0 && job->state > job_state) || /* (job_comparison == 0 && job->state != job_state) || */ (job_comparison > 0 && job->state < job_state) || (username && job->username && strcasecmp(username, job->username))) continue; if (count > 0) ippAddSeparator(client->response); count ++; _papplJobCopyAttributes(client, job, ra); } cupsArrayDelete(ra); pthread_rwlock_unlock(&(client->printer->rwlock)); } // // 'ipp_get_printer_attributes()' - Get the attributes for a printer object. // static void ipp_get_printer_attributes( pappl_client_t *client) // I - Client { cups_array_t *ra; // Requested attributes array pappl_printer_t *printer = client->printer; // Printer if (!printer->device_in_use && !printer->processing_job && (time(NULL) - printer->status_time) > 1 && printer->driver_data.status_cb) { // Update printer status... (printer->driver_data.status_cb)(printer); printer->status_time = time(NULL); } // Send the attributes... ra = ippCreateRequestedArray(client->request); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); pthread_rwlock_rdlock(&(printer->rwlock)); _papplPrinterCopyAttributes(client, printer, ra, ippGetString(ippFindAttribute(client->request, "document-format", IPP_TAG_MIMETYPE), 0, NULL)); pthread_rwlock_unlock(&(printer->rwlock)); cupsArrayDelete(ra); } // // 'ipp_identify_printer()' - Beep or display a message. // static void ipp_identify_printer( pappl_client_t *client) // I - Client { int i; // Looping var ipp_attribute_t *attr; // IPP attribute pappl_identify_actions_t actions; // "identify-actions" value const char *message; // "message" value if (client->printer->driver_data.identify_cb) { if ((attr = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD)) != NULL) { actions = PAPPL_IDENTIFY_ACTIONS_NONE; for (i = 0; i < ippGetCount(attr); i ++) actions |= _papplIdentifyActionsValue(ippGetString(attr, i, NULL)); } else actions = client->printer->driver_data.identify_default; if ((attr = ippFindAttribute(client->request, "message", IPP_TAG_TEXT)) != NULL) message = ippGetString(attr, 0, NULL); else message = NULL; (client->printer->driver_data.identify_cb)(client->printer, actions, message); } papplClientRespondIPP(client, IPP_STATUS_OK, NULL); } // // 'ipp_pause_printer()' - Stop a printer. // static void ipp_pause_printer( pappl_client_t *client) // I - Client { http_status_t auth_status; // Authorization status // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } papplPrinterPause(client->printer); papplClientRespondIPP(client, IPP_STATUS_OK, "Printer paused."); } // // 'ipp_print_job()' - Create a job object with an attached document. // static void ipp_print_job(pappl_client_t *client) // I - Client { pappl_job_t *job; // New job // Do we have a file to print? if (!_papplClientHaveDocumentData(client)) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request."); return; } // Validate print job attributes... if (!valid_job_attributes(client)) { _papplClientFlushDocumentData(client); return; } // Create the job... if ((job = create_job(client)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job."); return; } // Then finish getting the document data and process things... _papplJobCopyDocumentData(client, job); } // // 'ipp_resume_printer()' - Start a printer. // static void ipp_resume_printer( pappl_client_t *client) // I - Client { http_status_t auth_status; // Authorization status // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } papplPrinterResume(client->printer); papplClientRespondIPP(client, IPP_STATUS_OK, "Printer resumed."); } // // 'ipp_set_printer_attributes()' - Set printer attributes. // static void ipp_set_printer_attributes( pappl_client_t *client) // I - Client { http_status_t auth_status; // Authorization status // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } if (!_papplPrinterSetAttributes(client, client->printer)) return; papplClientRespondIPP(client, IPP_STATUS_OK, "Printer attributes set."); } // // 'ipp_validate_job()' - Validate job creation attributes. // static void ipp_validate_job( pappl_client_t *client) // I - Client { if (valid_job_attributes(client)) papplClientRespondIPP(client, IPP_STATUS_OK, NULL); } // // 'valid_job_attributes()' - Determine whether the job attributes are valid. // // When one or more job attributes are invalid, this function adds a suitable // response and attributes to the unsupported group. // static bool // O - `true` if valid, `false` if not valid_job_attributes( pappl_client_t *client) // I - Client { int i, // Looping var count; // Number of values bool valid = true; // Valid attributes? ipp_attribute_t *attr, // Current attribute *supported; // xxx-supported attribute // If a shutdown is pending, do not accept more jobs... if (client->system->shutdown_time) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_ACCEPTING_JOBS, "Not accepting new jobs."); return (false); } // Check operation attributes... valid = _papplJobValidateDocumentAttributes(client); pthread_rwlock_rdlock(&client->printer->rwlock); // Check the various job template attributes... if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || (ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG && ippGetValueTag(attr) != IPP_TAG_KEYWORD) || strcmp(ippGetString(attr, 0, NULL), "no-hold")) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || (ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG)) { papplClientRespondIPPUnsupported(client, attr); valid = false; } ippSetGroupTag(client->request, &attr, IPP_TAG_JOB); } else ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled"); if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || (ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG && ippGetValueTag(attr) != IPP_TAG_KEYWORD) || strcmp(ippGetString(attr, 0, NULL), "none")) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || (ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG && ippGetValueTag(attr) != IPP_TAG_KEYWORD)) { papplClientRespondIPPUnsupported(client, attr); valid = false; } else { supported = ippFindAttribute(client->printer->driver_attrs, "media-supported", IPP_TAG_KEYWORD); if (!ippContainsString(supported, ippGetString(attr, 0, NULL))) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } } if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL) { ipp_t *col, // media-col collection *size; // media-size collection ipp_attribute_t *member, // Member attribute *x_dim, // x-dimension *y_dim; // y-dimension int x_value, // y-dimension value y_value; // x-dimension value if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION) { papplClientRespondIPPUnsupported(client, attr); valid = false; } col = ippGetCollection(attr, 0); if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(member) != 1 || (ippGetValueTag(member) != IPP_TAG_NAME && ippGetValueTag(member) != IPP_TAG_NAMELANG && ippGetValueTag(member) != IPP_TAG_KEYWORD)) { papplClientRespondIPPUnsupported(client, attr); valid = false; } else { supported = ippFindAttribute(client->printer->driver_attrs, "media-supported", IPP_TAG_KEYWORD); if (!ippContainsString(supported, ippGetString(member, 0, NULL))) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } } else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL) { if (ippGetCount(member) != 1) { papplClientRespondIPPUnsupported(client, attr); valid = false; } else { size = ippGetCollection(member, 0); if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 || (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1) { papplClientRespondIPPUnsupported(client, attr); valid = false; } else { x_value = ippGetInteger(x_dim, 0); y_value = ippGetInteger(y_dim, 0); supported = ippFindAttribute(client->printer->driver_attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION); count = ippGetCount(supported); for (i = 0; i < count ; i ++) { size = ippGetCollection(supported, i); x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO); y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO); if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value)) break; } if (i >= count) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } } } } if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || (strcmp(ippGetString(attr, 0, NULL), "separate-documents-uncollated-copies") && strcmp(ippGetString(attr, 0, NULL), "separate-documents-collated-copies"))) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM || ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT || ippGetInteger(attr, 0) > IPP_ORIENT_NONE) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL) { int upper = 0, lower = ippGetRange(attr, 0, &upper); // "page-ranges" value if (!ippGetBoolean(ippFindAttribute(client->printer->attrs, "page-ranges-supported", IPP_TAG_BOOLEAN), 0) || ippGetValueTag(attr) != IPP_TAG_RANGE || ippGetCount(attr) != 1 || lower < 1 || upper < lower) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "print-color-mode", IPP_TAG_ZERO)) != NULL) { pappl_color_mode_t value = _papplColorModeValue(ippGetString(attr, 0, NULL)); // "print-color-mode" value if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || !(value & client->printer->driver_data.color_supported)) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "print-content-optimize", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || !_papplContentValue(ippGetString(attr, 0, NULL))) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "print-darkness", IPP_TAG_ZERO)) != NULL) { int value = ippGetInteger(attr, 0); // "print-darkness" value if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || value < -100 || value > 100 || client->printer->driver_data.darkness_supported == 0) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM || ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT || ippGetInteger(attr, 0) > IPP_QUALITY_HIGH) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "print-scaling", IPP_TAG_ZERO)) != NULL) { if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || !_papplScalingValue(ippGetString(attr, 0, NULL))) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "print-speed", IPP_TAG_ZERO)) != NULL) { int value = ippGetInteger(attr, 0); // "print-speed" value if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || value < client->printer->driver_data.speed_supported[0] || value > client->printer->driver_data.speed_supported[1] || client->printer->driver_data.speed_supported[1] == 0) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL) { int xdpi, // Horizontal resolution ydpi; // Vertical resolution ipp_res_t units; // Resolution units xdpi = ippGetResolution(attr, 0, &ydpi, &units); if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION || units != IPP_RES_PER_INCH) { papplClientRespondIPPUnsupported(client, attr); valid = false; } else { for (i = 0; i < client->printer->driver_data.num_resolution; i ++) { if (xdpi == client->printer->driver_data.x_resolution[i] && ydpi == client->printer->driver_data.y_resolution[i]) break; } if (i >= client->printer->driver_data.num_resolution) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } } if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL) { pappl_sides_t value = _papplSidesValue(ippGetString(attr, 0, NULL)); // "sides" value if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || !(value & client->printer->driver_data.sides_supported)) { papplClientRespondIPPUnsupported(client, attr); valid = false; } } pthread_rwlock_unlock(&client->printer->rwlock); return (valid); } pappl-1.0.3/pappl/printer-private.h000066400000000000000000000211651403603036100172500ustar00rootroot00000000000000// // Private printer header file for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_PRINTER_PRIVATE_H_ # define _PAPPL_PRINTER_PRIVATE_H_ // // Include necessary headers... // # include "dnssd-private.h" # include "printer.h" # include "log.h" # include # ifdef __APPLE__ # include # include # else # include # endif // __APPLE__ # ifdef HAVE_SYS_RANDOM_H # include # endif // HAVE_SYS_RANDOM_H # ifdef HAVE_GNUTLS_RND # include # include # endif // HAVE_GNUTLS_RND // // Include necessary headers... // # include "base-private.h" # include "device.h" // // Types and structures... // struct _pappl_printer_s // Printer data { pthread_rwlock_t rwlock; // Reader/writer lock pappl_system_t *system; // Containing system int printer_id; // "printer-id" value char *name, // "printer-name" value *dns_sd_name, // "printer-dns-sd-name" value *location, // "printer-location" value *geo_location, // "printer-geo-location" value (geo: URI) *organization, // "printer-organization" value *org_unit; // "printer-organizational-unit" value pappl_contact_t contact; // "printer-contact" value char *resource; // Resource path of printer size_t resourcelen; // Length of resource path char *uriname; // Name for URLs ipp_pstate_t state; // "printer-state" value pappl_preason_t state_reasons; // "printer-state-reasons" values time_t state_time; // "printer-state-change-time" value bool is_stopped, // Are we stopping this printer? is_deleted; // Has this printer been deleted? char *device_id, // "printer-device-id" value *device_uri; // Device URI pappl_device_t *device; // Current connection to device (if any) bool device_in_use; // Is the device in use? char *driver_name; // Driver name pappl_pr_driver_data_t driver_data; // Driver data ipp_t *driver_attrs; // Driver attributes ipp_t *attrs; // Other (static) printer attributes time_t start_time; // Startup time time_t config_time; // "printer-config-change-time" value time_t status_time; // Last time status was updated char *print_group; // PAM printing group, if any gid_t print_gid; // PAM printing group ID int num_supply; // Number of "printer-supply" values pappl_supply_t supply[PAPPL_MAX_SUPPLY]; // "printer-supply" values pappl_job_t *processing_job; // Currently printing job, if any int max_active_jobs, // Maximum number of active jobs to accept max_completed_jobs; // Maximum number of completed jobs to retain in history cups_array_t *active_jobs, // Array of active jobs *all_jobs, // Array of all jobs *completed_jobs; // Array of completed jobs int next_job_id, // Next "job-id" value impcompleted; // "printer-impressions-completed" value cups_array_t *links; // Web navigation links # ifdef HAVE_DNSSD _pappl_srv_t dns_sd_ipp_ref, // DNS-SD IPP service dns_sd_ipps_ref, // DNS-SD IPPS service dns_sd_http_ref, // DNS-SD HTTP service dns_sd_printer_ref, // DNS-SD LPD service dns_sd_pdl_ref; // DNS-SD AppSocket service DNSRecordRef dns_sd_ipp_loc_ref, // DNS-SD LOC record for IPP service dns_sd_ipps_loc_ref; // DNS-SD LOC record for IPPS service # elif defined(HAVE_AVAHI) _pappl_srv_t dns_sd_ref; // DNS-SD services # endif // HAVE_DNSSD unsigned char dns_sd_loc[16]; // DNS-SD LOC record data bool dns_sd_collision; // Was there a name collision? int dns_sd_serial; // DNS-SD serial number (for collisions) bool raw_active; // Raw listener active? int num_raw_listeners; // Number of raw socket listeners struct pollfd raw_listeners[2]; // Raw socket listeners bool usb_active; // USB gadget active? unsigned short usb_vendor_id, // USB vendor ID usb_product_id; // USB product ID pappl_uoptions_t usb_options; // USB gadget options char *usb_storage; // USB storage gadget file, if any }; // // Functions... // extern bool _papplPrinterAddRawListeners(pappl_printer_t *printer) _PAPPL_PRIVATE; extern void *_papplPrinterRunRaw(pappl_printer_t *printer) _PAPPL_PRIVATE; extern void *_papplPrinterRunUSB(pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterCheckJobs(pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterCleanJobs(pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterCopyAttributes(pappl_client_t *client, pappl_printer_t *printer, cups_array_t *ra, const char *format) _PAPPL_PRIVATE; extern void _papplPrinterCopyState(ipp_t *ipp, pappl_printer_t *printer, cups_array_t *ra) _PAPPL_PRIVATE; extern void _papplPrinterCopyXRI(pappl_client_t *client, ipp_t *ipp, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterDelete(pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterInitDriverData(pappl_pr_driver_data_t *d) _PAPPL_PRIVATE; extern void _papplPrinterProcessIPP(pappl_client_t *client) _PAPPL_PRIVATE; extern bool _papplPrinterRegisterDNSSDNoLock(pappl_printer_t *printer) _PAPPL_PRIVATE; extern bool _papplPrinterSetAttributes(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterUnregisterDNSSDNoLock(pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebCancelAllJobs(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebCancelJob(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebConfig(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebConfigFinalize(pappl_printer_t *printer, int num_form, cups_option_t *form) _PAPPL_PRIVATE; extern void _papplPrinterWebDefaults(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebDelete(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebHome(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebIteratorCallback(pappl_printer_t *printer, pappl_client_t *client) _PAPPL_PRIVATE; extern void _papplPrinterWebJobs(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebMedia(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplPrinterWebSupplies(pappl_client_t *client, pappl_printer_t *printer) _PAPPL_PRIVATE; extern const char *_papplColorModeString(pappl_color_mode_t value) _PAPPL_PRIVATE; extern pappl_color_mode_t _papplColorModeValue(const char *value) _PAPPL_PRIVATE; extern const char *_papplContentString(pappl_content_t value) _PAPPL_PRIVATE; extern pappl_content_t _papplContentValue(const char *value) _PAPPL_PRIVATE; extern ipp_t *_papplCreateMediaSize(const char *size_name) _PAPPL_PRIVATE; extern const char *_papplIdentifyActionsString(pappl_identify_actions_t v) _PAPPL_PRIVATE; extern pappl_identify_actions_t _papplIdentifyActionsValue(const char *s) _PAPPL_PRIVATE; extern const char *_papplKindString(pappl_kind_t v) _PAPPL_PRIVATE; extern const char *_papplLabelModeString(pappl_label_mode_t v) _PAPPL_PRIVATE; extern pappl_label_mode_t _papplLabelModeValue(const char *s) _PAPPL_PRIVATE; extern const char *_papplMarkerColorString(pappl_supply_color_t v) _PAPPL_PRIVATE; extern const char *_papplMarkerTypeString(pappl_supply_type_t v) _PAPPL_PRIVATE; extern ipp_t *_papplMediaColExport(pappl_pr_driver_data_t *driver_data, pappl_media_col_t *media, bool db) _PAPPL_PRIVATE; extern void _papplMediaColImport(ipp_t *col, pappl_media_col_t *media) _PAPPL_PRIVATE; extern const char *_papplMediaTrackingString(pappl_media_tracking_t v); extern pappl_media_tracking_t _papplMediaTrackingValue(const char *s); extern const char *_papplPrinterReasonString(pappl_preason_t value) _PAPPL_PRIVATE; extern pappl_preason_t _papplPrinterReasonValue(const char *value) _PAPPL_PRIVATE; extern const char *_papplRasterTypeString(pappl_raster_type_t value) _PAPPL_PRIVATE; extern const char *_papplScalingString(pappl_scaling_t value) _PAPPL_PRIVATE; extern pappl_scaling_t _papplScalingValue(const char *value) _PAPPL_PRIVATE; extern const char *_papplSidesString(pappl_sides_t value) _PAPPL_PRIVATE; extern pappl_sides_t _papplSidesValue(const char *value) _PAPPL_PRIVATE; extern const char *_papplSupplyColorString(pappl_supply_color_t value) _PAPPL_PRIVATE; extern const char *_papplSupplyTypeString(pappl_supply_type_t value) _PAPPL_PRIVATE; #endif // !_PAPPL_PRINTER_PRIVATE_H_ pappl-1.0.3/pappl/printer-raw.c000066400000000000000000000147561403603036100163720ustar00rootroot00000000000000// // Raw printing support for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // '_papplPrinterAddRawListeners()' - Create listener sockets for raw print queues. // bool // O - `true` on success, `false` otherwise _papplPrinterAddRawListeners( pappl_printer_t *printer) // I - Printer { int sock, // Listener socket port; // Listener port http_addrlist_t *addrlist; // Listen addresses char service[255]; // Service port // Listen on port 9100, 9101, etc. port = 9099 + printer->printer_id; snprintf(service, sizeof(service), "%d", port); if ((addrlist = httpAddrGetList(NULL, AF_INET, service)) != NULL) { if ((sock = httpAddrListen(&(addrlist->addr), port)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create socket print listener for '*:%d': %s", port, cupsLastErrorString()); } else { printer->raw_listeners[printer->num_raw_listeners].fd = sock; printer->raw_listeners[printer->num_raw_listeners].events = POLLIN | POLLERR; printer->num_raw_listeners ++; } httpAddrFreeList(addrlist); } if ((addrlist = httpAddrGetList(NULL, AF_INET6, service)) != NULL) { if ((sock = httpAddrListen(&(addrlist->addr), port)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create socket print listener for '*:%d': %s", port, cupsLastErrorString()); } else { printer->raw_listeners[printer->num_raw_listeners].fd = sock; printer->raw_listeners[printer->num_raw_listeners].events = POLLIN | POLLERR; printer->num_raw_listeners ++; } httpAddrFreeList(addrlist); } if (printer->num_raw_listeners > 0) papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Listening for socket print jobs on '*:%d'.", port); return (printer->num_raw_listeners > 0); } // // '_papplPrinterRunRaw()' - Accept raw print requests over sockets. // void * // O - Thread exit value _papplPrinterRunRaw( pappl_printer_t *printer) // I - Printer { int i; // Looping var papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Running socket print thread with %d listeners.", printer->num_raw_listeners); printer->raw_active = true; while (!printer->is_deleted && printer->system->is_running) { // Don't accept connections if we can't accept a new job... while (cupsArrayCount(printer->active_jobs) >= printer->max_active_jobs && !printer->is_deleted && printer->system->is_running) sleep(1); if (printer->is_deleted || !printer->system->is_running) break; // Wait 1 second for new connections... if ((i = poll(printer->raw_listeners, (nfds_t)printer->num_raw_listeners, 1000)) > 0) { if (printer->is_deleted || !printer->system->is_running) break; // Got a new connection request, accept from the corresponding listener... for (i = 0; i < printer->num_raw_listeners; i ++) { if (printer->raw_listeners[i].revents & POLLIN) { time_t activity; // Network activity watchdog int sock; // Client socket http_addr_t sockaddr; // Client address socklen_t sockaddrlen; // Length of client address struct pollfd sockp; // poll() data for client socket pappl_job_t *job; // New print job ssize_t bytes; // Bytes read from socket char buffer[8192]; // Copy buffer char filename[1024]; // Job filename // Accept the connection... sockaddrlen = sizeof(sockaddr); if ((sock = accept(printer->raw_listeners[i].fd, (struct sockaddr *)&sockaddr, &sockaddrlen)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to accept socket print connection: %s", strerror(errno)); continue; } // Create a new job with default attributes... papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Accepted socket print connection from '%s'.", httpAddrString(&sockaddr, buffer, sizeof(buffer))); if ((job = _papplJobCreate(printer, 0, "guest", printer->driver_data.format ? printer->driver_data.format : "application/octet-stream", "Untitled", NULL)) == NULL) { close(sock); continue; } // Read the print data from the socket... if ((job->fd = papplJobOpenFile(job, filename, sizeof(filename), printer->system->directory, NULL, "w")) < 0) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to create print file: %s", strerror(errno)); goto abort_job; } papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Created job file \"%s\", format \"%s\".", filename, job->format); activity = time(NULL); sockp.fd = sock; sockp.events = POLLIN | POLLERR; for (;;) { if (printer->is_deleted || !printer->system->is_running) { bytes = -1; break; } if ((bytes = poll(&sockp, 1, 1000)) < 0) { if ((time(NULL) - activity) >= 60) break; else continue; } activity = time(NULL); if (sockp.revents & POLLIN) { if ((bytes = read(sock, buffer, sizeof(buffer))) > 0) write(job->fd, buffer, (size_t)bytes); else break; } else if (sockp.revents & POLLERR) { bytes = -1; break; } } close(sock); close(job->fd); job->fd = -1; if (bytes < 0) { // Error while reading unlink(filename); goto abort_job; } // Finish the job... if ((job->filename = strdup(filename)) == NULL) { unlink(filename); goto abort_job; } job->state = IPP_JSTATE_PENDING; _papplPrinterCheckJobs(printer); continue; // Abort the job... abort_job: job->state = IPP_JSTATE_ABORTED; job->completed = time(NULL); pthread_rwlock_wrlock(&printer->rwlock); cupsArrayRemove(printer->active_jobs, job); cupsArrayAdd(printer->completed_jobs, job); if (!printer->system->clean_time) printer->system->clean_time = time(NULL) + 60; pthread_rwlock_unlock(&printer->rwlock); } } } else if (i < 0 && errno != EAGAIN) break; } printer->raw_active = false; return (NULL); } pappl-1.0.3/pappl/printer-support.c000066400000000000000000000374721403603036100173150ustar00rootroot00000000000000// // Printer support functions for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local globals... // static const char * const pappl_color_modes[] = { "auto", "auto-monochrome", "bi-level", "color", "monochrome", "process-monochrome" }; static const char * const pappl_contents[] = { "auto", "graphic", "photo", "text", "text-and-graphic" }; static const char * const pappl_identify_actions[] = { "display", "flash", "sound", "speak" }; static const char * const pappl_kinds[] = { "disc", "document", "envelope", "label", "large-format", "photo", "postcard", "receipt", "roll" }; static const char * const pappl_label_modes[] = { "applicator", "cutter", "cutter-delayed", "kiosk", "peel-off", "peel-off-prepeel", "rewind", "rfid", "tear-off" }; static const char * const pappl_marker_colors[] = { "#777777", "#000000", "#00FFFF", "#777777", "#00CC00", "#77FFFF", "#CCCCCC", "#FFCCFF", "#FF00FF", "#FF7700", "#770077", "#FFFF00" }; static const char * const pappl_marker_types[] = { "banding-supply", "binding-supply", "cleaner-unit", "corona-wire", "covers", "developer", "fuser-cleaning-pad", "fuser-oil-wick", "fuser-oil", "fuser-oiler", "fuser", "ink-cartridge", "ink-ribbon", "ink", "inserts", "opc", "paper-wrap", "ribbon-wax", "solid-wax", "staples", "stitching-wire", "toner-cartridge", "toner", "transfer-unit", "waste-ink", "waste-toner", "waste-water", "waste-wax", "water" }; static const char * const pappl_media_trackings[] = { "continuous", "gap", "mark", "web" }; static const char * const pappl_preasons[] = { "other", "cover-open", "input-tray-missing", "marker-supply-empty", "marker-supply-low", "marker-waste-almost-full", "marker-waste-full", "media-empty", "media-jam", "media-low", "media-needed", "offline", "spool-area-full", "toner-empty", "toner-low" }; static const char * const pappl_raster_types[] = { "adobe-rgb_8", "adobe-rgb_16", "black_1", "black_8", "black_16", "cmyk_8", "cmyk_8", "rgb_8", "rgb_16", "sgray_8", "sgray_16", "srgb_8", "srgb_16" }; static const char * const pappl_scalings[] = { "auto", "auto-fit", "fill", "fit", "none" }; static const char * const pappl_sides[] = { "one-sided", "two-sided-long-edge", "two-sided-short-edge" }; static const char * const pappl_supply_colors[] = { "no-color", "black", "cyan", "gray", "green", "light-cyan", "light-gray", "light-magenta", "magenta", "orange", "violet", "yellow" }; static const char * const pappl_supply_types[] = { "bandingSupply", "bindingSupply", "cleanerUnit", "coronaWire", "covers", "developer", "fuserCleaningPad", "fuserOilWick", "fuserOil", "fuserOiler", "fuser", "inkCartridge", "inkRibbon", "ink", "inserts", "opc", "paperWrap", "ribbonWax", "solidWax", "staples", "stitchingWire", "tonerCartridge", "toner", "transferUnit", "wasteInk", "wasteToner", "wasteWater", "wasteWax", "water" }; // // '_papplColorModeString()' - Return the keyword value associated with the IPP "print-color-mode" bit value. // const char * // O - IPP "print-color-mode" keyword value _papplColorModeString( pappl_color_mode_t value) // I - IPP "print-color-mode" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_color_modes)); } // // '_papplColorModeValue()' - Return the bit value associated with the IPP "print-color-mode" keyword value. // pappl_color_mode_t // O - IPP "print-color-mode" bit value _papplColorModeValue(const char *value) // I - IPP "print-color-mode" keyword value { return ((pappl_color_mode_t)_PAPPL_LOOKUP_VALUE(value, pappl_color_modes)); } // // '_papplContentString()' - Return the keyword associated with an IPP "print-content-optimize" bit value. // const char * // O - IPP "print-content-optimize" keyword value _papplContentString( pappl_content_t value) // I - IPP "print-content-optimize" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_contents)); } // // '_papplContentValue()' - Return the bit value associated with an IPP "print-content-optimize" keyword value. // pappl_content_t // O - IPP "print-content-optimize" bit value _papplContentValue(const char *value) // I - IPP "print-content-optimize" keyword value { return ((pappl_content_t)_PAPPL_LOOKUP_VALUE(value, pappl_contents)); } // // '_papplCreateMediaSize()' - Create a media-size collection. // ipp_t * // O - Collection value _papplCreateMediaSize( const char *size_name) // I - Media size name { pwg_media_t *pwg = pwgMediaForPWG(size_name); // Size information if (pwg) { ipp_t *col = ippNew(); // Collection value ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "x-dimension", pwg->width); ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "y-dimension", pwg->length); return (col); } else { return (NULL); } } // // '_papplIdentifyActionsString()' - Return the keyword value associated with the IPP "identify-actions" bit value. // const char * // O - IPP "identify-actions" keyword value _papplIdentifyActionsString( pappl_identify_actions_t v) // I - IPP "identify-actions" bit value { return (_PAPPL_LOOKUP_STRING(v, pappl_identify_actions)); } // // '_papplIdentifyActionsValue()' - Return the bit value associated with the IPP "identify-actions" keyword value. // pappl_identify_actions_t // O - IPP "identify-actions" bit value _papplIdentifyActionsValue( const char *s) // I - IPP "identify-actions" keyword value { return ((pappl_identify_actions_t)_PAPPL_LOOKUP_VALUE(s, pappl_identify_actions)); } // // '_papplKindString()' - Return the keyword value associated with the IPP "printer-kind" bit value. // const char * // O - IPP "printer-kind" keyword value _papplKindString( pappl_kind_t value) // I - IPP "printer-kind" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_kinds)); } // // '_papplLabelModeString()' - Return the keyword value associated with the IPP "label-mode-xxx" git value. // const char * // O - IPP "label-mode-xxx" keyword value _papplLabelModeString( pappl_label_mode_t value) // I - IPP "label-mode-xxx" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_label_modes)); } // // '_papplLabelModeValue()' - Return the bit value associated with the IPP "label-mode-xxx" keyword value. // pappl_label_mode_t // O - IPP "label-mode-xxx" bit value _papplLabelModeValue(const char *value) // I - IPP "label-mode-xxx" keyword value { return ((pappl_label_mode_t)_PAPPL_LOOKUP_VALUE(value, pappl_label_modes)); } // // '_papplMarkerColorString()' - Return the IPP "marker-colors" name string associated with the supply color enumeration value. // const char * // O - IPP "marker-colors" name string _papplMarkerColorString( pappl_supply_color_t value) // I - Supply color enumeration value { if (value >= PAPPL_SUPPLY_COLOR_NO_COLOR && value <= PAPPL_SUPPLY_COLOR_YELLOW) return (pappl_marker_colors[(int)value]); else return ("unknown"); } // // '_papplMarkerTypeString()' - Return the IPP "marker-types" keyword associated with the supply type enumeration value. // const char * // O - IPP "marker-types" keyword _papplMarkerTypeString( pappl_supply_type_t value) // I - Supply type enumeration value { if (value >= PAPPL_SUPPLY_TYPE_BANDING_SUPPLY && value <= PAPPL_SUPPLY_TYPE_WATER) return (pappl_marker_types[(int)value]); else return ("unknown"); } // // '_papplMediaColExport()' - Convert media values to a collection value. // ipp_t * // O - IPP "media-col" value _papplMediaColExport( pappl_pr_driver_data_t *driver_data,// I - Driver data pappl_media_col_t *media, // I - Media values bool db) // I - Create a "media-col-database" value? { ipp_t *col = NULL, // Collection value *size = _papplCreateMediaSize(media->size_name); // media-size value if (size) { col = ippNew(); ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-bottom-margin", media->bottom_margin); ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-margin", media->left_margin); if (driver_data->left_offset_supported[1] && !db) ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-left-offset", media->left_offset); ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-right-margin", media->right_margin); ippAddCollection(col, IPP_TAG_ZERO, "media-size", size); ippDelete(size); ippAddString(col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-size-name", NULL, media->size_name); if (driver_data->num_source > 0 && media->source[0]) ippAddString(col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-source", NULL, media->source); ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-margin", media->top_margin); if (driver_data->top_offset_supported[1] && !db) ippAddInteger(col, IPP_TAG_ZERO, IPP_TAG_INTEGER, "media-top-offset", media->top_offset); if (driver_data->tracking_supported && media->tracking) ippAddString(col, IPP_TAG_ZERO, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-tracking", NULL, _papplMediaTrackingString(media->tracking)); if (driver_data->num_type > 0 && media->type[0]) ippAddString(col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, "media-type", NULL, media->type); } return (col); } // // '_papplMediaColImport()' - Convert a collection value to media values. // void _papplMediaColImport( ipp_t *col, // I - IPP "media-col" value pappl_media_col_t *media) // O - Media values { ipp_attribute_t *size_name = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO), *x_dimension = ippFindAttribute(col, "media-size/x-dimension", IPP_TAG_INTEGER), *y_dimension = ippFindAttribute(col, "media-size/y-dimension", IPP_TAG_INTEGER), *bottom_margin = ippFindAttribute(col, "media-bottom-margin", IPP_TAG_INTEGER), *left_margin = ippFindAttribute(col, "media-left-margin", IPP_TAG_INTEGER), *left_offset = ippFindAttribute(col, "media-left-offset", IPP_TAG_INTEGER), *right_margin = ippFindAttribute(col, "media-right-margin", IPP_TAG_INTEGER), *source = ippFindAttribute(col, "media-source", IPP_TAG_ZERO), *top_margin = ippFindAttribute(col, "media-top-margin", IPP_TAG_INTEGER), *top_offset = ippFindAttribute(col, "media-top-offset", IPP_TAG_INTEGER), *tracking = ippFindAttribute(col, "media-tracking", IPP_TAG_ZERO), *type = ippFindAttribute(col, "media-type", IPP_TAG_ZERO); if (size_name) { const char *pwg_name = ippGetString(size_name, 0, NULL); pwg_media_t *pwg_media = pwgMediaForPWG(pwg_name); strlcpy(media->size_name, pwg_name, sizeof(media->size_name)); media->size_width = pwg_media->width; media->size_length = pwg_media->length; } else if (x_dimension && y_dimension) { pwg_media_t *pwg_media = pwgMediaForSize(ippGetInteger(x_dimension, 0), ippGetInteger(y_dimension, 0)); strlcpy(media->size_name, pwg_media->pwg, sizeof(media->size_name)); media->size_width = pwg_media->width; media->size_length = pwg_media->length; } if (bottom_margin) media->bottom_margin = ippGetInteger(bottom_margin, 0); if (left_margin) media->left_margin = ippGetInteger(left_margin, 0); if (left_offset) media->left_offset = ippGetInteger(left_offset, 0); if (right_margin) media->right_margin = ippGetInteger(right_margin, 0); if (source) strlcpy(media->source, ippGetString(source, 0, NULL), sizeof(media->source)); if (top_margin) media->top_margin = ippGetInteger(top_margin, 0); if (top_offset) media->top_offset = ippGetInteger(top_offset, 0); if (tracking) media->tracking = _papplMediaTrackingValue(ippGetString(tracking, 0, NULL)); if (type) strlcpy(media->type, ippGetString(type, 0, NULL), sizeof(media->type)); } // // '_papplMediaTrackingString()' - Return the keyword value associated with the IPP "media-tracking" bit value. // const char * // O - IPP "media-tracking" keyword value _papplMediaTrackingString( pappl_media_tracking_t value) // I - IPP "media-tracking" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_media_trackings)); } // // '_papplMediaTrackingValue()' - Return the bit value associated with the IPP "media-tracking" keyword value. // pappl_media_tracking_t // O - IPP "media-tracking" bit value _papplMediaTrackingValue( const char *value) // I - IPP "media-tracking" keyword value { return ((pappl_media_tracking_t)_PAPPL_LOOKUP_VALUE(value, pappl_media_trackings)); } // // '_papplPrinterReasonString()' - Return the keyword value associated with the IPP "printer-state-reasons" bit value. // const char * // O - IPP "printer-state-reasons" keyword value _papplPrinterReasonString( pappl_preason_t value) // I - IPP 'printer-state-reasons" bit value { if (value == PAPPL_PREASON_NONE) return ("none"); else return (_PAPPL_LOOKUP_STRING(value, pappl_preasons)); } // // '_papplPrinterReasonValue()' - Return the bit value associated with the IPP "printer-state-reasons" keyword value. // pappl_preason_t // O - IPP 'printer-state-reasons" bit value _papplPrinterReasonValue( const char *value) // I - IPP 'printer-state-reasons" keyword value { return ((pappl_preason_t)_PAPPL_LOOKUP_VALUE(value, pappl_preasons)); } // // '_papplRasterTypeString()' - Return the keyword associated with an IPP "pwg-raster-document-type-supported" bit value. // const char * // O - IPP "pwg-raster-document-type-supported" keyword value _papplRasterTypeString( pappl_raster_type_t value) // I - IPP "pwg-raster-document-type-supported" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_raster_types)); } // // '_papplScalingString()' - Return the keyword associated with an IPP "print-scaling" bit value. // const char * // O - IPP "print-scaling" keyword value _papplScalingString( pappl_scaling_t value) // I - IPP "print-scaling" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_scalings)); } // // '_papplScalingValue()' - Return the bit value associated with an IPP "print-scaling" keyword value. // pappl_scaling_t // O - IPP "print-scaling" bit value _papplScalingValue(const char *value) // I - IPP "print-scaling" keyword value { return ((pappl_scaling_t)_PAPPL_LOOKUP_VALUE(value, pappl_scalings)); } // // '_papplSidesString()' - Return the keyword associated with an IPP "sides" bit value. // const char * // O - IPP "sides" keyword value _papplSidesString(pappl_sides_t value) // I - IPP "sides" bit value { return (_PAPPL_LOOKUP_STRING(value, pappl_sides)); } // // '_papplSidesValue()' - Return the bit value associated with an IPP "sides" keyword value. // pappl_sides_t // O - IPP "sides" bit value _papplSidesValue(const char *value) // I - IPP "sides" keyword value { return ((pappl_sides_t)_PAPPL_LOOKUP_VALUE(value, pappl_sides)); } // // '_papplSupplyColorString()' - Return the IPP "printer-supply" color string associated with the supply color enumeration value. // const char * // O - IPP "printer-supply" color string _papplSupplyColorString( pappl_supply_color_t value) // I - Supply color enumeration value { if (value >= PAPPL_SUPPLY_COLOR_NO_COLOR && value <= PAPPL_SUPPLY_COLOR_YELLOW) return (pappl_supply_colors[(int)value]); else return ("unknown"); } // // '_papplSupplyTypeString()' - Return the IPP "printer-supply" type string associated with the supply type enumeration value. // const char * // O - IPP "printer-supply" type string _papplSupplyTypeString( pappl_supply_type_t value) // I - Supply type enumeration value { if (value >= PAPPL_SUPPLY_TYPE_BANDING_SUPPLY && value <= PAPPL_SUPPLY_TYPE_WATER) return (pappl_supply_types[(int)value]); else return ("unknown"); } pappl-1.0.3/pappl/printer-usb.c000066400000000000000000000433751403603036100163710ustar00rootroot00000000000000// // USB printer class support for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" #include #ifdef __linux # include # include # include #endif // __linux // // Local constants... // #ifdef __linux # define LINUX_USB_CONTROLLER "/sys/class/udc" # define LINUX_USB_GADGET "/sys/kernel/config/usb_gadget/g1" #endif // __linux // // Local functions... // #ifdef __linux static void disable_usb_printer(pappl_printer_t *printer); static bool enable_usb_printer(pappl_printer_t *printer); #endif // __linux // // '_papplPrinterRunUSB() ' - Run the USB printer thread. // void * // O - Thread exit status (not used) _papplPrinterRunUSB( pappl_printer_t *printer) // I - Printer { #ifdef __linux struct pollfd data; // USB printer gadget listener int count; // Number of file descriptors from poll() pappl_device_t *device = NULL; // Printer port data char buffer[8192]; // Print data buffer ssize_t bytes; // Bytes in buffer time_t status_time = 0; // Last port status update printer->usb_active = enable_usb_printer(printer); if (!printer->usb_active) return (NULL); if ((data.fd = open("/dev/g_printer0", O_RDWR | O_EXCL)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to open USB printer gadget: %s", strerror(errno)); return (NULL); } data.events = POLLIN | POLLRDNORM; papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Monitoring USB for incoming print jobs."); while (!printer->is_deleted && printer->system->is_running) { if ((count = poll(&data, 1, 1000)) < 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "USB poll failed: %s", strerror(errno)); if (printer->is_deleted || !printer->system->is_running) break; sleep(1); } else if (printer->is_deleted || !printer->system->is_running) { break; } else if (count > 0) { if (!device) { papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Starting USB print job."); while (!printer->is_deleted && (device = papplPrinterOpenDevice(printer)) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Waiting for USB access."); sleep(1); } if (printer->is_deleted || !printer->system->is_running) { papplPrinterCloseDevice(printer); break; } // Start looking for back-channel data and port status status_time = 0; data.events = POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM; } if ((time(NULL) - status_time) >= 1) { // Update port status once a second... pappl_preason_t reasons = papplDeviceGetStatus(device); // Current USB status bits unsigned char port_status = PRINTER_NOT_ERROR | PRINTER_SELECTED; // Current port status bits if (reasons & PAPPL_PREASON_OTHER) port_status &= ~PRINTER_NOT_ERROR; if (reasons & PAPPL_PREASON_MEDIA_EMPTY) port_status |= PRINTER_PAPER_EMPTY; if (reasons & PAPPL_PREASON_MEDIA_JAM) port_status |= 0x40; // Extension if (reasons & PAPPL_PREASON_COVER_OPEN) port_status |= 0x80; // Extension ioctl(data.fd, GADGET_SET_PRINTER_STATUS, (unsigned char)port_status); status_time = time(NULL); } if (data.revents & POLLRDNORM) { if ((bytes = read(data.fd, buffer, sizeof(buffer))) > 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Read %d bytes from USB port.", (int)bytes); papplDeviceWrite(device, buffer, (size_t)bytes); papplDeviceFlush(device); } else { if (bytes < 0) papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Read error from USB port: %s", strerror(errno)); papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Finishing USB print job."); papplPrinterCloseDevice(printer); device = NULL; } } if (data.revents & POLLWRNORM) { if ((bytes = papplDeviceRead(device, buffer, sizeof(buffer))) > 0) { papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Read %d bytes from printer.", (int)bytes); write(data.fd, buffer, (size_t)bytes); } } } else if (device) { papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Finishing USB print job."); papplPrinterCloseDevice(printer); device = NULL; // Stop doing back-channel data data.events = POLLIN | POLLRDNORM; } } if (device) { papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Finishing USB print job."); papplPrinterCloseDevice(printer); } papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "Disabling USB for incoming print jobs."); disable_usb_printer(printer); #else (void)printer; #endif // __linux return (NULL); } // // 'papplPrinterSetUSB()' - Set the USB vendor and product IDs for a printer. // // This function sets the USB vendor and product IDs for a printer as well as // specifying USB gadget options when the printer is registered with the USB // device controller. // // > Note: USB gadget functionality is currently only available when running // > on Linux with compatible hardware such as the Raspberry Pi. // void papplPrinterSetUSB( pappl_printer_t *printer, // I - Printer unsigned vendor_id, // I - USB vendor ID unsigned product_id, // I - USB product ID pappl_uoptions_t options, // I - USB gadget options const char *storagefile) // I - USB storage file, if any { if (printer) { printer->usb_vendor_id = (unsigned short)vendor_id; printer->usb_product_id = (unsigned short)product_id; printer->usb_options = options; free(printer->usb_storage); if (storagefile) printer->usb_storage = strdup(storagefile); else printer->usb_storage = NULL; } } #ifdef __linux // // 'disable_usb_printer()' - Disable the USB printer gadget module. // static void disable_usb_printer( pappl_printer_t *printer) // I - Printer { const char *gadget_dir = LINUX_USB_GADGET; // Gadget directory char filename[1024]; // Filename cups_file_t *fp; // File snprintf(filename, sizeof(filename), "%s/UDC", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) != NULL) { cupsFilePuts(fp, "\n"); cupsFileClose(fp); } else { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); } printer->usb_active = false; } // // 'enable_usb_printer()' - Configure and enable the USB printer gadget module. // static bool // O - `true` on success, `false` otherwise enable_usb_printer( pappl_printer_t *printer) // I - Printer { const char *gadget_dir = LINUX_USB_GADGET; // Gadget directory char filename[1024], // Filename destname[1024]; // Destination filename for symlinks cups_dir_t *dir; // Controller directory cups_dentry_t *dent; // Directory entry cups_file_t *fp; // File int num_devid; // Number of device ID values cups_option_t *devid; // Device ID values const char *val; // Value char mfg[256], // Manufacturer mdl[256], // Model name sn[256]; // Serial number // Get the information for this printer - vendor ID, product ID, etc. num_devid = papplDeviceParseID(printer->device_id, &devid); val = cupsGetOption("MANUFACTURER", num_devid, devid); if (!val) val = cupsGetOption("MFG", num_devid, devid); if (!val) val = cupsGetOption("MFR", num_devid, devid); if (val) strlcpy(mfg, val, sizeof(mfg)); else strlcpy(mfg, "Unknown", sizeof(mfg)); val = cupsGetOption("MODEL", num_devid, devid); if (!val) val = cupsGetOption("MDL", num_devid, devid); if (val) strlcpy(mdl, val, sizeof(mdl)); else strlcpy(mdl, "Printer", sizeof(mdl)); val = cupsGetOption("SERIALNUMBER", num_devid, devid); if (!val) val = cupsGetOption("SN", num_devid, devid); if (!val) val = cupsGetOption("SER", num_devid, devid); if (!val) val = cupsGetOption("SERN", num_devid, devid); if (!val && (val = strstr(printer->device_uri, "?serial=")) != NULL) val += 8; if (val) strlcpy(sn, val, sizeof(sn)); else strlcpy(sn, "0", sizeof(sn)); cupsFreeOptions(num_devid, devid); // Modern Linux kernels support USB gadgets through the configfs interface. // PAPPL takes control of this interface, so if you need (for example) a // serial gadget in addition to the printer gadget you need to specify that // with a call to papplPrinterSetUSB. // // The configfs interface lives under "/sys/kernel/config/usb_gadget/". The // available USB Device Controllers can be found under "/sys/class/udc". We // currently assume there will only be one of those and will expand the USB // gadget interface later as needed. // // The typical directory structure looks like this: // // g1/ // idVendor (usb_vendor ID as a hex number, e.g. "0x12CD") // idProduct (usb product ID as a hex number, e.g. "0x34AB") // strings/0x409/ // manufacturer (manufacturer name string) // product (model name string) // serialnumber (serial number string) // configs/c.1/ // symlink to functions/printer.g_printer0 // functions/printer.g_printer0 // pnp_string (IEEE-1284 device ID string) // UDC (first entry from /sys/class/udc) // Create the gadget configuration files and directories... if (mkdir(gadget_dir, 0777) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget directory '%s': %s", gadget_dir, strerror(errno)); return (false); } snprintf(filename, sizeof(filename), "%s/idVendor", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); return (false); } cupsFilePrintf(fp, "0x%04X\n", printer->usb_vendor_id); cupsFileClose(fp); snprintf(filename, sizeof(filename), "%s/idProduct", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); return (false); } cupsFilePrintf(fp, "0x%04X\n", printer->usb_product_id); cupsFileClose(fp); snprintf(filename, sizeof(filename), "%s/strings/0x409", gadget_dir); if (mkdir(filename, 0777) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget directory '%s': %s", filename, strerror(errno)); return (false); } snprintf(filename, sizeof(filename), "%s/strings/0x409/manufacturer", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); return (false); } cupsFilePrintf(fp, "%s\n", mfg); cupsFileClose(fp); snprintf(filename, sizeof(filename), "%s/strings/0x409/product", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); return (false); } cupsFilePrintf(fp, "%s\n", mdl); cupsFileClose(fp); snprintf(filename, sizeof(filename), "%s/strings/0x409/serialnumber", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); return (false); } cupsFilePrintf(fp, "%s\n", sn); cupsFileClose(fp); snprintf(filename, sizeof(filename), "%s/configs/c.1", gadget_dir); if (mkdir(filename, 0777) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget directory '%s': %s", filename, strerror(errno)); return (false); } snprintf(filename, sizeof(filename), "%s/functions/printer.g_printer0", gadget_dir); if (mkdir(filename, 0777) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget directory '%s': %s", filename, strerror(errno)); return (false); } snprintf(filename, sizeof(filename), "%s/functions/printer.g_printer0/pnp_string", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); return (false); } cupsFilePrintf(fp, "%s\n", printer->device_id); cupsFileClose(fp); snprintf(filename, sizeof(filename), "%s/functions/printer.g_printer0/q_len", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); } cupsFilePuts(fp, "10\n"); cupsFileClose(fp); snprintf(filename, sizeof(filename), "%s/functions/printer.g_printer0", gadget_dir); snprintf(destname, sizeof(destname), "%s/configs/c.1/printer.g_printer0", gadget_dir); if (symlink(filename, destname) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget symlink '%s': %s", destname, strerror(errno)); return (false); } // Add optional gadgets... if (printer->usb_options & PAPPL_UOPTIONS_ETHERNET) { // Standard USB-Ethernet interface... snprintf(filename, sizeof(filename), "%s/functions/ncm.usb0", gadget_dir); if (mkdir(filename, 0777) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget directory '%s': %s", filename, strerror(errno)); return (false); } snprintf(destname, sizeof(destname), "%s/configs/c.1/ncm.usb0", gadget_dir); if (symlink(filename, destname) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget symlink '%s': %s", destname, strerror(errno)); return (false); } } if (printer->usb_options & PAPPL_UOPTIONS_SERIAL) { // Standard serial port... snprintf(filename, sizeof(filename), "%s/functions/acm.ttyGS0", gadget_dir); if (mkdir(filename, 0777) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget directory '%s': %s", filename, strerror(errno)); return (false); } snprintf(destname, sizeof(destname), "%s/configs/c.1/acm.ttyGS0", gadget_dir); if (symlink(filename, destname) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget symlink '%s': %s", destname, strerror(errno)); return (false); } } if ((printer->usb_options & PAPPL_UOPTIONS_STORAGE) && printer->usb_storage) { // Standard USB mass storage device... snprintf(filename, sizeof(filename), "%s/functions/mass_storage.0", gadget_dir); if (mkdir(filename, 0777) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget directory '%s': %s", filename, strerror(errno)); return (false); } snprintf(filename, sizeof(filename), "%s/functions/mass_storage.0/lun.0/file", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); } cupsFilePrintf(fp, "%s\n", printer->usb_storage); cupsFileClose(fp); if (printer->usb_options & PAPPL_UOPTIONS_STORAGE_READONLY) { snprintf(filename, sizeof(filename), "%s/functions/mass_storage.0/lun.0/ro", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); } cupsFilePuts(fp, "1\n"); cupsFileClose(fp); } if (printer->usb_options & PAPPL_UOPTIONS_STORAGE_REMOVABLE) { snprintf(filename, sizeof(filename), "%s/functions/mass_storage.0/lun.0/removable", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_WARN, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); } cupsFilePuts(fp, "1\n"); cupsFileClose(fp); } snprintf(filename, sizeof(filename), "%s/functions/mass_storage.0", gadget_dir); snprintf(destname, sizeof(destname), "%s/configs/c.1/mass_storage.0", gadget_dir); if (symlink(filename, destname) && errno != EEXIST) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget symlink '%s': %s", destname, strerror(errno)); return (false); } } // Then assign this configuration to the first USB device controller if ((dir = cupsDirOpen(LINUX_USB_CONTROLLER)) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to find USB device controller in '%s': %s", LINUX_USB_CONTROLLER, strerror(errno)); return (false); } while ((dent = cupsDirRead(dir)) != NULL) { if (dent->filename[0] != '.' && dent->filename[0]) break; } if (!dent) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "No USB device controller in '%s': %s", LINUX_USB_CONTROLLER, strerror(errno)); cupsDirClose(dir); return (false); } papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Using UDC '%s' for USB gadgets.", dent->filename); snprintf(filename, sizeof(filename), "%s/UDC", gadget_dir); if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB gadget file '%s': %s", filename, strerror(errno)); cupsDirClose(dir); return (false); } cupsFilePrintf(fp, "%s\n", dent->filename); cupsFileClose(fp); cupsDirClose(dir); papplLogPrinter(printer, PAPPL_LOGLEVEL_INFO, "USB printer gadget configured."); return (true); } #endif // __linux pappl-1.0.3/pappl/printer-webif.c000066400000000000000000001652241403603036100166720ustar00rootroot00000000000000// // Printer web interface functions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local functions... // static void job_cb(pappl_job_t *job, pappl_client_t *client); static char *localize_keyword(const char *attrname, const char *keyword, char *buffer, size_t bufsize); static char *localize_media(pappl_media_col_t *media, bool include_source, char *buffer, size_t bufsize); static void media_chooser(pappl_client_t *client, pappl_pr_driver_data_t *driver_data, const char *title, const char *name, pappl_media_col_t *media); static char *time_string(time_t tv, char *buffer, size_t bufsize); static void job_pager(pappl_client_t *client, pappl_printer_t *printer, int job_index, int limit); // // '_papplPrinterWebCancelAllJobs()' - Cancel all printer jobs. // void _papplPrinterWebCancelAllJobs( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { const char *status = NULL; // Status message, if any if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variables cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { char path[1024]; // Resource path papplPrinterCancelAllJobs(printer); snprintf(path, sizeof(path), "%s/jobs", printer->uriname); papplClientRespondRedirect(client, HTTP_STATUS_FOUND, path); cupsFreeOptions(num_form, form); return; } cupsFreeOptions(num_form, form); } papplClientHTMLPrinterHeader(client, printer, "Cancel All Jobs", 0, NULL, NULL); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, " "); if (papplPrinterGetNumberOfActiveJobs(printer) > 0) { papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n"); papplPrinterIterateActiveJobs(printer, (pappl_job_cb_t)job_cb, client, 1, 0); papplClientHTMLPuts(client, " \n" "
Job #NameOwnerPages CompletedStatus
\n"); } else papplClientHTMLPuts(client, "

No jobs in history.

\n"); papplClientHTMLFooter(client); } // // '_papplPrinterWebCancelJob()' - Cancel a job. // void _papplPrinterWebCancelJob( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { int job_id = 0; // Job ID to cancel pappl_job_t *job; // Job to cancel const char *status = NULL; // Status message, if any int num_form; // Number of form variables cups_option_t *form; // Form variables const char *value; // Value of form variable if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_GET) { if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid GET data."; } else if ((value = cupsGetOption("job-id", num_form, form)) != NULL) { char *end; // End of value job_id = (int)strtol(value, &end, 10); if (errno == ERANGE || *end) { job_id = 0; status = "Invalid job ID."; } } cupsFreeOptions(num_form, form); } else if (client->operation == HTTP_STATE_POST) { if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else if ((value = cupsGetOption("job-id", num_form, form)) != NULL) { // Get the job to cancel char *end; // End of value job_id = (int)strtol(value, &end, 10); if (errno == ERANGE || *end) { status = "Invalid job ID."; } else if ((job = papplPrinterFindJob(printer, job_id)) != NULL) { char path[1024]; // Resource path papplJobCancel(job); snprintf(path, sizeof(path), "%s/jobs", printer->uriname); papplClientRespondRedirect(client, HTTP_STATUS_FOUND, path); cupsFreeOptions(num_form, form); return; } else { status = "Invalid Job ID."; } cupsFreeOptions(num_form, form); } else { status = "Invalid form submission."; } } papplClientHTMLPrinterHeader(client, printer, "Cancel Job", 0, NULL, NULL); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); if (job_id) { papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPrintf(client, " \n", job_id); } papplClientHTMLFooter(client); } // // '_papplPrinterWebConfig()' - Show the printer configuration web page. // void _papplPrinterWebConfig( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { const char *status = NULL; // Status message, if any char dns_sd_name[64], // DNS-SD name location[128], // Location geo_location[128], // Geo-location latitude organization[128], // Organization org_unit[128]; // Organizational unit pappl_contact_t contact; // Contact info if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { _papplPrinterWebConfigFinalize(printer, num_form, form); if (printer->system->options & PAPPL_SOPTIONS_MULTI_QUEUE) _papplPrinterWebDelete(client, printer); status = "Changes saved."; } cupsFreeOptions(num_form, form); } papplClientHTMLPrinterHeader(client, printer, "Configuration", 0, NULL, NULL); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); _papplClientHTMLInfo(client, true, papplPrinterGetDNSSDName(printer, dns_sd_name, sizeof(dns_sd_name)), papplPrinterGetLocation(printer, location, sizeof(location)), papplPrinterGetGeoLocation(printer, geo_location, sizeof(geo_location)), papplPrinterGetOrganization(printer, organization, sizeof(organization)), papplPrinterGetOrganizationalUnit(printer, org_unit, sizeof(org_unit)), papplPrinterGetContact(printer, &contact)); papplClientHTMLPrinterFooter(client); } // // '_papplPrinterWebConfigFinalize()' - Save the changes to the printer configuration. // void _papplPrinterWebConfigFinalize( pappl_printer_t *printer, // I - Printer int num_form, // I - Number of form variables cups_option_t *form) // I - Form variables { const char *value, // Form value *geo_lat, // Geo-location latitude *geo_lon, // Geo-location longitude *contact_name, // Contact name *contact_email, // Contact email *contact_tel; // Contact telephone number if ((value = cupsGetOption("dns_sd_name", num_form, form)) != NULL) papplPrinterSetDNSSDName(printer, *value ? value : NULL); if ((value = cupsGetOption("location", num_form, form)) != NULL) papplPrinterSetLocation(printer, *value ? value : NULL); geo_lat = cupsGetOption("geo_location_lat", num_form, form); geo_lon = cupsGetOption("geo_location_lon", num_form, form); if (geo_lat && geo_lon) { char uri[1024]; // "geo:" URI if (*geo_lat && *geo_lon) { snprintf(uri, sizeof(uri), "geo:%g,%g", strtod(geo_lat, NULL), strtod(geo_lon, NULL)); papplPrinterSetGeoLocation(printer, uri); } else papplPrinterSetGeoLocation(printer, NULL); } if ((value = cupsGetOption("organization", num_form, form)) != NULL) papplPrinterSetOrganization(printer, *value ? value : NULL); if ((value = cupsGetOption("organizational_unit", num_form, form)) != NULL) papplPrinterSetOrganizationalUnit(printer, *value ? value : NULL); contact_name = cupsGetOption("contact_name", num_form, form); contact_email = cupsGetOption("contact_email", num_form, form); contact_tel = cupsGetOption("contact_telephone", num_form, form); if (contact_name || contact_email || contact_tel) { pappl_contact_t contact; // Contact info memset(&contact, 0, sizeof(contact)); if (contact_name) strlcpy(contact.name, contact_name, sizeof(contact.name)); if (contact_email) strlcpy(contact.email, contact_email, sizeof(contact.email)); if (contact_tel) strlcpy(contact.telephone, contact_tel, sizeof(contact.telephone)); papplPrinterSetContact(printer, &contact); } } // // '_papplPrinterWebDefaults()' - Show the printer defaults web page. // void _papplPrinterWebDefaults( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { int i, j; // Looping vars pappl_pr_driver_data_t data; // Driver data const char *keyword; // Current keyword char text[256]; // Localized text for keyword const char *status = NULL; // Status message, if any bool show_source = false; // Show the media source? static const char * const orients[] = // orientation-requested strings { "Portrait", "Landscape", "Reverse Landscape", "Reverse Portrait", "Auto" }; static const char * const orient_svgs[] = { // orientation-requested images "%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='24' viewBox='0 0 18 24'%3e%3crect fill='rgba(255,255,255,.5)' stroke='currentColor' stroke-width='1' x='0' y='0' width='18' height='24' rx='5' ry='5'/%3e%3ctext x='3' y='18' font-size='18' fill='currentColor' rotate='0'%3eA%3c/text%3e%3c/svg%3e", "%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='24' viewBox='0 0 18 24'%3e%3crect fill='rgba(255,255,255,.5)' stroke='currentColor' stroke-width='1' x='0' y='0' width='18' height='24' rx='5' ry='5'/%3e%3ctext x='15' y='19' font-size='18' fill='currentColor' rotate='-90'%3eA%3c/text%3e%3c/svg%3e", "%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='24' viewBox='0 0 18 24'%3e%3crect fill='rgba(255,255,255,.5)' stroke='currentColor' stroke-width='1' x='0' y='0' width='18' height='24' rx='5' ry='5'/%3e%3ctext x='3' y='6' font-size='18' fill='currentColor' rotate='90'%3eA%3c/text%3e%3c/svg%3e", "%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='24' viewBox='0 0 18 24'%3e%3crect fill='rgba(255,255,255,.5)' stroke='currentColor' stroke-width='1' x='0' y='0' width='18' height='24' rx='5' ry='5'/%3e%3ctext x='15' y='7' font-size='18' fill='currentColor' rotate='180'%3eA%3c/text%3e%3c/svg%3e", "%3csvg xmlns='http://www.w3.org/2000/svg' width='18' height='24' viewBox='0 0 18 24'%3e%3crect fill='rgba(255,255,255,.5)' stroke='currentColor' stroke-width='1' x='0' y='0' width='18' height='24' rx='5' ry='5'/%3e%3ctext x='5' y='18' font-size='18' fill='currentColor' rotate='0'%3e?%3c/text%3e%3c/svg%3e" }; if (!papplClientHTMLAuthorize(client)) return; papplPrinterGetDriverData(printer, &data); if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables int num_vendor = 0; // Number of vendor options cups_option_t *vendor = NULL; // Vendor options if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { const char *value; // Value of form variable char *end; // End of value if ((value = cupsGetOption("orientation-requested", num_form, form)) != NULL) { data.orient_default = (ipp_orient_t)strtol(value, &end, 10); if (errno == ERANGE || *end || data.orient_default < IPP_ORIENT_PORTRAIT || data.orient_default > IPP_ORIENT_NONE) data.orient_default = IPP_ORIENT_PORTRAIT; } if ((value = cupsGetOption("output-bin", num_form, form)) != NULL) { for (i = 0; i < data.num_bin; i ++) { if (!strcmp(data.bin[i], value)) { data.bin_default = i; break; } } } if ((value = cupsGetOption("print-color-mode", num_form, form)) != NULL) data.color_default = _papplColorModeValue(value); if ((value = cupsGetOption("print-content-optimize", num_form, form)) != NULL) data.content_default = _papplContentValue(value); if ((value = cupsGetOption("print-darkness", num_form, form)) != NULL) { data.darkness_configured = (int)strtol(value, &end, 10); if (errno == ERANGE || *end || data.darkness_configured < 0 || data.darkness_configured > 100) data.darkness_configured = 50; } if ((value = cupsGetOption("print-quality", num_form, form)) != NULL) data.quality_default = (ipp_quality_t)ippEnumValue("print-quality", value); if ((value = cupsGetOption("print-scaling", num_form, form)) != NULL) data.scaling_default = _papplScalingValue(value); if ((value = cupsGetOption("print-speed", num_form, form)) != NULL) { data.speed_default = (int)strtol(value, &end, 10) * 2540; if (errno == ERANGE || *end || data.speed_default < 0 || data.speed_default > data.speed_supported[1]) data.speed_default = 0; } if ((value = cupsGetOption("sides", num_form, form)) != NULL) data.sides_default = _papplSidesValue(value); if ((value = cupsGetOption("printer-resolution", num_form, form)) != NULL) { if (sscanf(value, "%dx%ddpi", &data.x_default, &data.y_default) == 1) data.y_default = data.x_default; } if ((value = cupsGetOption("media-source", num_form, form)) != NULL) { for (i = 0; i < data.num_source; i ++) { if (!strcmp(value, data.source[i])) { data.media_default = data.media_ready[i]; break; } } } for (i = 0; i < data.num_vendor; i ++) { char supattr[128]; // xxx-supported snprintf(supattr, sizeof(supattr), "%s-supported", data.vendor[i]); if ((value = cupsGetOption(data.vendor[i], num_form, form)) != NULL) num_vendor = cupsAddOption(data.vendor[i], value, num_vendor, &vendor); else if (ippFindAttribute(printer->driver_attrs, supattr, IPP_TAG_BOOLEAN)) num_vendor = cupsAddOption(data.vendor[i], "false", num_vendor, &vendor); } if (papplPrinterSetDriverDefaults(printer, &data, num_vendor, vendor)) status = "Changes saved."; else status = "Bad printer defaults."; cupsFreeOptions(num_vendor, vendor); } cupsFreeOptions(num_form, form); } papplClientHTMLPrinterHeader(client, printer, "Printing Defaults", 0, NULL, NULL); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, " \n" " \n"); // media-col-default papplClientHTMLPuts(client, " \n", printer->uriname); // orientation-requested-default papplClientHTMLPuts(client, " \n"); // print-color-mode-default papplClientHTMLPuts(client, " \n"); if (data.sides_supported && data.sides_supported != PAPPL_SIDES_ONE_SIDED) { // sides-default papplClientHTMLPuts(client, " \n"); } // output-bin-default if (data.num_bin > 0) { papplClientHTMLPuts(client, " \n"); } // print-quality-default papplClientHTMLPuts(client, " \n"); // print-darkness-configured if (data.darkness_supported) { papplClientHTMLPuts(client, " \n"); } // print-speed-default if (data.speed_supported[1]) { papplClientHTMLPuts(client, " \n"); } // print-content-optimize-default papplClientHTMLPuts(client, " \n"); // print-scaling-default papplClientHTMLPuts(client, " \n"); // printer-resolution-default papplClientHTMLPuts(client, " \n"); // Vendor options pthread_rwlock_rdlock(&printer->rwlock); for (i = 0; i < data.num_vendor; i ++) { char defname[128], // xxx-default name defvalue[1024], // xxx-default value supname[128]; // xxx-supported name ipp_attribute_t *attr; // Attribute int count; // Number of values snprintf(defname, sizeof(defname), "%s-default", data.vendor[i]); snprintf(supname, sizeof(defname), "%s-supported", data.vendor[i]); if ((attr = ippFindAttribute(printer->driver_attrs, defname, IPP_TAG_ZERO)) != NULL) ippAttributeString(attr, defvalue, sizeof(defvalue)); else defvalue[0] = '\0'; if ((attr = ippFindAttribute(printer->driver_attrs, supname, IPP_TAG_ZERO)) != NULL) { count = ippGetCount(attr); papplClientHTMLPrintf(client, " \n"); } } pthread_rwlock_unlock(&printer->rwlock); papplClientHTMLPuts(client, " \n" " \n" "
Media:"); if (data.num_source > 1) { papplClientHTMLPuts(client, ""); } else papplClientHTMLEscape(client, localize_media(data.media_ready, false, text, sizeof(text)), 0); papplClientHTMLPrintf(client, " Configure Media
Orientation:"); for (i = IPP_ORIENT_PORTRAIT; i <= IPP_ORIENT_NONE; i ++) { papplClientHTMLPrintf(client, " ", i, data.orient_default == (ipp_orient_t)i ? " checked" : "", orient_svgs[i - IPP_ORIENT_PORTRAIT], orients[i - IPP_ORIENT_PORTRAIT]); } papplClientHTMLPuts(client, "
Print Mode:"); if (data.color_supported == (PAPPL_COLOR_MODE_AUTO | PAPPL_COLOR_MODE_MONOCHROME) || data.color_supported == (PAPPL_COLOR_MODE_AUTO | PAPPL_COLOR_MODE_MONOCHROME | PAPPL_COLOR_MODE_AUTO_MONOCHROME)) { papplClientHTMLPuts(client, "B&W"); } else { for (i = PAPPL_COLOR_MODE_AUTO; i <= PAPPL_COLOR_MODE_PROCESS_MONOCHROME; i *= 2) { if ((data.color_supported & (pappl_color_mode_t)i) && i != PAPPL_COLOR_MODE_AUTO_MONOCHROME) { keyword = _papplColorModeString((pappl_color_mode_t)i); papplClientHTMLPrintf(client, " ", keyword, (pappl_color_mode_t)i == data.color_default ? " checked" : "", localize_keyword("print-color-mode", keyword, text, sizeof(text))); } } } papplClientHTMLPuts(client, "
2-Sided Printing:"); for (i = PAPPL_SIDES_ONE_SIDED; i <= PAPPL_SIDES_TWO_SIDED_SHORT_EDGE; i *= 2) { if (data.sides_supported & (pappl_sides_t)i) { keyword = _papplSidesString((pappl_sides_t)i); papplClientHTMLPrintf(client, " ", keyword, (pappl_sides_t)i == data.sides_default ? " checked" : "", localize_keyword("sides", keyword, text, sizeof(text))); } } papplClientHTMLPuts(client, "
Output Tray:"); if (data.num_bin > 1) { papplClientHTMLPuts(client, ""); } else { papplClientHTMLPrintf(client, "%s", localize_keyword("output-bin", data.bin[data.bin_default], text, sizeof(text))); } papplClientHTMLPuts(client, "
Print Quality:"); for (i = IPP_QUALITY_DRAFT; i <= IPP_QUALITY_HIGH; i ++) { keyword = ippEnumString("print-quality", i); papplClientHTMLPrintf(client, " ", keyword, (ipp_quality_t)i == data.quality_default ? " checked" : "", localize_keyword("print-quality", keyword, text, sizeof(text))); } papplClientHTMLPuts(client, "
Print Darkness:
Print Speed:
Optimize For:
Scaling:
Resolution:"); if (data.num_resolution == 1) { if (data.x_resolution[0] != data.y_resolution[0]) papplClientHTMLPrintf(client, "%dx%ddpi", data.x_resolution[0], data.y_resolution[0]); else papplClientHTMLPrintf(client, "%ddpi", data.x_resolution[0]); } else { papplClientHTMLPuts(client, ""); } papplClientHTMLPuts(client, "
%s:", data.vendor[i]); switch (ippGetValueTag(attr)) { case IPP_TAG_BOOLEAN : papplClientHTMLPrintf(client, "", data.vendor[i], !strcmp(defvalue, "true") ? " checked" : ""); break; case IPP_TAG_INTEGER : papplClientHTMLPrintf(client, ""); break; case IPP_TAG_RANGE : { int upper, lower = ippGetRange(attr, 0, &upper); // Range papplClientHTMLPrintf(client, "", data.vendor[i], lower, upper, defvalue); } break; case IPP_TAG_KEYWORD : papplClientHTMLPrintf(client, ""); break; default : papplClientHTMLPuts(client, "Unsupported value syntax."); break; } papplClientHTMLPuts(client, "
" " \n"); papplClientHTMLPrinterFooter(client); } // // '_papplPrinterWebDelete()' - Show the printer delete confirmation web page. // void _papplPrinterWebDelete( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { const char *status = NULL; // Status message, if any if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variables cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else if (printer->processing_job) { // Printer is processing a job... status = "Printer is currently active."; } else { if (!printer->is_deleted) { papplPrinterDelete(printer); printer = NULL; } papplClientRespondRedirect(client, HTTP_STATUS_FOUND, "/"); cupsFreeOptions(num_form, form); return; } cupsFreeOptions(num_form, form); } papplClientHTMLPrinterHeader(client, printer, "Delete Printer", 0, NULL, NULL); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client," "); papplClientHTMLFooter(client); } // // '_papplPrinterWebHome()' - Show the printer home page. // void _papplPrinterWebHome( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { const char *status = NULL; // Status text, if any ipp_pstate_t printer_state; // Printer state char edit_path[1024]; // Edit configuration URL const int limit = 20; // Jobs per page int job_index = 1; // Job index // Save current printer state... printer_state = papplPrinterGetState(printer); // Handle POSTs to print a test page... if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables const char *action; // Form action if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else if ((action = cupsGetOption("action", num_form, form)) == NULL) { status = "Missing action."; } else if (!strcmp(action, "identify-printer")) { if (printer->driver_data.identify_supported && printer->driver_data.identify_cb) { (printer->driver_data.identify_cb)(printer, printer->driver_data.identify_supported, "Hello."); status = "Printer identified."; } else { status = "Unable to identify printer."; } } else if (!strcmp(action, "print-test-page")) { pappl_job_t *job; // New job const char *filename, // Test Page filename *username; // Username char buffer[1024]; // File Buffer // Get the testfile to print, if any... if (printer->driver_data.testpage_cb) filename = (printer->driver_data.testpage_cb)(printer, buffer, sizeof(buffer)); else filename = NULL; if (filename) { // Have a file to print, so create a job and print it... if (client->username[0]) username = client->username; else username = "guest"; if (access(filename, R_OK)) { status = "Unable to access test print file."; } else if ((job = _papplJobCreate(printer, 0, username, NULL, "Test Page", NULL)) == NULL) { status = "Unable to create test print job."; } else { // Submit the job for processing... _papplJobSubmitFile(job, filename); status = "Test page printed."; printer_state = IPP_PSTATE_PROCESSING; } } else { status = "Test page printed."; printer_state = IPP_PSTATE_PROCESSING; } } else status = "Unknown action."; cupsFreeOptions(num_form, form); } // Show status... papplClientHTMLPrinterHeader(client, printer, NULL, printer_state == IPP_PSTATE_PROCESSING ? 10 : 0, NULL, NULL); papplClientHTMLPuts(client, "
\n" "
\n"); _papplPrinterWebIteratorCallback(printer, client); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); snprintf(edit_path, sizeof(edit_path), "%s/config", printer->uriname); papplClientHTMLPrintf(client, "

Configuration Change

\n", client->host_field, client->host_port, edit_path); _papplClientHTMLPutLinks(client, printer->links, PAPPL_LOPTIONS_CONFIGURATION); _papplClientHTMLInfo(client, false, printer->dns_sd_name, printer->location, printer->geo_location, printer->organization, printer->org_unit, &printer->contact); if (!(printer->system->options & PAPPL_SOPTIONS_MULTI_QUEUE)) _papplSystemWebSettings(client); papplClientHTMLPrintf(client, "
\n" "
\n" "

Jobs", printer->uriname); if (papplPrinterGetNumberOfJobs(printer) > 0) { if (cupsArrayCount(printer->active_jobs) > 0) papplClientHTMLPrintf(client, " Cancel All Jobs

\n", client->host_field, client->host_port, printer->uriname); else papplClientHTMLPuts(client, "\n"); _papplClientHTMLPutLinks(client, printer->links, PAPPL_LOPTIONS_JOB); job_pager(client, printer, job_index, limit); papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n"); papplPrinterIterateAllJobs(printer, (pappl_job_cb_t)job_cb, client, job_index, limit); papplClientHTMLPuts(client, " \n" "
Job #NameOwnerPagesStatus
\n"); job_pager(client, printer, job_index, limit); } else { papplClientHTMLPuts(client, "\n"); _papplClientHTMLPutLinks(client, printer->links, PAPPL_LOPTIONS_JOB); papplClientHTMLPuts(client, "

No jobs in history.

\n"); } papplClientHTMLPrinterFooter(client); } // // '_papplPrinterWebIteratorCallback()' - Show the printer status. // void _papplPrinterWebIteratorCallback( pappl_printer_t *printer, // I - Printer pappl_client_t *client) // I - Client { int i; // Looping var pappl_preason_t reason, // Current reason printer_reasons;// Printer state reasons ipp_pstate_t printer_state; // Printer state int printer_jobs; // Number of queued jobs char uri[256]; // Form URI static const char * const states[] = // State strings { "Idle", "Printing", "Stopped" }; static const char * const reasons[] = // Reason strings { "Other", "Cover Open", "Tray Missing", "Out of Ink", "Low Ink", "Waste Tank Almost Full", "Waste Tank Full", "Media Empty", "Media Jam", "Media Low", "Media Needed", "Too Many Jobs", "Out of Toner", "Low Toner" }; printer_jobs = papplPrinterGetNumberOfActiveJobs(printer); printer_state = papplPrinterGetState(printer); printer_reasons = papplPrinterGetReasons(printer); snprintf(uri, sizeof(uri), "%s/", printer->uriname); if (!strcmp(client->uri, "/") && (client->system->options & PAPPL_SOPTIONS_MULTI_QUEUE)) papplClientHTMLPrintf(client, "

%s Delete

\n", printer->uriname, printer->name, client->host_field, client->host_port, printer->uriname); else papplClientHTMLPuts(client, "

Status

\n"); papplClientHTMLPrintf(client, "

%s, %d %s", ippEnumString("printer-state", (int)printer_state), printer->uriname, states[printer_state - IPP_PSTATE_IDLE], printer_jobs, printer_jobs == 1 ? "job" : "jobs"); for (i = 0, reason = PAPPL_PREASON_OTHER; reason <= PAPPL_PREASON_TONER_LOW; i ++, reason *= 2) { if (printer_reasons & reason) papplClientHTMLPrintf(client, ", %s", reasons[i]); } if (strcmp(printer->name, printer->driver_data.make_and_model)) papplClientHTMLPrintf(client, ".
%s

\n", printer->driver_data.make_and_model); else papplClientHTMLPuts(client, ".

\n"); papplClientHTMLPuts(client, "
"); _papplClientHTMLPutLinks(client, printer->links, PAPPL_LOPTIONS_STATUS); if (printer->driver_data.has_supplies) papplClientHTMLPrintf(client, " Supplies", printer->uriname); if (printer->driver_data.identify_supported) { papplClientHTMLStartForm(client, uri, false); papplClientHTMLPrintf(client, ""); } if (printer->driver_data.testpage_cb) { papplClientHTMLStartForm(client, uri, false); papplClientHTMLPrintf(client, ""); } if (strcmp(client->uri, "/") && (client->system->options & PAPPL_SOPTIONS_MULTI_QUEUE)) papplClientHTMLPrintf(client, " Delete Printer", client->host_field, client->host_port, printer->uriname); papplClientHTMLPuts(client, "
\n"); } // // '_papplPrinterWebJobs()' - Show the printer jobs web page. // void _papplPrinterWebJobs( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { ipp_pstate_t printer_state; // Printer state int job_index = 1, // Job index limit = 20; // Jobs per page if (!papplClientHTMLAuthorize(client)) return; printer_state = papplPrinterGetState(printer); if (client->operation == HTTP_STATE_GET) { cups_option_t *form = NULL; // Form variables int num_form = papplClientGetForm(client, &form); // Number of form variables const char *value = NULL; // Value of form variable if ((value = cupsGetOption("job-index", num_form, form)) != NULL) job_index = (int)strtol(value, NULL, 10); cupsFreeOptions(num_form, form); } if (cupsArrayCount(printer->active_jobs) > 0) { char url[1024]; // URL for Cancel All Jobs httpAssembleURIf(HTTP_URI_CODING_ALL, url, sizeof(url), "https", NULL, client->host_field, client->host_port, "%s/cancelall", printer->uriname); papplClientHTMLPrinterHeader(client, printer, "Jobs", printer_state == IPP_PSTATE_PROCESSING ? 10 : 0, "Cancel All Jobs", url); } else { papplClientHTMLPrinterHeader(client, printer, "Jobs", printer_state == IPP_PSTATE_PROCESSING ? 10 : 0, NULL, NULL); } if (papplPrinterGetNumberOfJobs(printer) > 0) { job_pager(client, printer, job_index, limit); papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n"); papplPrinterIterateAllJobs(printer, (pappl_job_cb_t)job_cb, client, job_index, limit); papplClientHTMLPuts(client, " \n" "
Job #NameOwnerPages CompletedStatus
\n"); job_pager(client, printer, job_index, limit); } else papplClientHTMLPuts(client, "

No jobs in history.

\n"); papplClientHTMLPrinterFooter(client); } // // '_papplPrinterWebMedia()' - Show the printer media web page. // void _papplPrinterWebMedia( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { int i; // Looping var pappl_pr_driver_data_t data; // Driver data char name[32], // Prefix (readyN) text[256]; // Localized media-souce name const char *status = NULL; // Status message, if any if (!papplClientHTMLAuthorize(client)) return; papplPrinterGetDriverData(printer, &data); if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { pwg_media_t *pwg = NULL; // PWG media info pappl_media_col_t *ready; // Current ready media const char *value, // Value of form variable *custom_width, // Custom media width *custom_length; // Custom media length memset(data.media_ready, 0, sizeof(data.media_ready)); for (i = 0, ready = data.media_ready; i < data.num_source; i ++, ready ++) { // size snprintf(name, sizeof(name), "ready%d-size", i); if ((value = cupsGetOption(name, num_form, form)) == NULL) continue; if (!strcmp(value, "custom")) { // Custom size... snprintf(name, sizeof(name), "ready%d-custom-width", i); custom_width = cupsGetOption(name, num_form, form); snprintf(name, sizeof(name), "ready%d-custom-length", i); custom_length = cupsGetOption(name, num_form, form); if (custom_width && custom_length) pwg = pwgMediaForSize((int)(2540.0 * strtod(custom_width, NULL)), (int)(2540.0 * strtod(custom_length, NULL))); } else { // Standard size... pwg = pwgMediaForPWG(value); } papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s='%s',%d,%d", name, pwg ? pwg->pwg : "unknown", pwg ? pwg->width : 0, pwg ? pwg->length : 0); if (pwg) { strlcpy(ready->size_name, pwg->pwg, sizeof(ready->size_name)); ready->size_width = pwg->width; ready->size_length = pwg->length; } // source strlcpy(ready->source, data.source[i], sizeof(ready->source)); // margins snprintf(name, sizeof(name), "ready%d-borderless", i); if (cupsGetOption(name, num_form, form)) { ready->bottom_margin = ready->top_margin = 0; ready->left_margin = ready->right_margin = 0; } else { ready->bottom_margin = ready->top_margin = data.bottom_top; ready->left_margin = ready->right_margin = data.left_right; } // top-offset snprintf(name, sizeof(name), "ready%d-top-offset", i); if ((value = cupsGetOption(name, num_form, form)) != NULL) ready->top_offset = (int)(100.0 * strtod(value, NULL)); // tracking snprintf(name, sizeof(name), "ready%d-tracking", i); if ((value = cupsGetOption(name, num_form, form)) != NULL) ready->tracking = _papplMediaTrackingValue(value); // type snprintf(name, sizeof(name), "ready%d-type", i); if ((value = cupsGetOption(name, num_form, form)) != NULL) strlcpy(ready->type, value, sizeof(ready->type)); } papplPrinterSetReadyMedia(printer, data.num_source, data.media_ready); status = "Changes saved."; } cupsFreeOptions(num_form, form); } papplClientHTMLPrinterHeader(client, printer, "Media", 0, NULL, NULL); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, " \n" " \n"); for (i = 0; i < data.num_source; i ++) { if (!strcmp(data.source[i], "manual")) continue; snprintf(name, sizeof(name), "ready%d", i); media_chooser(client, &data, localize_keyword("media-source", data.source[i], text, sizeof(text)), name, data.media_ready + i); } papplClientHTMLPuts(client, " \n" " \n" "
" " \n" " \n"); papplClientHTMLPrinterFooter(client); } // // '_papplPrinterWebSupplies()' - Show the printer supplies web page. // void _papplPrinterWebSupplies( pappl_client_t *client, // I - Client pappl_printer_t *printer) // I - Printer { int i, // Looping var num_supply; // Number of supplies pappl_supply_t supply[100]; // Supplies static const char * const backgrounds[] = { "url(data:image/png;base64," "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c" "6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAAB" "AAEAAKACAAQAAAABAAAADKADAAQAAAABAAAADAAAAAATDPpdAAAAaUlEQVQo" "FY2R0Q3AIAhEa7siCet0HeKQtGeiwWKR+wH0HWAsRKTHK2ZGWEpExvmJLAuD" "LbXWNgHFV7Zzv2sTemHjCsYmS8MfjIbOEMHOsIMnQwYehiwMw6WqNxKr6F/c" "oyMYm0yGHYwtHq4fKZD9DnawAAAAAElFTkSuQmCC)", // no-color "#222", // black - not 100% black for dark mode UI "#0FF", // cyan "#777", // gray "#0C0", // green "#7FF", // light-cyan "#CCC", // light-gray "#FCF", // light-magenta "#F0F", // magenta "#F70", // orange "#707", // violet "#FF0" // yellow }; num_supply = papplPrinterGetSupplies(printer, (int)(sizeof(supply) / sizeof(supply[0])), supply); papplClientHTMLPrinterHeader(client, printer, "Supplies", 0, NULL, NULL); papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n"); for (i = 0; i < num_supply; i ++) { papplClientHTMLPrintf(client, "\n", supply[i].description, backgrounds[supply[i].color], supply[i].level * 0.5, supply[i].level, 50.0 - supply[i].level * 0.5, supply[i].level); } papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" "
%s
\n"); papplClientHTMLPrinterFooter(client); } // // 'job_cb()' - Job iterator callback. // static void job_cb(pappl_job_t *job, // I - Job pappl_client_t *client) // I - Client { bool show_cancel = false; // Show the "cancel" button? char when[256], // When job queued/started/finished hhmmss[64]; // Time HH:MM:SS switch (papplJobGetState(job)) { case IPP_JSTATE_PENDING : case IPP_JSTATE_HELD : show_cancel = true; snprintf(when, sizeof(when), "Queued at %s", time_string(papplJobGetTimeCreated(job), hhmmss, sizeof(hhmmss))); break; case IPP_JSTATE_PROCESSING : case IPP_JSTATE_STOPPED : if (papplJobIsCanceled(job)) { strlcpy(when, "Canceling", sizeof(when)); } else { show_cancel = true; snprintf(when, sizeof(when), "Started at %s", time_string(papplJobGetTimeProcessed(job), hhmmss, sizeof(hhmmss))); } break; case IPP_JSTATE_ABORTED : snprintf(when, sizeof(when), "Aborted at %s", time_string(papplJobGetTimeCompleted(job), hhmmss, sizeof(hhmmss))); break; case IPP_JSTATE_CANCELED : snprintf(when, sizeof(when), "Canceled at %s", time_string(papplJobGetTimeCompleted(job), hhmmss, sizeof(hhmmss))); break; case IPP_JSTATE_COMPLETED : snprintf(when, sizeof(when), "Completed at %s", time_string(papplJobGetTimeCompleted(job), hhmmss, sizeof(hhmmss))); break; } papplClientHTMLPrintf(client, " %d%s%s%d%s", papplJobGetID(job), papplJobGetName(job), papplJobGetUsername(job), papplJobGetImpressionsCompleted(job), when); if (show_cancel) papplClientHTMLPrintf(client, " Cancel Job\n", job->printer->uriname, papplJobGetID(job)); else papplClientHTMLPuts(client, "\n"); } // // 'job_pager()' - Show the job paging links. // static void job_pager(pappl_client_t *client, // I - Client pappl_printer_t *printer, // I - Printer int job_index, // I - First job shown (1-based) int limit) // I - Maximum jobs shown { int num_jobs = 0, // Number of jobs num_pages = 0, // Number of pages i, // Looping var page = 0; // Current page char path[1024]; // resource path if ((num_jobs = papplPrinterGetNumberOfJobs(printer)) <= limit) return; num_pages = (num_jobs + limit - 1) / limit; page = (job_index - 1) / limit; snprintf(path, sizeof(path), "%s/jobs", printer->uriname); papplClientHTMLPuts(client, "
"); if (page > 0) papplClientHTMLPrintf(client, "«", path, (page - 1) * limit + 1); for (i = 0; i < num_pages; i ++) { if (i == page) papplClientHTMLPrintf(client, " %d", i + 1); else papplClientHTMLPrintf(client, " %d", path, i * limit + 1, i + 1); } if (page < (num_pages - 1)) papplClientHTMLPrintf(client, " »", path, (page + 1) * limit + 1); papplClientHTMLPuts(client, "
\n"); } // // 'localize_keyword()' - Localize a media keyword... // static char * // O - Localized string localize_keyword( const char *attrname, // I - Attribute name const char *keyword, // I - Keyword string char *buffer, // I - String buffer size_t bufsize) // I - String buffer size { char *ptr; // Pointer into string // TODO: Do real localization of keywords (Issue #58) if (!strcmp(keyword, "bi-level")) { strlcpy(buffer, "B&W (no shading)", bufsize); } else if (!strcmp(keyword, "monochrome")) { strlcpy(buffer, "B&W", bufsize); } else if (!strcmp(keyword, "main-roll")) { strlcpy(buffer, "Main", bufsize); } else if (!strcmp(keyword, "alternate-roll")) { strlcpy(buffer, "Alternate", bufsize); } else if (!strcmp(keyword, "labels")) { strlcpy(buffer, "Cut Labels", bufsize); } else if (!strcmp(keyword, "labels-continuous")) { strlcpy(buffer, "Continuous Labels", bufsize); } else if (!strcmp(attrname, "media-type") && !strcmp(keyword, "continuous")) { strlcpy(buffer, "Continuous Paper", bufsize); } else if (!strncmp(keyword, "photographic", 12)) { if (keyword[12] == '-') snprintf(buffer, bufsize, "%c%s Photo Paper", toupper(keyword[13]), keyword + 14); else strlcpy(buffer, "Photo Paper", bufsize); } else if (!strcmp(keyword, "stationery")) { strlcpy(buffer, "Plain Paper", bufsize); } else if (!strcmp(keyword, "stationery-letterhead")) { strlcpy(buffer, "Letterhead", bufsize); } else if (!strcmp(keyword, "one-sided")) { strlcpy(buffer, "Off", bufsize); } else if (!strcmp(keyword, "two-sided-long-edge")) { strlcpy(buffer, "On (Portrait)", bufsize); } else if (!strcmp(keyword, "two-sided-short-edge")) { strlcpy(buffer, "On (Landscape)", bufsize); } else if (!strcmp(attrname, "media")) { pwg_media_t *pwg = pwgMediaForPWG(keyword); // PWG media size info if (!strcmp(pwg->ppd, "Letter")) strlcpy(buffer, "US Letter", bufsize); else if (!strcmp(pwg->ppd, "Legal")) strlcpy(buffer, "US Legal", bufsize); else if (!strcmp(pwg->ppd, "Env10")) strlcpy(buffer, "#10 Envelope", bufsize); else if (!strcmp(pwg->ppd, "A4") || !strcmp(pwg->ppd, "A5") || !strcmp(pwg->ppd, "A6")) strlcpy(buffer, pwg->ppd, bufsize); else if (!strcmp(pwg->ppd, "EnvDL")) strlcpy(buffer, "DL Envelope", bufsize); else if ((pwg->width % 100) == 0 && (pwg->width % 2540) != 0) snprintf(buffer, bufsize, "%d x %dmm", pwg->width / 100, pwg->length / 100); else snprintf(buffer, bufsize, "%g x %g\"", pwg->width / 2540.0, pwg->length / 2540.0); } else { strlcpy(buffer, keyword, bufsize); *buffer = (char)toupper(*buffer); for (ptr = buffer + 1; *ptr; ptr ++) { if (*ptr == '-' && ptr[1]) { *ptr++ = ' '; *ptr = (char)toupper(*ptr); } } } return (buffer); } // // 'localize_media()' - Localize media-col information. // static char * // O - Localized description of the media localize_media( pappl_media_col_t *media, // I - Media info bool include_source, // I - Include the media source? char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char size[128], // Size name string source[128], // Source string type[128]; // Type string const char *borderless; // Borderless qualifier if (!media->size_name[0]) strlcpy(size, "Unknown", sizeof(size)); else localize_keyword("media", media->size_name, size, sizeof(size)); if (!media->type[0]) strlcpy(type, "Unknown", sizeof(type)); else localize_keyword("media-type", media->type, type, sizeof(type)); if (!media->left_margin && !media->right_margin && !media->top_margin && !media->bottom_margin) borderless = ", Borderless"; else borderless = ""; if (include_source) snprintf(buffer, bufsize, "%s (%s%s) from %s", size, type, borderless, localize_keyword("media-source", media->source, source, sizeof(source))); else snprintf(buffer, bufsize, "%s (%s%s)", size, type, borderless); return (buffer); } // // 'media_chooser()' - Show the media chooser. // static void media_chooser( pappl_client_t *client, // I - Client pappl_pr_driver_data_t *driver_data,// I - Driver data const char *title, // I - Label/title const char *name, // I - Base name pappl_media_col_t *media) // I - Current media values { int i, // Looping var cur_index = 0, // Current size index sel_index = 0; // Selected size index... pwg_media_t *pwg; // PWG media size info char text[256]; // Human-readable value/text const char *min_size = NULL, // Minimum size *max_size = NULL; // Maximum size // media-size papplClientHTMLPrintf(client, " %s Media:", title); for (i = 0; i < driver_data->num_media && (!min_size || !max_size); i ++) { if (!strncmp(driver_data->media[i], "custom_", 7) || !strncmp(driver_data->media[i], "roll_", 5)) { if (strstr(driver_data->media[i], "_min_")) min_size = driver_data->media[i]; else if (strstr(driver_data->media[i], "_max_")) max_size = driver_data->media[i]; } } if (min_size && max_size) { papplClientHTMLPrintf(client, "", name); for (i = 0; i < driver_data->num_media; i ++) { if (!strncmp(driver_data->media[i], "custom_", 7) || !strncmp(driver_data->media[i], "roll_", 5)) { if (strstr(driver_data->media[i], "_min_")) min_size = driver_data->media[i]; else if (strstr(driver_data->media[i], "_max_")) max_size = driver_data->media[i]; continue; } if (!strcmp(driver_data->media[i], media->size_name)) sel_index = cur_index; papplClientHTMLPrintf(client, "", driver_data->media[i], sel_index == cur_index ? " selected" : "", localize_keyword("media", driver_data->media[i], text, sizeof(text))); cur_index ++; } if (min_size && max_size) { int cur_width, min_width, max_width;// Current/min/max width int cur_length, min_length, max_length; // Current/min/max length if ((pwg = pwgMediaForPWG(min_size)) != NULL) { min_width = pwg->width; min_length = pwg->length; } else { min_width = 1 * 2540; min_length = 1 * 2540; } if ((pwg = pwgMediaForPWG(max_size)) != NULL) { max_width = pwg->width; max_length = pwg->length; } else { max_width = 9 * 2540; max_length = 22 * 2540; } if ((cur_width = media->size_width) < min_width) cur_width = min_width; else if (cur_width > max_width) cur_width = max_width; if ((cur_length = media->size_length) < min_length) cur_length = min_length; else if (cur_length > max_length) cur_length = max_length; papplClientHTMLPrintf(client, "
x
\n", sel_index == 0 ? "inline-block" : "none", name, name, min_width / 2540.0, max_width / 2540.0, cur_width / 2540.0, name, min_length / 2540.0, max_length / 2540.0, cur_length / 2540.0); } else papplClientHTMLPuts(client, "\n"); if (driver_data->borderless) { papplClientHTMLPrintf(client, "  Borderless\n", name, (!media->bottom_margin && !media->left_margin && !media->right_margin && !media->top_margin) ? " checked" : ""); } // media-left/top-offset (if needed) if (driver_data->left_offset_supported[1] || driver_data->top_offset_supported[1]) { papplClientHTMLPuts(client, " Offset "); if (driver_data->left_offset_supported[1]) { papplClientHTMLPrintf(client, "", name, driver_data->left_offset_supported[0] / 100.0, driver_data->left_offset_supported[1] / 100.0, media->left_offset / 100.0); if (driver_data->top_offset_supported[1]) papplClientHTMLPuts(client, " x "); } if (driver_data->top_offset_supported[1]) papplClientHTMLPrintf(client, "", name, driver_data->top_offset_supported[0] / 100.0, driver_data->top_offset_supported[1] / 100.0, media->top_offset / 100.0); papplClientHTMLPuts(client, " mm\n"); } // media-tracking (if needed) if (driver_data->tracking_supported) { papplClientHTMLPrintf(client, " \n"); } // media-type papplClientHTMLPrintf(client, " \n"); } // // 'time_string()' - Return the local time in hours, minutes, and seconds. // static char * time_string(time_t tv, // I - Time value char *buffer, // I - Buffer size_t bufsize) // I - Size of buffer { struct tm date; // Local time and date localtime_r(&tv, &date); strftime(buffer, bufsize, "%X", &date); return (buffer); } pappl-1.0.3/pappl/printer.c000066400000000000000000000664721403603036100156050ustar00rootroot00000000000000// // Printer object for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local functions... // static int compare_active_jobs(pappl_job_t *a, pappl_job_t *b); static int compare_all_jobs(pappl_job_t *a, pappl_job_t *b); static int compare_completed_jobs(pappl_job_t *a, pappl_job_t *b); // // 'papplPrinterCancelAllJobs()' - Cancel all jobs on the printer. // // This function cancels all jobs on the printer. If any job is currently being // printed, it will be stopped at a convenient time (usually the end of a page) // so that the printer will be left in a known state. // void papplPrinterCancelAllJobs( pappl_printer_t *printer) // I - Printer { pappl_job_t *job; // Job information // Loop through all jobs and cancel them. // // Since we have a writer lock, it is safe to use cupsArrayFirst/Last... pthread_rwlock_wrlock(&printer->rwlock); for (job = (pappl_job_t *)cupsArrayFirst(printer->active_jobs); job; job = (pappl_job_t *)cupsArrayNext(printer->active_jobs)) { // Cancel this job... if (job->state == IPP_JSTATE_PROCESSING || (job->state == IPP_JSTATE_HELD && job->fd >= 0)) { job->is_canceled = true; } else { job->state = IPP_JSTATE_CANCELED; job->completed = time(NULL); _papplJobRemoveFile(job); cupsArrayRemove(printer->active_jobs, job); cupsArrayAdd(printer->completed_jobs, job); } } pthread_rwlock_unlock(&printer->rwlock); if (!printer->system->clean_time) printer->system->clean_time = time(NULL) + 60; } // // 'papplPrinterCreate()' - Create a new printer. // // This function creates a new printer (service) on the specified system. The // "type" argument specifies the type of service to create and must currently // be the value `PAPPL_SERVICE_TYPE_PRINT`. // // The "printer_id" argument specifies a positive integer identifier that is // unique to the system. If you specify a value of `0` a new identifier will // be assigned. // // The "driver_name" argument specifies a named driver for the printer, from // the list of drivers registered with the @link papplSystemSetPrinterDrivers@ // function. // // The "device_id" and "device_uri" arguments specify the IEEE-1284 device ID // and device URI strings for the printer. // // On error, this function sets the `errno` variable to one of the following // values: // // - `EEXIST`: A printer with the specified name already exists. // - `EINVAL`: Bad values for the arguments were specified. // - `EIO`: The driver callback failed. // - `ENOENT`: No driver callback has been set. // - `ENOMEM`: Ran out of memory. // pappl_printer_t * // O - Printer or `NULL` on error papplPrinterCreate( pappl_system_t *system, // I - System int printer_id, // I - printer-id value or `0` for new const char *printer_name, // I - Human-readable printer name const char *driver_name, // I - Driver name const char *device_id, // I - IEEE-1284 device ID const char *device_uri) // I - Device URI { pappl_printer_t *printer; // Printer char resource[1024], // Resource path *resptr, // Pointer into resource path uuid[128], // printer-uuid print_group[65];// print-group value int k_supported; // Maximum file size supported struct statfs spoolinfo; // FS info for spool directory double spoolsize; // FS size char path[256]; // Path to resource pappl_pr_driver_data_t driver_data; // Driver data ipp_t *driver_attrs; // Driver attributes static const char * const ipp_versions[] = { // ipp-versions-supported values "1.1", "2.0" }; static const int operations[] = // operations-supported values { IPP_OP_PRINT_JOB, IPP_OP_VALIDATE_JOB, IPP_OP_CREATE_JOB, IPP_OP_SEND_DOCUMENT, IPP_OP_CANCEL_JOB, IPP_OP_GET_JOB_ATTRIBUTES, IPP_OP_GET_JOBS, IPP_OP_GET_PRINTER_ATTRIBUTES, IPP_OP_PAUSE_PRINTER, IPP_OP_RESUME_PRINTER, IPP_OP_SET_PRINTER_ATTRIBUTES, IPP_OP_CANCEL_MY_JOBS, IPP_OP_CLOSE_JOB, IPP_OP_IDENTIFY_PRINTER }; static const char * const charset[] = // charset-supported values { "us-ascii", "utf-8" }; static const char * const compression[] = { // compression-supported values "deflate", "gzip", "none" }; static const char * const multiple_document_handling[] = { // multiple-document-handling-supported values "separate-documents-uncollated-copies", "separate-documents-collated-copies" }; static const int orientation_requested[] = { IPP_ORIENT_PORTRAIT, IPP_ORIENT_LANDSCAPE, IPP_ORIENT_REVERSE_LANDSCAPE, IPP_ORIENT_REVERSE_PORTRAIT, IPP_ORIENT_NONE }; static const char * const print_content_optimize[] = { // print-content-optimize-supported "auto", "graphic", "photo", "text-and-graphic", "text" }; static const int print_quality[] = // print-quality-supported { IPP_QUALITY_DRAFT, IPP_QUALITY_NORMAL, IPP_QUALITY_HIGH }; static const char * const print_scaling[] = { // print-scaling-supported "auto", "auto-fit", "fill", "fit", "none" }; static const char * const uri_security[] = { // uri-security-supported values "none", "tls" }; static const char * const which_jobs[] = { // which-jobs-supported values "completed", "not-completed", "all" }; // Range check input... if (!system || !printer_name || !driver_name || !device_uri) { errno = EINVAL; return (NULL); } if (!system->driver_cb) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "No driver callback set, unable to add printer."); errno = ENOENT; return (NULL); } // Prepare URI values for the printer attributes... if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE) { // Make sure printer names that start with a digit have a resource path // containing an underscore... if (isdigit(*printer_name & 255)) snprintf(resource, sizeof(resource), "/ipp/print/_%s", printer_name); else snprintf(resource, sizeof(resource), "/ipp/print/%s", printer_name); // Convert URL reserved characters to underscore... for (resptr = resource + 11; *resptr; resptr ++) { if ((*resptr & 255) <= ' ' || strchr("\177/\\\'\"?#", *resptr)) *resptr = '_'; } // Eliminate duplicate and trailing underscores... resptr = resource + 11; while (*resptr) { if (resptr[0] == '_' && resptr[1] == '_') memmove(resptr, resptr + 1, strlen(resptr)); // Duplicate underscores else if (resptr[0] == '_' && !resptr[1]) *resptr = '\0'; // Trailing underscore else resptr ++; } } else strlcpy(resource, "/ipp/print", sizeof(resource)); // Make sure the printer doesn't already exist... if ((printer = papplSystemFindPrinter(system, resource, 0, NULL)) != NULL) { int n; // Current instance number char temp[1024]; // Temporary resource path if (!strcmp(printer_name, printer->name)) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Printer '%s' already exists.", printer_name); errno = EEXIST; return (NULL); } for (n = 2; n < 10; n ++) { snprintf(temp, sizeof(temp), "%s_%d", resource, n); if (!papplSystemFindPrinter(system, temp, 0, NULL)) break; } if (n >= 10) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Printer '%s' name conflicts with existing printer.", printer_name); errno = EEXIST; return (NULL); } strlcpy(resource, temp, sizeof(resource)); } // Allocate memory for the printer... if ((printer = calloc(1, sizeof(pappl_printer_t))) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for printer: %s", strerror(errno)); return (NULL); } papplLog(system, PAPPL_LOGLEVEL_INFO, "Printer '%s' at resource path '%s'.", printer_name, resource); _papplSystemMakeUUID(system, printer_name, 0, uuid, sizeof(uuid)); // Get the maximum spool size based on the size of the filesystem used for // the spool directory. If the host OS doesn't support the statfs call // or the filesystem is larger than 2TiB, always report INT_MAX. if (statfs(system->directory, &spoolinfo)) k_supported = INT_MAX; else if ((spoolsize = (double)spoolinfo.f_bsize * spoolinfo.f_blocks / 1024) > INT_MAX) k_supported = INT_MAX; else k_supported = (int)spoolsize; // Initialize printer structure and attributes... pthread_rwlock_init(&printer->rwlock, NULL); printer->system = system; printer->name = strdup(printer_name); printer->dns_sd_name = strdup(printer_name); printer->resource = strdup(resource); printer->resourcelen = strlen(resource); printer->uriname = printer->resource + 10; // Skip "/ipp/print" in resource printer->device_id = device_id ? strdup(device_id) : NULL; printer->device_uri = strdup(device_uri); printer->driver_name = strdup(driver_name); printer->attrs = ippNew(); printer->start_time = time(NULL); printer->config_time = printer->start_time; printer->state = IPP_PSTATE_IDLE; printer->state_reasons = PAPPL_PREASON_NONE; printer->state_time = printer->start_time; printer->all_jobs = cupsArrayNew3((cups_array_func_t)compare_all_jobs, NULL, NULL, 0, NULL, (cups_afree_func_t)_papplJobDelete); printer->active_jobs = cupsArrayNew((cups_array_func_t)compare_active_jobs, NULL); printer->completed_jobs = cupsArrayNew((cups_array_func_t)compare_completed_jobs, NULL); printer->next_job_id = 1; printer->max_active_jobs = (system->options & PAPPL_SOPTIONS_MULTI_QUEUE) ? 0 : 1; printer->max_completed_jobs = 100; printer->usb_vendor_id = 0x1209; // See printer->usb_product_id = 0x8011; if (!printer->name || !printer->dns_sd_name || !printer->resource || (device_id && !printer->device_id) || !printer->device_uri || !printer->driver_name || !printer->attrs) { // Failed to allocate one of the required members... _papplPrinterDelete(printer); return (NULL); } if (papplSystemGetDefaultPrintGroup(system, print_group, sizeof(print_group))) papplPrinterSetPrintGroup(printer, print_group); // If the driver is "auto", figure out the proper driver name... if (!strcmp(driver_name, "auto") && system->autoadd_cb) { // If device_id is NULL, try to look it up... if (!printer->device_id && strncmp(device_uri, "file://", 7)) { pappl_device_t *device; // Connection to printer if ((device = papplDeviceOpen(device_uri, "auto", papplLogDevice, system)) != NULL) { char new_id[1024]; // New 1284 device ID if (papplDeviceGetID(device, new_id, sizeof(new_id))) printer->device_id = strdup(new_id); papplDeviceClose(device); } } if ((driver_name = (system->autoadd_cb)(printer_name, device_uri, printer->device_id, system->driver_cbdata)) == NULL) { errno = EIO; _papplPrinterDelete(printer); return (NULL); } } // Initialize driver... driver_attrs = NULL; _papplPrinterInitDriverData(&driver_data); if (!(system->driver_cb)(system, driver_name, device_uri, device_id, &driver_data, &driver_attrs, system->driver_cbdata)) { errno = EIO; _papplPrinterDelete(printer); return (NULL); } papplPrinterSetDriverData(printer, &driver_data, driver_attrs); ippDelete(driver_attrs); // Generate printer-device-id value as needed... if (!printer->device_id) { char temp_id[400], // Temporary "printer-device-id" string mfg[128], // Manufacturer name *mdl, // Model name cmd[128], // Command (format) list *ptr; // Pointer into string ipp_attribute_t *formats; // "document-format-supported" attribute int i, // Looping var count; // Number of values // Assume make and model are separated by a space... strlcpy(mfg, driver_data.make_and_model, sizeof(mfg)); if ((mdl = strchr(mfg, ' ')) != NULL) *mdl++ = '\0'; // Nul-terminate the make else mdl = mfg; // No separator, so assume the make and model are the same formats = ippFindAttribute(printer->driver_attrs, "document-format-supported", IPP_TAG_MIMETYPE); count = ippGetCount(formats); for (i = 0, ptr = cmd; i < count; i ++) { const char *format = ippGetString(formats, i, NULL); // Current MIME media type if (!strcmp(format, "application/pdf")) format = "PDF"; else if (!strcmp(format, "application/postscript")) format = "PS"; else if (!strcmp(format, "application/vnd.hp-postscript")) format = "PCL"; else if (!strcmp(format, "application/vnd.zebra-zpl")) format = "ZPL"; else if (!strcmp(format, "image/jpeg")) format = "JPEG"; else if (!strcmp(format, "image/png")) format = "PNG"; else if (!strcmp(format, "image/pwg-raster")) format = "PWG"; else if (!strcmp(format, "image/urf")) format = "URF"; else if (!strcmp(format, "text/plain")) format = "TXT"; else if (!strcmp(format, "application/octet-stream")) continue; if (ptr > cmd) snprintf(ptr, sizeof(cmd) - (size_t)(ptr - cmd), ",%s", format); else strlcpy(cmd, format, sizeof(cmd)); ptr += strlen(ptr); } *ptr = '\0'; snprintf(temp_id, sizeof(temp_id), "MFG:%s;MDL:%s;CMD:%s;", mfg, mdl, cmd); if ((printer->device_id = strdup(temp_id)) == NULL) { _papplPrinterDelete(printer); return (NULL); } } // charset-configured ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-configured", NULL, "utf-8"); // charset-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charset) / sizeof(charset[0]), NULL, charset); // compression-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "compression-supported", (int)(sizeof(compression) / sizeof(compression[0])), NULL, compression); // copies-default ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1); // document-format-default ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-default", NULL, "application/octet-stream"); // generated-natural-language-supported ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en"); // ipp-versions-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", (int)(sizeof(ipp_versions) / sizeof(ipp_versions[0])), NULL, ipp_versions); // job-ids-supported ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1); // job-k-octets-supported ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, k_supported); // job-priority-default ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50); // job-priority-supported ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 1); // job-sheets-default ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none"); // job-sheets-supported ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none"); // job-spooling-supported ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-spooling-supported", NULL, printer->max_active_jobs > 1 ? "spool" : "stream"); if (_papplSystemFindMIMEFilter(system, "image/jpeg", "image/pwg-raster")) { static const char * const jpeg_features_supported[] = { // "jpeg-features-supported" values "arithmetic", "cmyk", "progressive" }; // jpeg-features-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "jpeg-features-supported", (int)(sizeof(jpeg_features_supported) / sizeof(jpeg_features_supported[0])), NULL, jpeg_features_supported); // jpeg-k-octets-supported ippAddRange(printer->attrs, IPP_TAG_PRINTER, "jpeg-k-octets-supported", 0, k_supported); // jpeg-x-dimension-supported ippAddRange(printer->attrs, IPP_TAG_PRINTER, "jpeg-x-dimension-supported", 0, 16384); // jpeg-y-dimension-supported ippAddRange(printer->attrs, IPP_TAG_PRINTER, "jpeg-y-dimension-supported", 1, 16384); } // multiple-document-handling-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-document-handling-supported", sizeof(multiple_document_handling) / sizeof(multiple_document_handling[0]), NULL, multiple_document_handling); // multiple-document-jobs-supported ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0); // multiple-operation-time-out ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60); // multiple-operation-time-out-action ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job"); // natural-language-configured ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "natural-language-configured", NULL, "en"); // operations-supported ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", (int)(sizeof(operations) / sizeof(operations[0])), operations); // orientation-requested-supported ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested) / sizeof(orientation_requested[0])), orientation_requested); if (_papplSystemFindMIMEFilter(system, "application/pdf", "image/pwg-raster")) { static const char * const pdf_versions_supported[] = { // "pdf-versions-supported" values "adobe-1.3", "adobe-1.4", "adobe-1.5", "adobe-1.6", "iso-32000-1_2008" // PDF 1.7 }; // max-page-ranges-supported ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "max-page-ranges-supported", 1); // page-ranges-supported ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1); // pdf-k-octets-supported ippAddRange(printer->attrs, IPP_TAG_PRINTER, "pdf-k-octets-supported", 0, k_supported); // pdf-versions-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdf-versions-supported", (int)(sizeof(pdf_versions_supported) / sizeof(pdf_versions_supported[0])), NULL, pdf_versions_supported); } // pdl-override-supported ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted"); // print-content-optimize-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", (int)(sizeof(print_content_optimize) / sizeof(print_content_optimize[0])), NULL, print_content_optimize); // print-quality-supported ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality) / sizeof(print_quality[0])), print_quality); // print-scaling-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-scaling-supported", (int)(sizeof(print_scaling) / sizeof(print_scaling[0])), NULL, print_scaling); // printer-device-id if (printer->device_id) ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, printer->device_id); // printer-get-attributes-supported ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format"); // printer-id ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-id", printer_id); // printer-info ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, printer_name); // printer-name ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, printer_name); // printer-uuid ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid); // uri-security-supported if (papplSystemGetTLSOnly(system)) ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", NULL, "tls"); else ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", 2, NULL, uri_security); // which-jobs-supported ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "which-jobs-supported", sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs); // Add the printer to the system... _papplSystemAddPrinter(system, printer, printer_id); // Do any post-creation work... if (system->create_cb) (system->create_cb)(printer, system->driver_cbdata); // Add socket listeners... if (system->options & PAPPL_SOPTIONS_RAW_SOCKET) { if (_papplPrinterAddRawListeners(printer) && system->is_running) { pthread_t tid; // Thread ID if (pthread_create(&tid, NULL, (void *(*)(void *))_papplPrinterRunRaw, printer)) { // Unable to create client thread... papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create raw listener thread: %s", strerror(errno)); } else { // Detach the main thread from the raw thread to prevent hangs... pthread_detach(tid); } } } // Add icons... _papplSystemAddPrinterIcons(system, printer); // Add web pages, if any... if (system->options & PAPPL_SOPTIONS_WEB_INTERFACE) { snprintf(path, sizeof(path), "%s/", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebHome, printer); snprintf(path, sizeof(path), "%s/cancel", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebCancelJob, printer); snprintf(path, sizeof(path), "%s/cancelall", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebCancelAllJobs, printer); if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE) { snprintf(path, sizeof(path), "%s/delete", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebDelete, printer); } snprintf(path, sizeof(path), "%s/config", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebConfig, printer); snprintf(path, sizeof(path), "%s/jobs", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebJobs, printer); snprintf(path, sizeof(path), "%s/media", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebMedia, printer); papplPrinterAddLink(printer, "Media", path, PAPPL_LOPTIONS_NAVIGATION | PAPPL_LOPTIONS_STATUS); snprintf(path, sizeof(path), "%s/printing", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebDefaults, printer); papplPrinterAddLink(printer, "Printing Defaults", path, PAPPL_LOPTIONS_NAVIGATION | PAPPL_LOPTIONS_STATUS); if (printer->driver_data.has_supplies) { snprintf(path, sizeof(path), "%s/supplies", printer->uriname); papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplPrinterWebSupplies, printer); papplPrinterAddLink(printer, "Supplies", path, PAPPL_LOPTIONS_STATUS); } } _papplSystemConfigChanged(system); // Return it! return (printer); } // // '_papplPrinterDelete()' - Free memory associated with a printer. // void _papplPrinterDelete( pappl_printer_t *printer) // I - Printer { int i; // Looping var _pappl_resource_t *r; // Current resource char prefix[1024]; // Prefix for printer resources size_t prefixlen; // Length of prefix // Let USB/raw printing threads know to exit printer->is_deleted = true; while (printer->raw_active || printer->usb_active) { // Wait for threads to finish usleep(100000); } // Close raw listener sockets... for (i = 0; i < printer->num_raw_listeners; i ++) { close(printer->raw_listeners[i].fd); printer->raw_listeners[i].fd = -1; } printer->num_raw_listeners = 0; // Remove DNS-SD registrations... _papplPrinterUnregisterDNSSDNoLock(printer); // Remove printer-specific resources... snprintf(prefix, sizeof(prefix), "%s/", printer->uriname); prefixlen = strlen(prefix); // Note: System writer lock is already held when calling cupsArrayRemove // for the system's printer object, so we don't need a separate lock here // and can safely use cupsArrayFirst/Next... for (r = (_pappl_resource_t *)cupsArrayFirst(printer->system->resources); r; r = (_pappl_resource_t *)cupsArrayNext(printer->system->resources)) { if (r->cbdata == printer || !strncmp(r->path, prefix, prefixlen)) cupsArrayRemove(printer->system->resources, r); } // If applicable, call the delete function... if (printer->driver_data.delete_cb) (printer->driver_data.delete_cb)(printer, &printer->driver_data); // Delete jobs... cupsArrayDelete(printer->active_jobs); cupsArrayDelete(printer->completed_jobs); cupsArrayDelete(printer->all_jobs); // Free memory... free(printer->name); free(printer->dns_sd_name); free(printer->location); free(printer->geo_location); free(printer->organization); free(printer->org_unit); free(printer->resource); free(printer->device_id); free(printer->device_uri); free(printer->driver_name); free(printer->usb_storage); ippDelete(printer->driver_attrs); ippDelete(printer->attrs); cupsArrayDelete(printer->links); free(printer); } // // 'papplPrinterDelete()' - Delete a printer. // // This function deletes a printer from a system, freeing all memory and // canceling all jobs as needed. // void papplPrinterDelete( pappl_printer_t *printer) // I - Printer { pappl_system_t *system = printer->system; // System // Remove the printer from the system object... pthread_rwlock_wrlock(&system->rwlock); cupsArrayRemove(system->printers, printer); pthread_rwlock_unlock(&system->rwlock); _papplSystemConfigChanged(system); } // // 'compare_active_jobs()' - Compare two active jobs. // static int // O - Result of comparison compare_active_jobs(pappl_job_t *a, // I - First job pappl_job_t *b) // I - Second job { return (b->job_id - a->job_id); } // // 'compare_jobs()' - Compare two jobs. // static int // O - Result of comparison compare_all_jobs(pappl_job_t *a, // I - First job pappl_job_t *b) // I - Second job { return (b->job_id - a->job_id); } // // 'compare_completed_jobs()' - Compare two completed jobs. // static int // O - Result of comparison compare_completed_jobs(pappl_job_t *a, // I - First job pappl_job_t *b) // I - Second job { return (b->job_id - a->job_id); } pappl-1.0.3/pappl/printer.h000066400000000000000000000637641403603036100156130ustar00rootroot00000000000000// // Printer header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_PRINTER_H_ # define _PAPPL_PRINTER_H_ // // Include necessary headers... // # include "base.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Limits... // # define PAPPL_MAX_BIN 16 // Maximum number of output bins # define PAPPL_MAX_COLOR_MODE 8 // Maximum number of color modes # define PAPPL_MAX_MEDIA 256 // Maximum number of media sizes # define PAPPL_MAX_RASTER_TYPE 16 // Maximum number of raster types # define PAPPL_MAX_RESOLUTION 4 // Maximum number of printer resolutions # define PAPPL_MAX_SOURCE 16 // Maximum number of sources/rolls # define PAPPL_MAX_SUPPLY 32 // Maximum number of supplies # define PAPPL_MAX_TYPE 32 // Maximum number of media types # define PAPPL_MAX_VENDOR 32 // Maximum number of vendor extension attributes // // Constants... // enum pappl_color_mode_e // IPP "print-color-mode" bit values { PAPPL_COLOR_MODE_AUTO = 0x0001, // 'auto' - Automatic color/monochrome print mode PAPPL_COLOR_MODE_AUTO_MONOCHROME = 0x0002, // 'auto-monochrome' - Automatic monochrome/process monochrome print mode PAPPL_COLOR_MODE_BI_LEVEL = 0x0004, // 'bi-level' - B&W (threshold) print mode PAPPL_COLOR_MODE_COLOR = 0x0008, // 'color' - Full color print mode PAPPL_COLOR_MODE_MONOCHROME = 0x0010, // 'monochrome' - Grayscale print mode using 1 color PAPPL_COLOR_MODE_PROCESS_MONOCHROME = 0x0020 // 'process-monochrome' - Grayscale print mode using multiple colors }; typedef unsigned pappl_color_mode_t; // Bitfield for IPP "print-color-mode" values enum pappl_content_e // IPP "print-content-optimize" bit values { PAPPL_CONTENT_AUTO = 0x01, // 'auto': Automatically determine content PAPPL_CONTENT_GRAPHIC = 0x02, // 'graphic': Optimize for vector graphics PAPPL_CONTENT_PHOTO = 0x04, // 'photo': Optimize for photos/continuous tone images PAPPL_CONTENT_TEXT = 0x08, // 'text': Optimize for text PAPPL_CONTENT_TEXT_AND_GRAPHIC = 0x10 // 'text-and-graphic': Optimize for text and vector graphics }; typedef unsigned pappl_content_t; // Bitfield for IPP "print-content-optimize" values typedef enum pappl_duplex_e // Duplex printing support { PAPPL_DUPLEX_NONE, // No duplex printing support PAPPL_DUPLEX_NORMAL, // Duplex with normal back sides PAPPL_DUPLEX_FLIPPED, // Duplex with flipped back sides PAPPL_DUPLEX_ROTATED, // Duplex with back side rotated 180 degrees for long-edge duplex PAPPL_DUPLEX_MANUAL_TUMBLE // Duplex with back side rotated 180 degrees for short-edge duplex } pappl_duplex_t; enum pappl_finishings_e // IPP "finishings" bit values { PAPPL_FINISHINGS_NONE = 0x0000, // 'none' PAPPL_FINISHINGS_PUNCH = 0x0001, // 'punch' PAPPL_FINISHINGS_STAPLE = 0x0002, // 'staple' PAPPL_FINISHINGS_TRIM = 0x0004 // 'trim' }; typedef unsigned pappl_finishings_t; // Bitfield for IPP "finishings" values enum pappl_identify_actions_e // IPP "identify-actions" bit values { PAPPL_IDENTIFY_ACTIONS_NONE = 0x0000, // No actions PAPPL_IDENTIFY_ACTIONS_DISPLAY = 0x0001, // 'display': Display a message PAPPL_IDENTIFY_ACTIONS_FLASH = 0x0002, // 'flash': Flash the display or a light PAPPL_IDENTIFY_ACTIONS_SOUND = 0x0004, // 'sound': Make a sound PAPPL_IDENTIFY_ACTIONS_SPEAK = 0x0008 // 'speak': Speak a message }; typedef unsigned pappl_identify_actions_t; // Bitfield for IPP "identify-actions" values enum pappl_kind_e // IPP "printer-kind" bit values { PAPPL_KIND_DISC = 0x0001, // 'disc' PAPPL_KIND_DOCUMENT = 0x0002, // 'document' PAPPL_KIND_ENVELOPE = 0x0004, // 'envelope' PAPPL_KIND_LABEL = 0x0008, // 'label' PAPPL_KIND_LARGE_FORMAT = 0x0010, // 'large-format' PAPPL_KIND_PHOTO = 0x0020, // 'photo' PAPPL_KIND_POSTCARD = 0x0040, // 'postcard' PAPPL_KIND_RECEIPT = 0x0080, // 'receipt' PAPPL_KIND_ROLL = 0x0100 // 'roll' }; typedef unsigned pappl_kind_t; // Bitfield for IPP "printer-kind" values enum pappl_label_mode_e // IPP "label-mode-xxx" bit values { PAPPL_LABEL_MODE_APPLICATOR = 0x0001, // 'applicator' PAPPL_LABEL_MODE_CUTTER = 0x0002, // 'cutter' PAPPL_LABEL_MODE_CUTTER_DELAYED = 0x0004, // 'cutter-delayed' PAPPL_LABEL_MODE_KIOSK = 0x0008, // 'kiosk' PAPPL_LABEL_MODE_PEEL_OFF = 0x0010, // 'peel-off' PAPPL_LABEL_MODE_PEEL_OFF_PREPEEL = 0x0020, // 'peel-off-prepeel' PAPPL_LABEL_MODE_REWIND = 0x0040, // 'rewind' PAPPL_LABEL_MODE_RFID = 0x0080, // 'rfid' PAPPL_LABEL_MODE_TEAR_OFF = 0x0100 // 'tear-off' }; typedef unsigned short pappl_label_mode_t; // Bitfield for IPP "label-mode-xxx" values enum pappl_media_tracking_e // IPP "media-tracking" bit values { PAPPL_MEDIA_TRACKING_CONTINUOUS = 0x0001, // 'continuous' PAPPL_MEDIA_TRACKING_GAP = 0x0002, // 'gap' PAPPL_MEDIA_TRACKING_MARK = 0x0004, // 'mark' PAPPL_MEDIA_TRACKING_WEB = 0x0008 // 'web' }; typedef unsigned short pappl_media_tracking_t; // Bitfield for IPP "media-tracking" values enum pappl_preason_e // IPP "printer-state-reasons" bit values { PAPPL_PREASON_NONE = 0x0000, // 'none' PAPPL_PREASON_OTHER = 0x0001, // 'other' PAPPL_PREASON_COVER_OPEN = 0x0002, // 'cover-open' PAPPL_PREASON_INPUT_TRAY_MISSING = 0x0004, // 'input-tray-missing' PAPPL_PREASON_MARKER_SUPPLY_EMPTY = 0x0008, // 'marker-supply-empty' PAPPL_PREASON_MARKER_SUPPLY_LOW = 0x0010, // 'marker-supply-low' PAPPL_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020, // 'marker-waste-almost-full' PAPPL_PREASON_MARKER_WASTE_FULL = 0x0040, // 'marker-waste-full' PAPPL_PREASON_MEDIA_EMPTY = 0x0080, // 'media-empty' PAPPL_PREASON_MEDIA_JAM = 0x0100, // 'media-jam' PAPPL_PREASON_MEDIA_LOW = 0x0200, // 'media-low' PAPPL_PREASON_MEDIA_NEEDED = 0x0400, // 'media-needed' PAPPL_PREASON_OFFLINE = 0x0800, // 'offline' PAPPL_PREASON_SPOOL_AREA_FULL = 0x1000, // 'spool-area-full' PAPPL_PREASON_TONER_EMPTY = 0x2000, // 'toner-empty' PAPPL_PREASON_TONER_LOW = 0x4000, // 'toner-low' PAPPL_PREASON_DEVICE_STATUS = 0x6fff // Supported @link papplDeviceGetStatus@ bits }; enum pappl_raster_type_e // IPP "pwg-raster-document-type-supported" bit values { PAPPL_PWG_RASTER_TYPE_NONE = 0x0000, // Do not force a particular raster type PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_8 = 0x0001, // 8-bit per component AdobeRGB PAPPL_PWG_RASTER_TYPE_ADOBE_RGB_16 = 0x0002, // 16-bit per component AdobeRGB PAPPL_PWG_RASTER_TYPE_BLACK_1 = 0x0004, // 1-bit (device) black PAPPL_PWG_RASTER_TYPE_BLACK_8 = 0x0008, // 8-bit (device) black PAPPL_PWG_RASTER_TYPE_BLACK_16 = 0x0010, // 16-bit (device) black PAPPL_PWG_RASTER_TYPE_CMYK_8 = 0x0020, // 8-bit per component (device) CMYK PAPPL_PWG_RASTER_TYPE_CMYK_16 = 0x0040, // 16-bit per component (device) CMYK PAPPL_PWG_RASTER_TYPE_RGB_8 = 0x0080, // 8-bit per component (device) RGB PAPPL_PWG_RASTER_TYPE_RGB_16 = 0x0100, // 16-bit per component (device) RGB) PAPPL_PWG_RASTER_TYPE_SGRAY_8 = 0x0200, // 8-bit grayscale with 2.2 gamma PAPPL_PWG_RASTER_TYPE_SGRAY_16 = 0x0400, // 16-bit grayscale with 2.2 gamma PAPPL_PWG_RASTER_TYPE_SRGB_8 = 0x0800, // 8-bit per component sRGB PAPPL_PWG_RASTER_TYPE_SRGB_16 = 0x1000 // 16-bit per component sRGB }; typedef unsigned pappl_raster_type_t; // Bitfield for IPP "pwg-raster-document-type-supported" values enum pappl_scaling_e // IPP "print-scaling" bit values { PAPPL_SCALING_AUTO = 0x01, // 'auto': Scale to fit (non-borderless) or fill (borderless) if larger, otherwise center PAPPL_SCALING_AUTO_FIT = 0x02, // 'auto-fit': Scale to fit if larger, otherwise center PAPPL_SCALING_FILL = 0x04, // 'fill': Scale to fill the media PAPPL_SCALING_FIT = 0x08, // 'fit': Scale to fit within margins PAPPL_SCALING_NONE = 0x10 // 'none': No scaling (center/crop) }; typedef unsigned pappl_scaling_t; // Bitfield for IPP "print-scaling" values enum pappl_sides_e // IPP "sides" bit values { PAPPL_SIDES_ONE_SIDED = 0x01, // 'one-sided' PAPPL_SIDES_TWO_SIDED_LONG_EDGE = 0x02, // 'two-sided-long-edge' PAPPL_SIDES_TWO_SIDED_SHORT_EDGE = 0x04, // 'two-sided-short-edge' }; typedef unsigned pappl_sides_t; // Bitfield for IPP "sides" values typedef enum pappl_supply_color_e // "printer-supply" color values { PAPPL_SUPPLY_COLOR_NO_COLOR, // No color (waste tank, etc.) PAPPL_SUPPLY_COLOR_BLACK, // Black ink/toner (photo or matte) PAPPL_SUPPLY_COLOR_CYAN, // Cyan ink/toner PAPPL_SUPPLY_COLOR_GRAY, // Gray ink (sometimes marketed as light gray) PAPPL_SUPPLY_COLOR_GREEN, // Green ink PAPPL_SUPPLY_COLOR_LIGHT_CYAN, // Light cyan ink PAPPL_SUPPLY_COLOR_LIGHT_GRAY, // Light gray ink (sometimes marketed as light light gray) PAPPL_SUPPLY_COLOR_LIGHT_MAGENTA, // Light magenta ink PAPPL_SUPPLY_COLOR_MAGENTA, // Magenta ink/toner PAPPL_SUPPLY_COLOR_ORANGE, // Orange ink PAPPL_SUPPLY_COLOR_VIOLET, // Violet ink PAPPL_SUPPLY_COLOR_YELLOW // Yellow ink/toner } pappl_supply_color_t; typedef enum pappl_supply_type_e // IPP "printer-supply" type values { PAPPL_SUPPLY_TYPE_BANDING_SUPPLY, // Banding finisher supplies PAPPL_SUPPLY_TYPE_BINDING_SUPPLY, // Binding finisher supplies PAPPL_SUPPLY_TYPE_CLEANER_UNIT, // Cleaning unit PAPPL_SUPPLY_TYPE_CORONA_WIRE, // Corona wire (laser printers) PAPPL_SUPPLY_TYPE_COVERS, // Cover finisher supplies PAPPL_SUPPLY_TYPE_DEVELOPER, // Developer supply PAPPL_SUPPLY_TYPE_FUSER_CLEANING_PAD, // Fuser cleaning pad (laser printers) PAPPL_SUPPLY_TYPE_FUSER_OIL_WICK, // Fuser oil wick (laser printers) PAPPL_SUPPLY_TYPE_FUSER_OIL, // Fuser oil supply (laser printers) PAPPL_SUPPLY_TYPE_FUSER_OILER, // Fuser oiler (laser printers) PAPPL_SUPPLY_TYPE_FUSER, // Fuser (laser printers) PAPPL_SUPPLY_TYPE_INK_CARTRIDGE, // Ink cartridge PAPPL_SUPPLY_TYPE_INK_RIBBON, // Ink ribbon supply PAPPL_SUPPLY_TYPE_INK, // Ink supply PAPPL_SUPPLY_TYPE_INSERTS, // Insert finisher supplies PAPPL_SUPPLY_TYPE_OPC, // Optical photoconductor (laser printers) PAPPL_SUPPLY_TYPE_PAPER_WRAP, // Wrap finisher supplies PAPPL_SUPPLY_TYPE_RIBBON_WAX, // Wax ribbon supply PAPPL_SUPPLY_TYPE_SOLID_WAX, // Solid wax supply PAPPL_SUPPLY_TYPE_STAPLES, // Staple finisher supplies PAPPL_SUPPLY_TYPE_STITCHING_WIRE, // Staple/stitch finisher supplies PAPPL_SUPPLY_TYPE_TONER_CARTRIDGE, // Toner cartridge PAPPL_SUPPLY_TYPE_TONER, // Toner supply PAPPL_SUPPLY_TYPE_TRANSFER_UNIT, // Transfer unit (laser printers) PAPPL_SUPPLY_TYPE_WASTE_INK, // Waste ink PAPPL_SUPPLY_TYPE_WASTE_TONER, // Waste toner PAPPL_SUPPLY_TYPE_WASTE_WATER, // Waste water PAPPL_SUPPLY_TYPE_WASTE_WAX, // Waste wax PAPPL_SUPPLY_TYPE_WATER // Water supply } pappl_supply_type_t; enum pappl_uoptions_e // USB gadget options { PAPPL_UOPTIONS_NONE = 0, // No options (just USB printer) PAPPL_UOPTIONS_ETHERNET = 0x01, // Include USB ethernet gadget PAPPL_UOPTIONS_SERIAL = 0x02, // Include USB serial gadget PAPPL_UOPTIONS_STORAGE = 0x04, // Include USB mass storage gadget PAPPL_UOPTIONS_STORAGE_READONLY = 0x08, // USB mass storage gadget is read-only PAPPL_UOPTIONS_STORAGE_REMOVABLE = 0x10 // USB mass storage gadget is removable }; typedef unsigned pappl_uoptions_t; // USB gadget options bitfield // // Callback functions... // typedef void (*pappl_default_cb_t)(ipp_attribute_t *attr, void *data); // papplIterateDefaults callback typedef void (*pappl_job_cb_t)(pappl_job_t *job, void *data); // papplIterateXxxJobs callback function typedef void (*pappl_pr_delete_cb_t)(pappl_printer_t *printer, pappl_pr_driver_data_t *data); // Printer deletion callback typedef void (*pappl_pr_identify_cb_t)(pappl_printer_t *printer, pappl_identify_actions_t actions, const char *message); // Identify-Printer callback typedef bool (*pappl_pr_printfile_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); // Print a "raw" job callback typedef bool (*pappl_pr_rendjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); // End a raster job callback typedef bool (*pappl_pr_rendpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); // End a raster page callback typedef bool (*pappl_pr_rstartjob_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); // Start a raster job callback typedef bool (*pappl_pr_rstartpage_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); // Start a raster page callback typedef bool (*pappl_pr_rwriteline_cb_t)(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned y, const unsigned char *line); // Write a line of raster graphics callback typedef bool (*pappl_pr_status_cb_t)(pappl_printer_t *printer); // Update printer status callback typedef const char *(*pappl_pr_testpage_cb_t)(pappl_printer_t *printer, char *buffer, size_t bufsize); // Print a test page callback // // Structures... // typedef struct pappl_icon_s // Printer PNG icon structure { char filename[256]; // External filename, if any const void *data; // PNG icon data, if any size_t datalen; // Size of PNG icon data } pappl_icon_t; typedef struct pappl_media_col_s // Media details structure { int bottom_margin; // Bottom margin in hundredths of millimeters int left_margin; // Left margin in hundredths of millimeters int left_offset; // Left offset in hundredths of millimeters int right_margin; // Right margin in hundredths of millimeters int size_width; // Width in hundredths of millimeters int size_length; // Height in hundredths of millimeters char size_name[64]; // PWG media size name char source[64]; // PWG media source name int top_margin; // Top margin in hundredths of millimeters int top_offset; // Top offset in hundredths of millimeters pappl_media_tracking_t tracking; // Media tracking char type[64]; // PWG media type name } pappl_media_col_t; struct pappl_pr_options_s // Combined print job options { cups_page_header2_t header; // Raster header unsigned num_pages; // Number of pages in job unsigned first_page; // First page in page-ranges, starting at 1 unsigned last_page; // Last page in page-ranges, starting at 1 pappl_dither_t dither; // Dither array, if any int copies; // "copies" value pappl_finishings_t finishings; // "finishings" value(s) pappl_media_col_t media; // "media"/"media-col" value ipp_orient_t orientation_requested; // "orientation-requested" value char output_bin[64]; // "output-bin" value pappl_color_mode_t print_color_mode; // "print-color-mode" value pappl_content_t print_content_optimize; // "print-content-optimize" value int print_darkness; // "print-darkness" value int darkness_configured; // "printer-darkness-configured" value ipp_quality_t print_quality; // "print-quality" value pappl_scaling_t print_scaling; // "print-scaling" value int print_speed; // "print-speed" value int printer_resolution[2]; // "printer-resolution" value in dots per inch pappl_sides_t sides; // "sides" value int num_vendor; // Number of vendor options cups_option_t *vendor; // Vendor options }; typedef struct pappl_supply_s // Supply data { pappl_supply_color_t color; // Color, if any char description[256]; // Description bool is_consumed; // Is this a supply that is consumed? int level; // Level (0-100, -1 = unknown) pappl_supply_type_t type; // Type } pappl_supply_t; struct pappl_pr_driver_data_s // Printer driver data { void *extension; // Extension data (managed by driver) pappl_pr_delete_cb_t delete_cb; // Printer deletion callback pappl_pr_identify_cb_t identify_cb; // Identify-Printer callback pappl_pr_printfile_cb_t printfile_cb; // Print (raw) file callback pappl_pr_rendjob_cb_t rendjob_cb; // End raster job callback pappl_pr_rendpage_cb_t rendpage_cb; // End raster page callback pappl_pr_rstartjob_cb_t rstartjob_cb; // Start raster job callback pappl_pr_rstartpage_cb_t rstartpage_cb; // Start raster page callback pappl_pr_rwriteline_cb_t rwriteline_cb; // Write raster line callback pappl_pr_status_cb_t status_cb; // Status callback pappl_pr_testpage_cb_t testpage_cb; // Test page callback pappl_dither_t gdither; // 'auto', 'text', and 'graphic' dither array pappl_dither_t pdither; // 'photo' dither array const char *format; // Printer-specific format char make_and_model[128]; // "printer-make-and-model" value int ppm; // "pages-per-minute" value int ppm_color; // "pages-per-minute-color" value, if any pappl_icon_t icons[3]; // "printer-icons" values pappl_kind_t kind; // "printer-kind" values bool has_supplies; // Printer has supplies to report bool input_face_up; // Does input media come in face-up? bool output_face_up; // Does output media come out face-up? ipp_orient_t orient_default; // "orientation-requested-default" value pappl_color_mode_t color_supported; // "print-color-mode" values pappl_color_mode_t color_default; // "print-color-mode-default" value pappl_content_t content_default; // "print-content-default" value ipp_quality_t quality_default; // "print-quality-default" value pappl_scaling_t scaling_default; // "print-scaling-default" value pappl_raster_type_t raster_types; // "pwg-raster-document-type-supported" values pappl_raster_type_t force_raster_type; // Force a particular raster type? pappl_duplex_t duplex; // Duplex printing modes supported pappl_sides_t sides_supported; // "sides-supported" values pappl_sides_t sides_default; // "sides-default" value pappl_finishings_t finishings; // "finishings-supported" values int num_resolution; // Number of printer resolutions int x_resolution[PAPPL_MAX_RESOLUTION]; // Horizontal printer resolutions int y_resolution[PAPPL_MAX_RESOLUTION]; // Vertical printer resolutions int x_default; // Default horizontal resolution int y_default; // Default vertical resolution bool borderless; // Borderless margins supported? int left_right; // Left and right margins in hundredths of millimeters int bottom_top; // Bottom and top margins in hundredths of millimeters int num_media; // Number of supported media const char *media[PAPPL_MAX_MEDIA];// Supported media pappl_media_col_t media_default; // Default media pappl_media_col_t media_ready[PAPPL_MAX_SOURCE]; // Ready media int num_source; // Number of media sources (trays/rolls) const char *source[PAPPL_MAX_SOURCE]; // Media sources int left_offset_supported[2]; // media-left-offset-supported (0,0 for none) int top_offset_supported[2]; // media-top-offset-supported (0,0 for none) pappl_media_tracking_t tracking_supported; // media-tracking-supported int num_type; // Number of media types const char *type[PAPPL_MAX_TYPE]; // Media types int num_bin; // Number of output bins const char *bin[PAPPL_MAX_BIN]; // Output bins int bin_default; // Default output bin pappl_label_mode_t mode_configured; // label-mode-configured pappl_label_mode_t mode_supported; // label-mode-supported int tear_offset_configured; // label-tear-offset-configured int tear_offset_supported[2]; // label-tear-offset-supported (0,0 for none) int speed_supported[2]; // print-speed-supported (0,0 for none) int speed_default; // print-speed-default int darkness_default; // print-darkness-default int darkness_configured; // printer-darkness-configured int darkness_supported; // printer/print-darkness-supported (0 for none) pappl_identify_actions_t identify_default; // "identify-actions-default" values pappl_identify_actions_t identify_supported; // "identify-actions-supported" values int num_features; // Number of "ipp-features-supported" values const char *features[PAPPL_MAX_VENDOR]; // "ipp-features-supported" values int num_vendor; // Number of vendor attributes const char *vendor[PAPPL_MAX_VENDOR]; // Vendor attribute names }; // // Functions... // extern void papplPrinterAddLink(pappl_printer_t *printer, const char *label, const char *path_or_url, pappl_loptions_t options) _PAPPL_PUBLIC; extern void papplPrinterCancelAllJobs(pappl_printer_t *printer) _PAPPL_PUBLIC; extern void papplPrinterCloseDevice(pappl_printer_t *printer) _PAPPL_PUBLIC; extern pappl_printer_t *papplPrinterCreate(pappl_system_t *system, int printer_id, const char *printer_name, const char *driver_name, const char *device_id, const char *device_uri) _PAPPL_PUBLIC; extern void papplPrinterDelete(pappl_printer_t *printer) _PAPPL_PUBLIC; extern pappl_job_t *papplPrinterFindJob(pappl_printer_t *printer, int job_id) _PAPPL_PUBLIC; extern pappl_contact_t *papplPrinterGetContact(pappl_printer_t *printer, pappl_contact_t *contact) _PAPPL_PUBLIC; extern const char *papplPrinterGetDeviceID(pappl_printer_t *printer) _PAPPL_PUBLIC; extern const char *papplPrinterGetDeviceURI(pappl_printer_t *printer) _PAPPL_PUBLIC; extern char *papplPrinterGetDNSSDName(pappl_printer_t *printer, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern ipp_t *papplPrinterGetDriverAttributes(pappl_printer_t *printer) _PAPPL_PUBLIC; extern pappl_pr_driver_data_t *papplPrinterGetDriverData(pappl_printer_t *printer, pappl_pr_driver_data_t *data) _PAPPL_PUBLIC; extern const char *papplPrinterGetDriverName(pappl_printer_t *printer) _PAPPL_PUBLIC; extern char *papplPrinterGetGeoLocation(pappl_printer_t *printer, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern int papplPrinterGetID(pappl_printer_t *printer) _PAPPL_PUBLIC; extern int papplPrinterGetImpressionsCompleted(pappl_printer_t *printer) _PAPPL_PUBLIC; extern char *papplPrinterGetLocation(pappl_printer_t *printer, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern int papplPrinterGetMaxActiveJobs(pappl_printer_t *printer) _PAPPL_PUBLIC; extern int papplPrinterGetMaxCompletedJobs(pappl_printer_t *printer) _PAPPL_PUBLIC; extern const char *papplPrinterGetName(pappl_printer_t *printer) _PAPPL_PUBLIC; extern int papplPrinterGetNextJobID(pappl_printer_t *printer) _PAPPL_PUBLIC; extern int papplPrinterGetNumberOfActiveJobs(pappl_printer_t *printer) _PAPPL_PUBLIC; extern int papplPrinterGetNumberOfCompletedJobs(pappl_printer_t *printer) _PAPPL_PUBLIC; extern int papplPrinterGetNumberOfJobs(pappl_printer_t *printer) _PAPPL_PUBLIC; extern char *papplPrinterGetOrganization(pappl_printer_t *printer, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplPrinterGetOrganizationalUnit(pappl_printer_t *printer, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplPrinterGetPath(pappl_printer_t *printer, const char *subpath, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplPrinterGetPrintGroup(pappl_printer_t *printer, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern pappl_preason_t papplPrinterGetReasons(pappl_printer_t *printer) _PAPPL_PUBLIC; extern ipp_pstate_t papplPrinterGetState(pappl_printer_t *printer) _PAPPL_PUBLIC; extern int papplPrinterGetSupplies(pappl_printer_t *printer, int max_supplies, pappl_supply_t *supplies) _PAPPL_PUBLIC; extern pappl_system_t *papplPrinterGetSystem(pappl_printer_t *printer) _PAPPL_PUBLIC; extern void papplPrinterHTMLFooter(pappl_client_t *client) _PAPPL_PUBLIC; extern void papplPrinterHTMLHeader(pappl_client_t *client, const char *title, int refresh) _PAPPL_PUBLIC; extern void papplPrinterIterateActiveJobs(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit) _PAPPL_PUBLIC; extern void papplPrinterIterateAllJobs(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit) _PAPPL_PUBLIC; extern void papplPrinterIterateCompletedJobs(pappl_printer_t *printer, pappl_job_cb_t cb, void *data, int first_index, int limit) _PAPPL_PUBLIC; extern pappl_device_t *papplPrinterOpenDevice(pappl_printer_t *printer) _PAPPL_PUBLIC; extern void papplPrinterPause(pappl_printer_t *printer) _PAPPL_PUBLIC; extern void papplPrinterRemoveLink(pappl_printer_t *printer, const char *label) _PAPPL_PUBLIC; extern void papplPrinterResume(pappl_printer_t *printer) _PAPPL_PUBLIC; extern void papplPrinterSetContact(pappl_printer_t *printer, pappl_contact_t *contact) _PAPPL_PUBLIC; extern void papplPrinterSetDNSSDName(pappl_printer_t *printer, const char *value) _PAPPL_PUBLIC; extern bool papplPrinterSetDriverData(pappl_printer_t *printer, pappl_pr_driver_data_t *data, ipp_t *attrs) _PAPPL_PUBLIC; extern bool papplPrinterSetDriverDefaults(pappl_printer_t *printer, pappl_pr_driver_data_t *data, int num_vendor, cups_option_t *vendor) _PAPPL_PUBLIC; extern void papplPrinterSetGeoLocation(pappl_printer_t *printer, const char *value) _PAPPL_PUBLIC; extern void papplPrinterSetImpressionsCompleted(pappl_printer_t *printer, int add) _PAPPL_PUBLIC; extern void papplPrinterSetLocation(pappl_printer_t *printer, const char *value) _PAPPL_PUBLIC; extern void papplPrinterSetMaxActiveJobs(pappl_printer_t *printer, int max_active_jobs) _PAPPL_PUBLIC; extern void papplPrinterSetMaxCompletedJobs(pappl_printer_t *printer, int max_completed_jobs) _PAPPL_PUBLIC; extern void papplPrinterSetNextJobID(pappl_printer_t *printer, int next_job_id) _PAPPL_PUBLIC; extern void papplPrinterSetOrganization(pappl_printer_t *printer, const char *value) _PAPPL_PUBLIC; extern void papplPrinterSetOrganizationalUnit(pappl_printer_t *printer, const char *value) _PAPPL_PUBLIC; extern void papplPrinterSetPrintGroup(pappl_printer_t *printer, const char *value) _PAPPL_PUBLIC; extern bool papplPrinterSetReadyMedia(pappl_printer_t *printer, int num_ready, pappl_media_col_t *ready) _PAPPL_PUBLIC; extern void papplPrinterSetReasons(pappl_printer_t *printer, pappl_preason_t add, pappl_preason_t remove) _PAPPL_PUBLIC; extern void papplPrinterSetSupplies(pappl_printer_t *printer, int num_supplies, pappl_supply_t *supplies) _PAPPL_PUBLIC; extern void papplPrinterSetUSB(pappl_printer_t *printer, unsigned vendor_id, unsigned product_id, pappl_uoptions_t options, const char *storagefile) _PAPPL_PUBLIC; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_PRINTER_H_ pappl-1.0.3/pappl/resource-private.h000066400000000000000000004113171403603036100174160ustar00rootroot00000000000000/* icon-sm.png */ static unsigned char icon_sm_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82, 0,0,0,48,0,0,0,48,8,3,0,0,0,96,220,9, 181,0,0,4,25,105,67,67,80,107,67,71,67,111,108,111, 114,83,112,97,99,101,71,101,110,101,114,105,99,82,71,66, 0,0,56,141,141,85,93,104,28,85,20,62,187,115,103,35, 36,206,83,108,52,133,116,168,63,13,37,13,147,86,52,161, 180,186,127,221,221,54,110,150,73,54,218,34,232,100,246,238, 206,152,201,206,56,51,187,253,161,79,69,80,124,49,234,155, 20,196,191,183,128,32,40,245,15,219,62,180,47,149,10,37, 218,212,32,40,62,180,248,131,80,232,139,166,235,153,59,51, 153,105,186,177,222,101,238,124,243,157,239,158,123,238,185,103, 239,5,232,185,170,88,150,145,20,1,22,154,174,45,23,50, 226,115,135,143,136,61,43,144,132,135,160,23,6,161,87,81, 29,43,93,169,76,2,54,79,11,119,181,91,223,67,194,123, 95,217,213,221,254,159,173,183,70,29,21,32,113,31,98,179, 230,168,11,136,143,1,240,167,85,203,118,1,122,250,145,31, 63,234,90,30,246,98,232,183,49,64,196,47,122,184,225,99, 215,195,115,62,126,141,105,102,228,44,226,211,136,5,85,83, 106,136,151,16,143,204,197,248,70,12,251,49,176,214,95,160, 77,106,235,170,232,229,162,98,155,117,221,160,177,112,239,97, 254,159,109,193,104,133,243,109,195,167,207,153,159,62,132,239, 97,92,251,43,53,37,231,225,81,196,75,170,146,159,70,252, 8,226,107,109,125,182,28,224,219,150,155,145,17,63,6,144, 220,222,154,175,166,17,239,68,92,172,219,7,170,190,159,164, 173,181,138,33,126,231,132,54,243,44,226,45,136,207,55,231, 202,83,193,216,171,170,147,197,156,193,118,196,183,53,90,242, 242,59,4,192,137,186,91,154,241,199,114,251,109,83,158,242, 231,229,234,53,154,203,123,121,68,252,250,188,121,72,246,125, 114,159,57,237,233,124,232,243,132,150,45,7,252,165,151,148, 131,21,196,131,136,127,161,70,65,246,231,226,254,177,220,74, 16,3,25,106,26,229,73,127,46,146,163,14,91,47,227,93, 109,166,232,207,75,12,23,55,212,31,75,22,235,250,129,82, 160,255,68,179,139,114,128,175,89,6,171,81,140,141,79,218, 45,185,234,235,249,81,197,206,23,124,159,124,133,54,171,129, 127,190,13,179,9,5,40,152,48,135,189,10,77,88,3,17, 100,40,64,6,223,22,216,104,169,131,14,6,50,20,173,20, 25,138,95,161,102,23,27,231,192,60,242,58,180,153,205,193, 190,194,148,254,200,200,95,131,249,184,206,180,42,100,135,62, 130,22,178,26,252,142,172,22,211,101,241,171,133,92,99,19, 63,126,44,55,2,63,38,25,32,18,217,131,207,94,50,73, 246,145,113,50,1,34,121,138,60,77,246,147,28,178,19,100, 239,250,216,74,108,69,94,60,55,214,253,188,140,51,82,166, 155,69,221,57,180,187,160,96,255,51,42,76,92,83,215,172, 44,14,182,134,35,203,41,251,5,93,189,252,198,95,177,92, 233,44,55,81,182,226,25,157,186,87,206,249,95,249,235,252, 50,246,43,252,106,164,224,127,228,87,241,183,114,199,90,204, 187,178,76,195,236,108,88,115,119,85,26,109,6,227,22,240, 209,153,197,137,197,29,243,113,241,228,87,15,70,126,150,201, 153,231,175,244,93,60,89,111,46,14,70,172,151,5,250,106, 249,86,25,78,141,68,172,244,131,244,135,180,44,189,39,125, 40,253,198,189,205,125,202,125,205,125,206,125,193,93,2,145, 59,203,157,227,190,225,46,112,31,115,95,198,246,106,243,26, 90,223,123,22,121,24,183,103,233,150,107,172,74,33,35,108, 21,30,22,114,194,54,225,81,97,50,242,39,12,8,99,66, 81,216,129,150,173,235,251,22,159,47,158,61,29,14,99,31, 230,167,251,92,190,46,86,1,137,251,177,2,244,77,254,85, 85,84,233,112,148,41,29,86,111,77,56,190,65,19,140,36, 67,100,140,148,54,84,247,184,87,243,161,34,149,79,229,82, 105,16,83,59,83,19,169,177,212,65,15,135,179,166,118,160, 109,2,251,252,29,213,169,110,178,82,234,210,99,222,125,2, 89,211,58,110,235,13,205,21,119,75,210,147,98,26,175,54, 42,150,154,234,232,136,168,24,134,200,76,142,104,83,135,218, 109,90,27,5,239,222,244,143,244,155,50,187,15,19,91,46, 71,156,251,12,192,190,63,241,236,251,46,226,142,180,0,150, 28,128,129,199,35,110,24,207,202,7,222,5,56,243,132,218, 178,219,193,29,145,72,124,11,224,212,247,236,246,191,250,50, 120,126,253,212,233,220,196,115,172,231,45,128,181,55,59,157, 191,223,239,116,214,62,64,255,171,0,103,141,127,1,160,159, 124,85,3,92,11,239,0,0,0,56,101,88,73,102,77,77, 0,42,0,0,0,8,0,1,135,105,0,4,0,0,0,1, 0,0,0,26,0,0,0,0,0,2,160,2,0,4,0,0, 0,1,0,0,0,48,160,3,0,4,0,0,0,1,0,0, 0,48,0,0,0,0,248,255,78,54,0,0,3,101,105,84, 88,116,88,77,76,58,99,111,109,46,97,100,111,98,101,46, 120,109,112,0,0,0,0,0,60,120,58,120,109,112,109,101, 116,97,32,120,109,108,110,115,58,120,61,34,97,100,111,98, 101,58,110,115,58,109,101,116,97,47,34,32,120,58,120,109, 112,116,107,61,34,88,77,80,32,67,111,114,101,32,53,46, 52,46,48,34,62,10,32,32,32,60,114,100,102,58,82,68, 70,32,120,109,108,110,115,58,114,100,102,61,34,104,116,116, 112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,49, 57,57,57,47,48,50,47,50,50,45,114,100,102,45,115,121, 110,116,97,120,45,110,115,35,34,62,10,32,32,32,32,32, 32,60,114,100,102,58,68,101,115,99,114,105,112,116,105,111, 110,32,114,100,102,58,97,98,111,117,116,61,34,34,10,32, 32,32,32,32,32,32,32,32,32,32,32,120,109,108,110,115, 58,100,99,61,34,104,116,116,112,58,47,47,112,117,114,108, 46,111,114,103,47,100,99,47,101,108,101,109,101,110,116,115, 47,49,46,49,47,34,10,32,32,32,32,32,32,32,32,32, 32,32,32,120,109,108,110,115,58,73,112,116,99,52,120,109, 112,69,120,116,61,34,104,116,116,112,58,47,47,105,112,116, 99,46,111,114,103,47,115,116,100,47,73,112,116,99,52,120, 109,112,69,120,116,47,50,48,48,56,45,48,50,45,50,57, 47,34,10,32,32,32,32,32,32,32,32,32,32,32,32,120, 109,108,110,115,58,112,104,111,116,111,115,104,111,112,61,34, 104,116,116,112,58,47,47,110,115,46,97,100,111,98,101,46, 99,111,109,47,112,104,111,116,111,115,104,111,112,47,49,46, 48,47,34,62,10,32,32,32,32,32,32,32,32,32,60,100, 99,58,99,114,101,97,116,111,114,62,10,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,83,101,113,62, 10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 60,114,100,102,58,108,105,62,77,105,99,104,97,101,108,32, 83,119,101,101,116,60,47,114,100,102,58,108,105,62,10,32, 32,32,32,32,32,32,32,32,32,32,32,60,47,114,100,102, 58,83,101,113,62,10,32,32,32,32,32,32,32,32,32,60, 47,100,99,58,99,114,101,97,116,111,114,62,10,32,32,32, 32,32,32,32,32,32,60,100,99,58,114,105,103,104,116,115, 62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,114, 100,102,58,65,108,116,62,10,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,108,105,32,120, 109,108,58,108,97,110,103,61,34,120,45,100,101,102,97,117, 108,116,34,62,67,111,112,121,114,105,103,104,116,32,50,48, 50,48,32,77,105,99,104,97,101,108,32,83,119,101,101,116, 60,47,114,100,102,58,108,105,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,60,47,114,100,102,58,65,108,116,62, 10,32,32,32,32,32,32,32,32,32,60,47,100,99,58,114, 105,103,104,116,115,62,10,32,32,32,32,32,32,32,32,32, 60,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,78,101,119,32,73,109,97,103,101,60, 47,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,10,32,32,32,32,32,32,32,32,32, 60,112,104,111,116,111,115,104,111,112,58,72,101,97,100,108, 105,110,101,62,78,101,119,32,73,109,97,103,101,60,47,112, 104,111,116,111,115,104,111,112,58,72,101,97,100,108,105,110, 101,62,10,32,32,32,32,32,32,60,47,114,100,102,58,68, 101,115,99,114,105,112,116,105,111,110,62,10,32,32,32,60, 47,114,100,102,58,82,68,70,62,10,60,47,120,58,120,109, 112,109,101,116,97,62,10,251,176,238,142,0,0,2,4,80, 76,84,69,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48, 48,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,38,38,38,0,0,0,58,58,58,51,51,51,43,43,43, 35,35,35,35,35,35,57,57,57,56,56,56,59,59,59,58, 58,58,57,57,57,55,55,55,59,59,59,58,58,58,57,57, 57,62,62,62,56,56,56,61,61,61,61,61,61,49,49,49, 59,59,59,62,62,62,63,63,63,61,61,61,0,0,0,3, 3,3,7,7,7,9,0,0,13,0,0,17,0,0,26,26, 26,35,0,0,49,49,49,58,58,58,59,0,1,60,0,1, 63,63,63,64,63,63,64,64,64,65,65,65,66,63,63,66, 66,66,68,68,68,69,69,69,70,0,1,71,71,71,72,72, 72,73,73,73,74,60,60,74,74,74,77,59,59,77,77,77, 78,78,78,79,79,79,81,81,81,84,84,84,85,85,85,86, 86,86,90,0,2,90,55,55,90,90,90,91,55,55,94,94, 94,98,98,98,106,106,106,108,49,50,108,108,108,109,109,109, 110,0,3,116,46,47,118,46,47,122,122,122,127,127,127,128, 128,128,131,41,43,131,131,131,132,132,132,134,134,134,138,138, 138,139,139,139,141,141,141,143,143,143,144,37,39,145,37,39, 148,148,148,149,1,4,149,149,149,152,152,152,156,156,156,159, 159,159,171,28,31,171,171,171,174,1,4,174,174,174,176,176, 176,183,24,27,185,185,185,189,189,189,190,1,5,193,20,24, 195,195,195,197,197,197,198,19,22,199,199,199,201,201,201,206, 206,206,207,207,207,210,1,5,210,210,210,212,212,212,213,213, 213,217,13,17,222,1,6,222,222,222,223,1,6,224,10,14, 225,10,14,227,227,227,228,9,13,228,228,228,229,9,13,231, 231,231,233,233,233,234,234,234,237,1,6,237,6,11,239,239, 239,240,240,240,242,4,9,242,242,242,243,243,243,244,1,6, 245,1,6,245,3,8,245,245,245,246,246,246,247,247,247,248, 2,7,249,249,249,250,250,250,251,2,7,251,251,251,252,252, 252,254,254,254,255,255,255,34,174,201,248,0,0,0,51,116, 82,78,83,0,1,2,4,5,6,7,10,15,17,27,28,30, 32,48,49,51,63,69,73,75,83,84,88,89,91,95,99,105, 114,131,152,153,161,163,215,217,218,221,231,232,234,234,235,238, 239,244,244,253,253,254,251,112,126,60,0,0,2,189,73,68, 65,84,72,199,173,150,247,87,19,65,16,199,151,131,16,5, 81,138,16,84,172,40,118,17,246,16,145,224,185,84,197,66, 183,162,168,9,34,130,70,108,40,22,84,140,26,17,27,177, 139,5,76,150,48,255,164,179,119,73,72,46,33,119,240,252, 254,176,187,55,111,63,59,37,179,121,75,8,42,73,74,177, 164,26,202,146,44,17,77,82,1,43,85,152,129,20,182,101, 245,34,139,138,164,22,218,89,25,69,49,154,112,172,176,111, 204,176,34,145,84,88,41,171,166,114,3,128,202,149,155,50, 172,132,20,216,101,252,40,239,24,252,169,219,196,35,164,89, 100,251,154,52,34,109,174,160,229,221,131,159,7,59,184,182, 181,38,8,236,143,5,104,197,214,101,36,25,19,250,253,168, 251,48,99,252,228,133,171,183,159,188,253,94,163,101,57,196, 121,56,227,240,74,201,38,22,5,157,255,229,81,26,109,160, 108,239,47,189,69,56,86,114,73,170,136,246,189,106,125,85, 53,170,185,198,153,117,241,170,136,124,132,69,93,218,52,64, 115,201,105,40,86,78,217,115,190,39,178,74,60,8,228,235, 128,112,146,157,92,167,104,224,77,116,72,120,222,208,67,78, 231,242,128,71,143,107,167,140,159,15,85,229,232,151,227,60, 170,51,66,95,65,15,250,223,149,119,93,163,115,123,136,7, 92,63,48,79,96,31,75,12,240,56,53,17,253,160,235,171, 4,30,244,128,102,153,173,146,254,182,8,3,231,49,150,4, 85,74,232,129,210,177,206,104,64,237,156,49,174,183,204,2, 13,227,177,221,74,27,226,148,34,4,24,221,230,217,113,97, 192,188,164,121,104,115,56,77,200,209,22,14,201,81,109,230, 240,106,71,24,112,154,11,199,249,95,128,186,250,58,67,0, 87,236,37,128,127,228,18,99,141,62,248,129,68,31,0,188, 190,213,216,50,3,240,225,238,105,252,116,171,64,184,74,78, 230,129,0,238,57,203,46,227,120,145,49,23,192,52,192,157, 86,117,250,84,237,10,2,17,33,121,160,255,196,4,12,176, 17,152,132,7,2,112,31,25,6,15,2,45,87,0,90,227, 3,237,127,96,160,57,0,61,48,117,72,0,7,131,64,239, 92,128,15,96,178,169,31,222,177,143,208,131,64,192,15,208, 131,192,20,38,195,226,2,1,239,189,115,236,5,192,87,128, 167,34,135,137,225,222,90,4,2,158,155,109,122,64,164,143, 33,225,220,62,3,62,159,31,166,155,130,59,16,104,22,179, 43,94,149,4,112,3,190,213,178,250,41,232,139,1,124,94, 175,247,84,100,72,207,84,192,13,247,113,28,134,199,46,24, 17,27,91,96,230,152,6,8,157,89,104,107,148,204,7,80, 16,216,89,102,30,40,43,181,17,11,174,204,222,7,198,118, 231,145,148,229,197,178,217,27,39,219,215,229,16,105,241,250, 93,178,185,63,1,185,178,104,69,38,190,28,150,108,40,54, 249,116,40,90,149,149,142,79,7,107,198,202,109,59,74,12, 31,39,202,246,181,249,89,226,233,64,36,107,122,102,78,158, 45,223,64,182,220,236,165,105,86,233,31,111,90,176,16,191, 68,108,64,0,0,0,0,73,69,78,68,174,66,96,130, }; /* icon-md.png */ static unsigned char icon_md_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82, 0,0,0,128,0,0,0,128,8,6,0,0,0,195,62,97, 203,0,0,4,25,105,67,67,80,107,67,71,67,111,108,111, 114,83,112,97,99,101,71,101,110,101,114,105,99,82,71,66, 0,0,56,141,141,85,93,104,28,85,20,62,187,115,103,35, 36,206,83,108,52,133,116,168,63,13,37,13,147,86,52,161, 180,186,127,221,221,54,110,150,73,54,218,34,232,100,246,238, 206,152,201,206,56,51,187,253,161,79,69,80,124,49,234,155, 20,196,191,183,128,32,40,245,15,219,62,180,47,149,10,37, 218,212,32,40,62,180,248,131,80,232,139,166,235,153,59,51, 153,105,186,177,222,101,238,124,243,157,239,158,123,238,185,103, 239,5,232,185,170,88,150,145,20,1,22,154,174,45,23,50, 226,115,135,143,136,61,43,144,132,135,160,23,6,161,87,81, 29,43,93,169,76,2,54,79,11,119,181,91,223,67,194,123, 95,217,213,221,254,159,173,183,70,29,21,32,113,31,98,179, 230,168,11,136,143,1,240,167,85,203,118,1,122,250,145,31, 63,234,90,30,246,98,232,183,49,64,196,47,122,184,225,99, 215,195,115,62,126,141,105,102,228,44,226,211,136,5,85,83, 106,136,151,16,143,204,197,248,70,12,251,49,176,214,95,160, 77,106,235,170,232,229,162,98,155,117,221,160,177,112,239,97, 254,159,109,193,104,133,243,109,195,167,207,153,159,62,132,239, 97,92,251,43,53,37,231,225,81,196,75,170,146,159,70,252, 8,226,107,109,125,182,28,224,219,150,155,145,17,63,6,144, 220,222,154,175,166,17,239,68,92,172,219,7,170,190,159,164, 173,181,138,33,126,231,132,54,243,44,226,45,136,207,55,231, 202,83,193,216,171,170,147,197,156,193,118,196,183,53,90,242, 242,59,4,192,137,186,91,154,241,199,114,251,109,83,158,242, 231,229,234,53,154,203,123,121,68,252,250,188,121,72,246,125, 114,159,57,237,233,124,232,243,132,150,45,7,252,165,151,148, 131,21,196,131,136,127,161,70,65,246,231,226,254,177,220,74, 16,3,25,106,26,229,73,127,46,146,163,14,91,47,227,93, 109,166,232,207,75,12,23,55,212,31,75,22,235,250,129,82, 160,255,68,179,139,114,128,175,89,6,171,81,140,141,79,218, 45,185,234,235,249,81,197,206,23,124,159,124,133,54,171,129, 127,190,13,179,9,5,40,152,48,135,189,10,77,88,3,17, 100,40,64,6,223,22,216,104,169,131,14,6,50,20,173,20, 25,138,95,161,102,23,27,231,192,60,242,58,180,153,205,193, 190,194,148,254,200,200,95,131,249,184,206,180,42,100,135,62, 130,22,178,26,252,142,172,22,211,101,241,171,133,92,99,19, 63,126,44,55,2,63,38,25,32,18,217,131,207,94,50,73, 246,145,113,50,1,34,121,138,60,77,246,147,28,178,19,100, 239,250,216,74,108,69,94,60,55,214,253,188,140,51,82,166, 155,69,221,57,180,187,160,96,255,51,42,76,92,83,215,172, 44,14,182,134,35,203,41,251,5,93,189,252,198,95,177,92, 233,44,55,81,182,226,25,157,186,87,206,249,95,249,235,252, 50,246,43,252,106,164,224,127,228,87,241,183,114,199,90,204, 187,178,76,195,236,108,88,115,119,85,26,109,6,227,22,240, 209,153,197,137,197,29,243,113,241,228,87,15,70,126,150,201, 153,231,175,244,93,60,89,111,46,14,70,172,151,5,250,106, 249,86,25,78,141,68,172,244,131,244,135,180,44,189,39,125, 40,253,198,189,205,125,202,125,205,125,206,125,193,93,2,145, 59,203,157,227,190,225,46,112,31,115,95,198,246,106,243,26, 90,223,123,22,121,24,183,103,233,150,107,172,74,33,35,108, 21,30,22,114,194,54,225,81,97,50,242,39,12,8,99,66, 81,216,129,150,173,235,251,22,159,47,158,61,29,14,99,31, 230,167,251,92,190,46,86,1,137,251,177,2,244,77,254,85, 85,84,233,112,148,41,29,86,111,77,56,190,65,19,140,36, 67,100,140,148,54,84,247,184,87,243,161,34,149,79,229,82, 105,16,83,59,83,19,169,177,212,65,15,135,179,166,118,160, 109,2,251,252,29,213,169,110,178,82,234,210,99,222,125,2, 89,211,58,110,235,13,205,21,119,75,210,147,98,26,175,54, 42,150,154,234,232,136,168,24,134,200,76,142,104,83,135,218, 109,90,27,5,239,222,244,143,244,155,50,187,15,19,91,46, 71,156,251,12,192,190,63,241,236,251,46,226,142,180,0,150, 28,128,129,199,35,110,24,207,202,7,222,5,56,243,132,218, 178,219,193,29,145,72,124,11,224,212,247,236,246,191,250,50, 120,126,253,212,233,220,196,115,172,231,45,128,181,55,59,157, 191,223,239,116,214,62,64,255,171,0,103,141,127,1,160,159, 124,85,3,92,11,239,0,0,0,56,101,88,73,102,77,77, 0,42,0,0,0,8,0,1,135,105,0,4,0,0,0,1, 0,0,0,26,0,0,0,0,0,2,160,2,0,4,0,0, 0,1,0,0,0,128,160,3,0,4,0,0,0,1,0,0, 0,128,0,0,0,0,107,70,38,77,0,0,3,101,105,84, 88,116,88,77,76,58,99,111,109,46,97,100,111,98,101,46, 120,109,112,0,0,0,0,0,60,120,58,120,109,112,109,101, 116,97,32,120,109,108,110,115,58,120,61,34,97,100,111,98, 101,58,110,115,58,109,101,116,97,47,34,32,120,58,120,109, 112,116,107,61,34,88,77,80,32,67,111,114,101,32,53,46, 52,46,48,34,62,10,32,32,32,60,114,100,102,58,82,68, 70,32,120,109,108,110,115,58,114,100,102,61,34,104,116,116, 112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,49, 57,57,57,47,48,50,47,50,50,45,114,100,102,45,115,121, 110,116,97,120,45,110,115,35,34,62,10,32,32,32,32,32, 32,60,114,100,102,58,68,101,115,99,114,105,112,116,105,111, 110,32,114,100,102,58,97,98,111,117,116,61,34,34,10,32, 32,32,32,32,32,32,32,32,32,32,32,120,109,108,110,115, 58,100,99,61,34,104,116,116,112,58,47,47,112,117,114,108, 46,111,114,103,47,100,99,47,101,108,101,109,101,110,116,115, 47,49,46,49,47,34,10,32,32,32,32,32,32,32,32,32, 32,32,32,120,109,108,110,115,58,73,112,116,99,52,120,109, 112,69,120,116,61,34,104,116,116,112,58,47,47,105,112,116, 99,46,111,114,103,47,115,116,100,47,73,112,116,99,52,120, 109,112,69,120,116,47,50,48,48,56,45,48,50,45,50,57, 47,34,10,32,32,32,32,32,32,32,32,32,32,32,32,120, 109,108,110,115,58,112,104,111,116,111,115,104,111,112,61,34, 104,116,116,112,58,47,47,110,115,46,97,100,111,98,101,46, 99,111,109,47,112,104,111,116,111,115,104,111,112,47,49,46, 48,47,34,62,10,32,32,32,32,32,32,32,32,32,60,100, 99,58,99,114,101,97,116,111,114,62,10,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,83,101,113,62, 10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 60,114,100,102,58,108,105,62,77,105,99,104,97,101,108,32, 83,119,101,101,116,60,47,114,100,102,58,108,105,62,10,32, 32,32,32,32,32,32,32,32,32,32,32,60,47,114,100,102, 58,83,101,113,62,10,32,32,32,32,32,32,32,32,32,60, 47,100,99,58,99,114,101,97,116,111,114,62,10,32,32,32, 32,32,32,32,32,32,60,100,99,58,114,105,103,104,116,115, 62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,114, 100,102,58,65,108,116,62,10,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,108,105,32,120, 109,108,58,108,97,110,103,61,34,120,45,100,101,102,97,117, 108,116,34,62,67,111,112,121,114,105,103,104,116,32,50,48, 50,48,32,77,105,99,104,97,101,108,32,83,119,101,101,116, 60,47,114,100,102,58,108,105,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,60,47,114,100,102,58,65,108,116,62, 10,32,32,32,32,32,32,32,32,32,60,47,100,99,58,114, 105,103,104,116,115,62,10,32,32,32,32,32,32,32,32,32, 60,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,78,101,119,32,73,109,97,103,101,60, 47,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,10,32,32,32,32,32,32,32,32,32, 60,112,104,111,116,111,115,104,111,112,58,72,101,97,100,108, 105,110,101,62,78,101,119,32,73,109,97,103,101,60,47,112, 104,111,116,111,115,104,111,112,58,72,101,97,100,108,105,110, 101,62,10,32,32,32,32,32,32,60,47,114,100,102,58,68, 101,115,99,114,105,112,116,105,111,110,62,10,32,32,32,60, 47,114,100,102,58,82,68,70,62,10,60,47,120,58,120,109, 112,109,101,116,97,62,10,251,176,238,142,0,0,16,129,73, 68,65,84,120,218,237,93,123,108,21,85,26,191,175,94,251, 64,240,218,18,88,179,165,130,97,179,125,216,214,148,34,244, 9,212,2,21,169,96,69,64,80,81,144,71,121,8,4,132, 168,188,130,224,11,159,200,83,158,245,85,197,63,214,16,255, 48,24,151,68,195,198,93,148,77,212,172,27,179,26,145,77, 81,131,187,8,45,21,132,179,223,239,236,57,215,233,112,123, 159,51,247,81,191,73,190,220,153,59,115,230,206,156,223,239, 124,231,251,126,231,204,92,135,131,23,94,120,225,133,23,94, 162,94,156,100,46,50,55,153,135,45,238,230,86,245,239,140, 43,232,21,21,21,229,213,213,213,171,43,43,43,63,168,170, 170,58,89,83,83,115,174,182,182,246,210,136,17,35,4,155, 61,134,250,69,61,163,190,169,222,63,36,91,87,86,86,86, 69,120,100,145,165,147,165,217,74,134,194,194,66,47,129,62, 143,46,224,219,225,195,135,255,132,139,202,206,206,22,25,25, 25,194,237,118,11,58,36,34,67,249,72,203,252,214,203,163, 158,81,223,168,119,148,7,14,10,143,197,89,89,89,253,20, 25,188,202,51,88,71,4,98,95,3,129,223,86,82,82,114, 166,79,159,62,12,96,18,149,7,30,192,5,248,148,151,151, 79,166,239,114,20,17,180,71,136,126,161,31,241,208,137,183, 17,195,206,250,124,62,6,32,137,203,3,31,224,68,221,243, 158,244,244,244,129,244,157,79,117,13,209,121,3,234,95,50, 201,189,28,46,46,46,62,235,241,120,24,128,20,40,15,156, 200,27,180,35,54,235,213,171,87,17,125,215,151,44,83,5, 140,206,136,90,62,192,207,207,207,239,96,0,82,175,60,112, 67,160,232,245,122,65,130,254,170,75,8,159,4,4,254,118, 180,124,6,32,117,203,195,19,80,151,208,66,235,154,4,153, 170,59,8,29,240,161,47,9,228,246,99,185,129,12,6,48, 174,229,129,31,112,164,236,173,153,182,11,84,119,144,30,52, 48,84,169,94,155,57,224,139,230,2,0,248,72,135,83,172, 117,186,197,159,157,105,162,221,233,77,169,10,188,224,186,34, 238,102,245,253,3,71,232,6,68,134,58,218,30,172,2,195, 180,110,187,2,228,249,72,41,162,185,0,35,224,135,9,240, 255,16,224,248,92,71,219,163,232,123,243,13,166,2,1,226, 249,251,118,16,64,117,5,103,74,75,75,55,208,250,141,100, 185,42,30,8,216,21,56,33,42,232,60,63,148,141,169,174, 238,210,194,205,128,103,132,168,80,38,64,124,8,0,60,41, 32,60,65,235,13,42,30,200,81,98,81,87,47,0,121,23, 202,82,184,46,253,39,79,70,80,192,29,54,19,160,174,166, 134,9,16,230,177,192,53,55,55,119,62,96,38,27,160,188, 128,203,236,254,87,7,211,162,59,220,233,226,216,85,57,98, 223,181,131,196,146,146,82,233,1,34,209,178,113,131,209,234, 224,245,4,246,236,178,33,98,211,31,254,40,222,190,38,87, 252,227,74,159,248,111,90,166,36,129,93,218,123,34,98,0, 187,238,101,216,176,97,23,73,215,217,74,48,143,37,203,55, 196,2,191,186,127,136,7,208,152,237,106,193,225,150,39,223, 36,134,144,71,153,237,112,137,237,78,143,248,27,121,153,211, 212,189,224,115,7,109,207,161,239,177,191,62,14,30,32,88, 171,76,37,15,0,92,201,11,124,68,235,77,100,67,84,90, 152,110,36,128,139,250,255,239,48,208,16,79,2,132,11,182, 55,129,89,64,79,32,0,112,5,190,180,62,29,206,158,44, 79,117,3,254,197,141,161,198,96,163,122,86,16,64,131,13, 112,225,198,195,5,59,30,0,228,244,96,2,0,87,224,75, 235,51,201,234,85,74,216,219,72,0,15,198,155,67,1,72, 37,196,33,2,171,131,128,59,159,128,62,50,152,117,210,53, 125,65,182,138,130,210,43,34,172,192,107,200,218,168,108,29, 17,16,229,113,158,68,220,67,180,215,31,142,41,124,231,146, 141,83,194,208,85,93,8,16,142,75,60,69,23,248,53,89, 35,181,214,150,1,3,69,123,148,55,138,114,45,121,3,69, 33,85,120,43,17,10,217,196,21,49,182,32,10,105,35,58, 159,177,252,78,42,243,39,178,51,116,111,7,232,19,231,113, 197,217,3,197,114,253,17,252,30,84,193,70,149,14,94,29, 49,1,190,54,168,121,200,10,198,87,86,70,21,149,162,28, 202,235,115,225,166,193,124,171,92,96,56,231,211,229,81,233, 39,232,190,214,211,241,0,63,25,116,136,72,174,63,194,223, 91,64,54,129,172,56,42,2,160,229,27,183,99,77,179,244, 185,0,194,23,38,169,56,150,10,12,231,124,186,252,219,84, 217,11,28,110,241,79,58,30,229,146,129,0,145,92,127,20, 4,152,168,8,144,29,49,1,60,166,109,171,8,0,247,215, 105,33,1,194,57,159,156,103,71,21,13,224,73,31,21,231, 233,51,51,73,148,200,112,175,63,238,4,112,216,68,0,59, 162,224,144,74,94,109,173,56,66,125,237,36,242,106,155,200, 221,218,25,133,39,131,18,153,18,4,136,231,232,218,154,130, 34,73,0,82,68,196,119,9,138,250,99,185,126,246,0,49, 180,32,210,64,197,241,204,222,178,11,64,235,103,15,16,38, 1,140,169,145,149,89,128,213,49,64,168,10,92,72,65,223, 135,57,253,68,63,213,250,251,217,44,196,244,24,2,24,163, 228,253,121,131,98,210,1,80,222,174,44,32,88,5,94,73, 134,180,111,70,249,80,127,235,119,48,1,194,35,64,171,33, 79,182,178,2,172,214,1,130,85,32,242,125,8,63,19,43, 42,252,173,159,9,16,38,1,160,80,181,42,165,108,20,69, 209,177,84,0,202,91,169,4,134,83,129,25,202,229,239,166, 223,60,248,187,223,251,91,63,19,64,17,32,84,208,134,33, 216,221,3,175,19,223,80,0,213,25,171,118,79,134,243,224, 124,245,54,140,235,119,23,164,162,229,183,230,94,43,78,121, 179,228,186,21,243,21,236,154,143,96,245,57,45,205,2,82, 125,74,215,104,154,208,18,175,209,184,30,169,3,244,180,57, 125,76,0,38,0,19,128,9,192,4,96,2,48,1,152,0, 76,0,38,0,19,128,9,192,4,96,2,48,1,152,0,76, 0,38,0,19,32,81,207,203,199,115,70,141,221,51,114,226, 241,126,2,246,0,22,122,128,100,32,128,149,30,140,9,192, 4,96,2,48,1,98,156,15,144,74,239,214,141,244,122,205, 199,39,250,126,99,189,126,158,15,192,30,128,187,0,38,0, 19,128,9,96,21,1,48,111,223,149,34,4,136,230,217,58, 243,253,37,146,0,209,92,191,237,4,248,194,244,244,108,50, 19,32,154,167,107,205,247,151,72,2,68,115,253,182,19,0, 243,246,237,122,46,32,25,158,175,55,223,95,34,9,16,205, 245,219,78,0,204,219,183,242,185,128,100,123,195,134,249,254, 226,77,128,88,175,223,118,2,232,74,210,239,208,233,236,65, 239,8,10,116,127,61,116,236,35,54,2,164,82,22,192,229, 153,0,92,158,9,192,229,153,0,92,158,9,192,229,153,0, 76,0,38,0,19,32,166,249,0,108,169,111,236,1,216,3, 48,1,152,0,76,0,38,128,21,4,72,53,45,60,84,5, 222,116,211,77,98,213,170,85,98,237,218,181,41,103,184,110, 92,127,220,9,144,76,45,32,86,2,232,74,76,197,224,78, 147,151,9,16,67,121,180,164,84,142,240,113,253,76,0,38, 0,19,192,74,2,212,213,213,137,250,250,122,191,5,170,120, 28,211,216,216,40,109,204,152,49,151,255,231,97,136,242,112, 223,122,63,206,213,221,126,38,64,2,8,112,228,200,17,97, 92,78,156,56,33,94,126,249,101,209,212,212,228,63,102,215, 174,93,254,253,159,126,250,105,151,242,171,87,175,238,82,190, 179,179,83,124,254,249,231,98,233,210,165,126,114,92,188,120, 209,191,31,235,159,124,242,137,120,250,233,167,197,200,145,35, 187,236,71,31,207,4,72,48,1,244,242,217,103,159,249,143, 1,41,140,203,93,119,221,229,223,183,113,227,198,128,229,127, 254,249,103,113,231,157,119,138,209,163,71,139,238,150,205,155, 55,119,217,191,110,221,58,38,64,162,8,112,248,240,97,113, 235,173,183,138,67,135,14,201,237,75,151,46,137,177,99,199, 138,5,11,22,116,1,21,203,171,175,190,26,144,0,0,243, 209,71,31,245,111,99,159,25,224,187,239,190,91,252,240,195, 15,114,27,191,157,116,4,72,246,57,115,177,204,9,12,69, 128,134,134,6,63,1,46,92,184,32,110,190,249,102,241,206, 59,239,200,237,47,191,252,82,236,217,179,71,174,3,192,81, 163,70,93,70,0,244,239,0,49,24,1,38,79,158,44,78, 157,58,149,156,4,232,233,179,130,131,17,224,151,95,126,145, 166,151,183,222,122,75,122,128,246,246,118,185,189,99,199,14, 49,125,250,116,255,254,21,43,86,92,70,128,115,231,206,249, 215,219,218,218,100,255,110,4,248,252,249,243,93,186,0,120, 151,164,34,64,79,127,46,32,24,1,224,222,79,158,60,41, 222,123,239,61,241,196,19,79,72,111,240,216,99,143,117,1, 172,163,163,195,191,14,143,97,38,192,143,63,254,40,62,254, 248,99,177,115,231,78,73,22,236,55,2,140,0,241,171,175, 190,18,7,14,28,16,203,151,47,191,108,127,194,9,208,211, 159,12,10,213,5,152,247,29,59,118,204,15,220,247,223,127, 47,237,244,233,211,254,214,140,180,208,72,0,68,245,230,115, 132,2,56,238,4,8,245,252,121,172,22,47,81,4,221,83, 103,132,191,23,9,1,166,76,153,34,3,65,237,254,245,247, 51,102,204,240,3,246,252,243,207,91,74,0,116,65,240,50, 48,116,61,19,38,76,8,121,253,73,245,126,128,120,15,254, 68,250,100,79,160,10,124,255,253,247,3,18,224,133,23,94, 240,3,163,221,185,182,227,199,143,203,239,143,30,61,234,39, 0,188,68,32,64,16,7,232,216,226,145,71,30,9,186,223, 184,128,124,129,8,144,212,143,135,91,253,120,121,60,8,192, 82,48,19,128,9,192,4,96,2,48,1,152,0,76,0,38, 192,111,144,0,72,195,110,187,237,54,49,110,220,184,203,246, 33,207,199,126,38,64,15,38,192,146,37,75,196,214,173,91, 165,61,254,248,227,93,246,205,156,57,83,146,195,92,6,3, 58,211,166,77,99,2,244,20,2,204,159,63,95,138,56,24, 229,187,231,158,123,196,254,253,251,165,156,187,114,229,74,169, 1,60,243,204,51,82,12,194,96,208,189,247,222,43,231,7, 188,244,210,75,146,4,203,150,45,19,219,182,109,19,79,61, 245,148,156,44,242,202,43,175,200,227,38,78,156,200,4,72, 21,2,180,180,180,72,16,23,46,92,40,238,187,239,62,57, 89,3,251,32,220,160,181,111,217,178,69,220,113,199,29,114, 146,200,147,79,62,41,30,124,240,65,49,107,214,44,73,154, 55,223,124,83,78,254,120,237,181,215,228,248,255,235,175,191, 206,30,32,213,8,0,224,245,54,8,128,150,111,38,0,20, 187,241,227,199,75,114,128,0,247,223,127,191,28,18,110,109, 109,149,158,0,134,152,1,10,34,19,32,133,8,176,120,241, 98,217,5,24,9,160,135,122,31,126,248,225,203,8,176,105, 211,38,217,45,236,221,187,87,206,12,122,232,161,135,100,252, 128,46,226,150,91,110,97,2,252,86,210,64,180,126,61,248, 99,92,231,32,144,117,0,38,0,19,32,137,9,96,231,255, 5,88,61,31,32,212,249,46,88,48,31,64,187,240,64,66, 80,119,134,128,15,241,192,164,73,147,194,46,131,20,17,101, 98,37,64,210,207,7,72,69,15,176,104,209,162,136,210,55, 157,29,232,96,49,152,33,72,196,39,82,72,4,152,220,5, 36,33,1,32,250,32,186,199,44,32,180,84,164,118,187,119, 239,22,47,190,248,162,244,14,16,118,176,141,227,48,117,92, 19,0,251,81,30,115,7,145,5,128,72,243,230,205,243,139, 70,152,1,124,240,224,65,57,199,16,2,19,68,35,16,1, 154,3,206,5,45,1,223,67,60,194,54,142,99,2,196,153, 0,0,4,66,16,90,42,68,30,204,4,198,54,246,97,10, 23,0,122,227,141,55,100,148,63,119,238,92,169,17,104,2, 64,13,196,172,29,40,133,70,87,63,103,206,28,63,1,112, 140,81,95,192,115,3,232,66,48,133,28,138,34,190,215,192, 131,100,76,128,56,19,96,195,134,13,98,205,154,53,82,205, 67,75,68,46,15,41,24,251,240,61,164,95,77,128,217,179, 103,75,162,24,9,112,251,237,183,75,25,88,159,15,250,0, 68,34,104,3,83,167,78,149,32,27,9,0,79,131,239,113, 62,0,110,20,158,224,5,130,165,147,76,0,139,9,128,202, 214,96,195,0,4,136,128,135,65,224,198,1,8,186,0,200, 189,0,91,107,252,90,32,194,126,148,123,238,185,231,164,88, 132,121,254,232,22,96,32,13,186,20,120,21,120,8,13,52, 192,135,135,217,190,125,187,104,110,110,238,34,60,25,39,159, 50,1,18,148,6,162,11,128,154,167,159,252,129,1,252,80, 66,143,241,120,243,19,192,129,94,74,17,141,112,196,4,136, 3,1,32,249,154,163,251,64,179,121,89,8,98,33,136,9, 208,147,102,4,193,240,56,88,34,128,213,194,18,19,32,65, 195,193,200,203,145,242,97,6,80,34,8,128,46,198,248,206, 129,148,38,64,170,61,25,164,103,4,25,69,33,68,251,200, 229,245,186,81,176,65,164,142,188,29,233,29,210,56,4,115, 198,89,65,200,16,112,236,3,15,60,32,158,125,246,89,25, 16,34,37,4,185,80,22,209,63,82,71,196,25,40,143,105, 104,32,0,202,32,91,192,167,109,4,168,173,173,189,20,111, 0,241,63,63,127,167,71,185,155,28,174,132,255,239,94,119, 4,0,176,72,227,144,146,33,229,67,32,8,96,225,154,161, 5,32,149,195,62,61,103,16,143,142,99,63,68,29,164,117, 198,89,65,80,3,225,77,0,58,52,1,0,143,215,200,128, 76,200,6,32,14,65,57,4,232,32,142,246,0,184,6,189, 29,45,1,20,190,221,19,160,166,166,230,156,219,221,253,35, 213,223,82,133,110,113,184,133,155,214,157,100,251,243,6,137, 246,40,129,71,57,148,199,121,112,190,205,14,79,196,36,136, 199,211,193,102,15,160,149,59,124,15,192,49,97,4,226,141, 81,176,209,199,128,28,152,28,98,156,21,132,52,114,223,190, 125,146,0,120,231,15,116,6,124,143,220,31,222,0,3,72, 240,32,90,76,210,4,64,234,137,87,198,68,75,0,224,10, 124,131,18,160,170,170,234,100,70,70,70,183,39,249,55,85, 232,0,195,118,135,59,93,140,175,172,140,170,95,67,57,148, 215,231,202,36,131,39,72,182,247,3,152,103,4,105,113,7, 93,128,214,255,225,218,141,130,141,62,6,4,0,184,198,89, 65,104,229,232,30,64,32,128,253,238,187,239,74,111,129,105, 103,32,6,142,129,215,208,98,146,158,121,132,24,96,253,250, 245,254,151,75,69,74,0,224,74,248,126,167,8,48,65,17, 224,234,46,4,168,172,172,252,48,59,59,59,232,95,199,166, 169,117,167,234,18,98,29,206,53,159,63,217,222,16,18,174, 184,19,201,12,33,43,206,23,41,1,128,235,240,225,195,255, 74,235,205,100,141,100,69,129,8,176,46,146,241,245,163,190, 190,150,142,231,227,124,49,197,19,100,223,100,246,22,187,7, 94,39,234,107,106,108,25,79,79,118,29,160,187,125,195,134, 13,187,120,195,13,55,236,34,156,231,146,141,35,43,32,187, 202,72,0,119,89,89,89,21,177,228,167,96,30,192,105,83, 23,224,180,192,3,196,26,68,246,100,33,8,184,246,239,223, 127,53,173,207,36,171,39,27,76,214,219,72,0,242,160,142, 44,234,39,190,237,211,167,79,192,147,160,143,30,97,120,69, 140,21,65,160,31,12,58,175,149,49,0,19,224,87,3,158, 228,221,79,208,250,82,178,233,100,213,100,121,192,219,72,0, 106,132,142,116,98,202,226,146,146,146,51,129,78,132,40,253, 95,212,74,1,150,203,34,0,92,10,124,156,215,202,44,128, 223,22,254,171,1,207,194,194,194,205,42,0,108,34,27,66, 214,31,120,59,76,75,90,86,86,86,191,234,234,234,54,159, 207,39,186,35,1,90,106,167,69,239,9,180,75,7,224,255, 11,248,191,1,71,224,233,114,185,150,211,246,44,178,177,100, 249,100,62,224,109,38,128,236,6,202,203,203,39,147,39,56, 235,241,120,248,31,55,82,184,60,240,3,142,131,6,13,218, 72,219,139,200,166,144,85,144,13,80,238,223,101,38,0,186, 1,47,89,78,69,69,197,222,226,226,226,179,12,64,234,150, 39,215,223,62,116,232,208,3,180,190,76,5,127,13,42,253, 203,81,56,59,29,1,22,55,216,145,158,158,62,144,2,135, 15,242,243,243,59,24,128,212,43,15,220,168,17,255,133,92, 255,10,218,158,167,250,254,27,201,114,85,235,119,59,186,89, 156,170,111,240,245,234,213,171,8,226,16,60,65,160,238,128, 1,72,190,242,192,9,45,31,224,211,250,74,21,248,77,37, 171,85,169,159,238,251,157,142,32,139,75,69,136,125,189,94, 111,17,245,35,45,232,75,204,129,33,3,144,92,229,129,15, 112,130,219,87,45,31,224,79,35,171,83,194,79,95,133,171, 203,17,198,2,23,145,169,210,133,34,74,35,154,49,86,128, 148,66,235,4,12,64,114,148,7,30,192,5,248,168,128,111, 153,114,251,83,21,248,69,10,199,204,96,174,63,80,87,224, 81,253,5,10,23,144,75,25,85,90,90,186,1,162,2,148, 37,200,139,208,152,49,208,16,108,20,145,1,180,174,60,234, 25,245,141,122,71,253,3,7,224,129,60,95,165,122,139,84, 192,215,164,220,126,129,194,47,75,225,233,116,68,176,104,18, 100,42,247,49,88,5,18,13,185,185,185,243,73,58,222,74, 23,240,17,70,153,48,212,136,241,102,254,127,94,251,12,245, 139,122,70,125,99,96,7,218,190,146,119,151,42,119,63,75, 165,122,13,10,167,193,10,183,204,104,192,55,146,192,173,250, 14,159,138,34,139,84,62,57,86,49,109,186,98,221,92,53, 218,180,128,205,118,107,86,245,61,83,213,127,147,194,163,66, 225,147,171,240,74,87,248,69,5,190,57,48,76,83,174,36, 71,137,9,249,74,86,172,86,3,12,227,212,80,227,4,101, 19,217,44,55,93,183,141,170,190,235,85,253,15,81,120,12, 80,248,100,41,188,92,14,11,23,237,13,188,234,7,124,170, 127,201,83,238,166,64,177,239,122,53,225,128,205,30,187,94, 213,115,129,170,247,60,133,131,79,225,226,13,183,213,255,15, 100,146,127,126,245,250,24,224,0,0,0,0,73,69,78,68, 174,66,96,130, }; /* icon-lg.png */ static unsigned char icon_lg_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82, 0,0,2,0,0,0,2,0,8,6,0,0,0,244,120,212, 250,0,0,4,25,105,67,67,80,107,67,71,67,111,108,111, 114,83,112,97,99,101,71,101,110,101,114,105,99,82,71,66, 0,0,56,141,141,85,93,104,28,85,20,62,187,115,103,35, 36,206,83,108,52,133,116,168,63,13,37,13,147,86,52,161, 180,186,127,221,221,54,110,150,73,54,218,34,232,100,246,238, 206,152,201,206,56,51,187,253,161,79,69,80,124,49,234,155, 20,196,191,183,128,32,40,245,15,219,62,180,47,149,10,37, 218,212,32,40,62,180,248,131,80,232,139,166,235,153,59,51, 153,105,186,177,222,101,238,124,243,157,239,158,123,238,185,103, 239,5,232,185,170,88,150,145,20,1,22,154,174,45,23,50, 226,115,135,143,136,61,43,144,132,135,160,23,6,161,87,81, 29,43,93,169,76,2,54,79,11,119,181,91,223,67,194,123, 95,217,213,221,254,159,173,183,70,29,21,32,113,31,98,179, 230,168,11,136,143,1,240,167,85,203,118,1,122,250,145,31, 63,234,90,30,246,98,232,183,49,64,196,47,122,184,225,99, 215,195,115,62,126,141,105,102,228,44,226,211,136,5,85,83, 106,136,151,16,143,204,197,248,70,12,251,49,176,214,95,160, 77,106,235,170,232,229,162,98,155,117,221,160,177,112,239,97, 254,159,109,193,104,133,243,109,195,167,207,153,159,62,132,239, 97,92,251,43,53,37,231,225,81,196,75,170,146,159,70,252, 8,226,107,109,125,182,28,224,219,150,155,145,17,63,6,144, 220,222,154,175,166,17,239,68,92,172,219,7,170,190,159,164, 173,181,138,33,126,231,132,54,243,44,226,45,136,207,55,231, 202,83,193,216,171,170,147,197,156,193,118,196,183,53,90,242, 242,59,4,192,137,186,91,154,241,199,114,251,109,83,158,242, 231,229,234,53,154,203,123,121,68,252,250,188,121,72,246,125, 114,159,57,237,233,124,232,243,132,150,45,7,252,165,151,148, 131,21,196,131,136,127,161,70,65,246,231,226,254,177,220,74, 16,3,25,106,26,229,73,127,46,146,163,14,91,47,227,93, 109,166,232,207,75,12,23,55,212,31,75,22,235,250,129,82, 160,255,68,179,139,114,128,175,89,6,171,81,140,141,79,218, 45,185,234,235,249,81,197,206,23,124,159,124,133,54,171,129, 127,190,13,179,9,5,40,152,48,135,189,10,77,88,3,17, 100,40,64,6,223,22,216,104,169,131,14,6,50,20,173,20, 25,138,95,161,102,23,27,231,192,60,242,58,180,153,205,193, 190,194,148,254,200,200,95,131,249,184,206,180,42,100,135,62, 130,22,178,26,252,142,172,22,211,101,241,171,133,92,99,19, 63,126,44,55,2,63,38,25,32,18,217,131,207,94,50,73, 246,145,113,50,1,34,121,138,60,77,246,147,28,178,19,100, 239,250,216,74,108,69,94,60,55,214,253,188,140,51,82,166, 155,69,221,57,180,187,160,96,255,51,42,76,92,83,215,172, 44,14,182,134,35,203,41,251,5,93,189,252,198,95,177,92, 233,44,55,81,182,226,25,157,186,87,206,249,95,249,235,252, 50,246,43,252,106,164,224,127,228,87,241,183,114,199,90,204, 187,178,76,195,236,108,88,115,119,85,26,109,6,227,22,240, 209,153,197,137,197,29,243,113,241,228,87,15,70,126,150,201, 153,231,175,244,93,60,89,111,46,14,70,172,151,5,250,106, 249,86,25,78,141,68,172,244,131,244,135,180,44,189,39,125, 40,253,198,189,205,125,202,125,205,125,206,125,193,93,2,145, 59,203,157,227,190,225,46,112,31,115,95,198,246,106,243,26, 90,223,123,22,121,24,183,103,233,150,107,172,74,33,35,108, 21,30,22,114,194,54,225,81,97,50,242,39,12,8,99,66, 81,216,129,150,173,235,251,22,159,47,158,61,29,14,99,31, 230,167,251,92,190,46,86,1,137,251,177,2,244,77,254,85, 85,84,233,112,148,41,29,86,111,77,56,190,65,19,140,36, 67,100,140,148,54,84,247,184,87,243,161,34,149,79,229,82, 105,16,83,59,83,19,169,177,212,65,15,135,179,166,118,160, 109,2,251,252,29,213,169,110,178,82,234,210,99,222,125,2, 89,211,58,110,235,13,205,21,119,75,210,147,98,26,175,54, 42,150,154,234,232,136,168,24,134,200,76,142,104,83,135,218, 109,90,27,5,239,222,244,143,244,155,50,187,15,19,91,46, 71,156,251,12,192,190,63,241,236,251,46,226,142,180,0,150, 28,128,129,199,35,110,24,207,202,7,222,5,56,243,132,218, 178,219,193,29,145,72,124,11,224,212,247,236,246,191,250,50, 120,126,253,212,233,220,196,115,172,231,45,128,181,55,59,157, 191,223,239,116,214,62,64,255,171,0,103,141,127,1,160,159, 124,85,3,92,11,239,0,0,0,56,101,88,73,102,77,77, 0,42,0,0,0,8,0,1,135,105,0,4,0,0,0,1, 0,0,0,26,0,0,0,0,0,2,160,2,0,4,0,0, 0,1,0,0,2,0,160,3,0,4,0,0,0,1,0,0, 2,0,0,0,0,0,40,48,151,191,0,0,3,101,105,84, 88,116,88,77,76,58,99,111,109,46,97,100,111,98,101,46, 120,109,112,0,0,0,0,0,60,120,58,120,109,112,109,101, 116,97,32,120,109,108,110,115,58,120,61,34,97,100,111,98, 101,58,110,115,58,109,101,116,97,47,34,32,120,58,120,109, 112,116,107,61,34,88,77,80,32,67,111,114,101,32,53,46, 52,46,48,34,62,10,32,32,32,60,114,100,102,58,82,68, 70,32,120,109,108,110,115,58,114,100,102,61,34,104,116,116, 112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,49, 57,57,57,47,48,50,47,50,50,45,114,100,102,45,115,121, 110,116,97,120,45,110,115,35,34,62,10,32,32,32,32,32, 32,60,114,100,102,58,68,101,115,99,114,105,112,116,105,111, 110,32,114,100,102,58,97,98,111,117,116,61,34,34,10,32, 32,32,32,32,32,32,32,32,32,32,32,120,109,108,110,115, 58,100,99,61,34,104,116,116,112,58,47,47,112,117,114,108, 46,111,114,103,47,100,99,47,101,108,101,109,101,110,116,115, 47,49,46,49,47,34,10,32,32,32,32,32,32,32,32,32, 32,32,32,120,109,108,110,115,58,73,112,116,99,52,120,109, 112,69,120,116,61,34,104,116,116,112,58,47,47,105,112,116, 99,46,111,114,103,47,115,116,100,47,73,112,116,99,52,120, 109,112,69,120,116,47,50,48,48,56,45,48,50,45,50,57, 47,34,10,32,32,32,32,32,32,32,32,32,32,32,32,120, 109,108,110,115,58,112,104,111,116,111,115,104,111,112,61,34, 104,116,116,112,58,47,47,110,115,46,97,100,111,98,101,46, 99,111,109,47,112,104,111,116,111,115,104,111,112,47,49,46, 48,47,34,62,10,32,32,32,32,32,32,32,32,32,60,100, 99,58,99,114,101,97,116,111,114,62,10,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,83,101,113,62, 10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 60,114,100,102,58,108,105,62,77,105,99,104,97,101,108,32, 83,119,101,101,116,60,47,114,100,102,58,108,105,62,10,32, 32,32,32,32,32,32,32,32,32,32,32,60,47,114,100,102, 58,83,101,113,62,10,32,32,32,32,32,32,32,32,32,60, 47,100,99,58,99,114,101,97,116,111,114,62,10,32,32,32, 32,32,32,32,32,32,60,100,99,58,114,105,103,104,116,115, 62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,114, 100,102,58,65,108,116,62,10,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,108,105,32,120, 109,108,58,108,97,110,103,61,34,120,45,100,101,102,97,117, 108,116,34,62,67,111,112,121,114,105,103,104,116,32,50,48, 50,48,32,77,105,99,104,97,101,108,32,83,119,101,101,116, 60,47,114,100,102,58,108,105,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,60,47,114,100,102,58,65,108,116,62, 10,32,32,32,32,32,32,32,32,32,60,47,100,99,58,114, 105,103,104,116,115,62,10,32,32,32,32,32,32,32,32,32, 60,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,78,101,119,32,73,109,97,103,101,60, 47,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,10,32,32,32,32,32,32,32,32,32, 60,112,104,111,116,111,115,104,111,112,58,72,101,97,100,108, 105,110,101,62,78,101,119,32,73,109,97,103,101,60,47,112, 104,111,116,111,115,104,111,112,58,72,101,97,100,108,105,110, 101,62,10,32,32,32,32,32,32,60,47,114,100,102,58,68, 101,115,99,114,105,112,116,105,111,110,62,10,32,32,32,60, 47,114,100,102,58,82,68,70,62,10,60,47,120,58,120,109, 112,109,101,116,97,62,10,251,176,238,142,0,0,94,139,73, 68,65,84,120,218,237,189,9,180,85,197,153,183,127,47,112, 65,8,147,56,32,104,8,18,21,34,56,128,200,44,56,128, 130,8,202,40,34,160,140,130,113,86,80,65,165,157,231,121, 72,52,70,193,121,138,243,172,128,173,137,95,244,223,186,186, 243,229,107,19,58,107,117,247,74,58,102,48,81,155,193,14, 118,220,255,250,29,119,93,235,22,123,159,121,218,231,60,123, 173,223,226,114,198,253,238,93,167,222,167,222,247,173,170,134, 6,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 142,234,60,26,17,66,8,85,189,56,56,138,226,220,91,229, 160,214,8,33,132,74,174,92,250,101,32,129,35,163,179,79, 231,200,219,164,81,19,66,8,161,138,41,93,255,156,14,28, 128,130,58,117,248,113,206,62,202,169,183,117,212,46,131,118, 64,8,33,84,114,101,234,139,221,126,59,10,22,178,133,2, 142,26,115,250,173,251,247,239,223,113,212,168,81,67,71,143, 30,61,219,104,213,200,145,35,31,54,122,199,232,35,243,248, 31,14,57,228,144,77,230,241,47,198,140,25,243,229,161,135, 30,26,32,132,16,170,140,212,15,171,63,86,191,172,254,89, 253,180,250,235,17,35,70,60,98,30,187,216,60,54,111,216, 176,97,35,119,223,125,247,157,76,255,222,193,81,251,80,62, 52,248,96,224,67,1,48,80,3,78,223,222,196,38,211,88, 6,155,6,116,166,105,40,47,26,253,151,249,123,219,144,33, 67,62,27,48,96,192,231,125,250,244,249,178,71,143,30,65, 183,110,221,130,206,157,59,7,237,219,183,15,154,154,154,130, 214,173,91,7,141,141,141,129,121,127,162,165,31,80,210,109, 192,126,236,199,254,250,181,95,253,176,250,99,245,203,234,159, 213,79,171,191,86,191,173,254,91,253,184,250,115,245,235,234, 223,77,127,255,146,249,247,156,131,15,62,120,132,121,127,23, 163,142,161,190,229,193,129,15,5,46,16,100,138,14,112,84, 171,227,55,55,254,219,134,26,23,155,134,240,154,105,20,91, 13,29,126,222,183,111,223,173,221,187,119,15,58,116,232,80, 19,142,157,14,16,251,177,31,251,177,191,37,40,168,127,87, 63,175,254,94,253,190,250,127,227,7,94,55,145,130,101,230, 177,190,230,117,157,67,117,242,160,192,5,2,55,66,144,41, 50,192,81,13,78,223,220,228,158,198,233,159,97,200,239,23, 230,102,111,53,97,254,77,106,8,162,70,58,0,236,199,126, 236,199,254,250,147,250,127,249,1,249,3,249,5,227,39,254, 175,241,17,231,246,235,215,111,31,243,124,215,48,66,16,7, 4,126,116,0,24,168,50,199,223,198,56,253,73,230,166,174, 211,205,221,119,223,125,55,43,52,228,142,240,235,253,7,128, 16,66,0,208,215,17,2,249,7,249,137,16,6,214,155,40, 193,76,243,220,46,70,221,28,32,240,97,96,7,64,160,138, 28,255,208,161,67,59,155,27,184,220,232,207,131,7,15,254, 124,183,221,118,11,90,181,106,5,1,35,132,16,202,216,255, 203,95,200,111,200,127,200,143,152,130,194,85,230,255,189,205, 115,59,135,48,176,163,7,3,126,154,192,175,23,240,65,128, 163,216,206,223,20,122,236,100,110,214,117,38,175,179,217,132, 116,54,119,236,216,145,16,24,246,99,63,246,99,63,246,231, 109,191,252,136,252,137,252,138,137,10,220,108,102,19,40,61, 176,171,3,3,93,157,52,193,183,34,234,5,0,129,82,58, 126,141,248,77,222,230,114,227,252,55,171,184,163,93,187,118, 252,0,176,31,251,177,31,251,177,191,104,246,203,175,200,191, 104,186,161,137,8,92,187,203,46,187,236,101,30,239,30,166, 8,118,242,162,2,81,233,129,54,30,8,144,22,40,48,220, 223,202,80,217,2,115,67,254,106,242,54,91,118,216,97,7, 194,90,116,0,216,143,253,216,143,253,37,147,252,140,252,141, 252,206,240,225,195,79,51,143,237,110,180,155,19,21,72,7, 2,81,53,2,128,64,174,206,223,140,248,15,50,250,215,131, 14,58,104,83,54,161,126,68,7,128,253,216,143,253,216,95, 44,117,234,212,41,144,255,209,194,67,102,173,129,113,230,177, 61,140,122,132,81,1,155,30,232,18,145,26,32,45,144,175, 243,55,23,188,131,33,175,155,141,243,223,98,166,111,124,197, 15,0,33,132,80,165,250,127,249,33,249,35,147,22,184,203, 64,193,62,33,8,244,116,64,96,199,8,16,136,171,15,0, 2,226,70,253,90,177,207,56,255,223,154,130,140,45,197,154, 191,15,0,32,132,16,0,80,136,228,143,228,151,228,159,76, 122,96,130,121,172,183,19,17,112,83,3,42,22,236,152,38, 45,64,52,32,194,249,183,50,132,181,74,148,101,10,47,190, 106,32,4,134,253,216,143,253,216,143,253,85,102,191,252,147, 252,148,89,63,224,10,243,255,239,26,125,199,3,1,21,11, 118,141,137,6,0,1,190,243,55,23,115,71,163,13,131,6, 13,218,148,75,117,63,63,0,236,199,126,236,199,126,236,47, 183,253,242,83,242,87,38,98,253,143,59,239,188,243,32,243, 88,159,16,4,108,177,160,155,22,112,163,1,164,4,92,231, 111,194,41,251,27,253,126,175,189,246,250,162,158,214,231,167, 3,192,126,236,199,126,236,79,174,228,175,228,183,228,191,246, 217,103,31,165,4,246,54,218,211,232,219,97,125,64,186,104, 64,93,67,64,202,88,179,132,239,4,205,183,220,117,215,93, 191,162,65,209,1,96,63,246,99,63,246,39,77,242,95,242, 99,3,7,14,156,103,254,223,207,73,11,248,209,0,91,27, 80,215,41,129,198,112,228,191,204,132,79,182,104,91,71,126, 0,8,33,132,146,218,255,203,143,201,159,153,93,104,47,48, 255,223,215,104,31,39,26,96,107,3,220,41,131,126,74,160, 46,32,192,58,255,139,77,1,197,230,114,45,234,3,0,32, 132,16,0,80,74,201,159,201,175,25,93,107,254,63,32,34, 26,208,61,34,37,224,215,5,212,44,4,216,197,125,174,49, 203,250,110,110,219,182,45,33,48,236,199,126,236,199,126,236, 175,25,251,229,215,228,223,12,4,220,110,254,191,127,24,13, 80,109,64,239,112,166,128,159,18,168,139,186,128,102,231,111, 66,36,155,139,53,191,159,31,0,246,99,63,246,99,63,246, 87,147,253,242,111,242,115,6,4,238,48,255,63,208,168,127, 152,18,232,227,164,4,118,169,23,8,104,14,251,139,140,202, 237,252,17,29,0,246,99,63,246,211,15,150,27,2,228,239, 204,238,181,74,7,104,154,224,126,78,74,160,87,56,75,96, 151,176,46,160,166,33,32,85,240,167,220,72,57,195,254,136, 14,0,251,177,31,251,177,191,82,146,191,147,223,51,75,219, 95,100,254,63,56,76,9,124,207,104,175,176,46,160,167,87, 28,216,49,2,2,220,154,128,228,57,127,77,245,83,117,100, 37,119,241,227,7,128,16,66,0,80,185,37,191,39,255,183, 223,126,251,45,54,255,63,216,232,128,176,46,160,230,33,192, 46,242,179,169,28,83,253,0,0,132,16,66,213,214,255,203, 255,201,15,238,185,231,158,51,204,255,135,132,117,1,182,56, 208,206,16,112,33,192,79,7,36,46,21,144,90,222,87,43, 36,85,195,34,63,132,0,177,31,251,177,31,251,177,191,82, 10,23,11,250,184,75,151,46,99,35,32,160,183,7,1,110, 77,64,219,164,65,128,221,216,103,131,150,73,164,1,96,63, 246,99,63,246,99,127,189,219,47,127,104,210,1,111,155,191, 15,49,26,106,52,48,156,33,176,119,68,58,192,66,128,93, 39,32,49,69,129,26,253,175,210,70,9,172,237,207,15,0, 251,177,31,251,177,31,125,189,119,128,252,162,153,34,120,131, 7,1,251,122,16,224,79,17,140,131,128,234,115,254,134,112, 6,107,171,196,82,236,234,135,232,0,176,31,251,177,31,251, 147,42,249,69,249,71,83,15,48,207,129,128,3,189,194,64, 119,157,0,119,217,224,170,46,10,108,52,211,29,58,152,60, 199,111,181,95,50,63,0,132,16,66,244,255,45,37,255,104, 32,224,119,6,6,198,135,16,48,196,131,128,94,33,4,104, 197,192,174,17,51,3,170,46,21,96,87,250,187,165,127,255, 254,91,104,0,197,215,254,13,141,193,233,13,173,249,65,35, 132,80,194,251,127,249,73,179,80,208,143,205,223,71,24,141, 10,33,64,83,4,181,78,128,22,11,210,138,129,90,54,120, 167,134,150,211,3,171,178,30,64,206,255,32,133,54,170,113, 165,191,36,2,128,117,248,79,53,182,9,254,216,216,54,248, 178,85,187,148,136,128,96,127,146,236,183,237,22,213,134,104, 255,197,145,252,164,252,101,239,222,189,231,155,255,31,110,52, 178,225,235,117,2,180,88,144,86,12,212,178,193,218,59,160, 123,150,69,129,21,175,250,255,215,238,221,187,127,69,3,40, 174,195,47,198,15,16,7,136,253,0,0,2,0,170,75,242, 151,166,102,238,87,230,239,9,70,135,25,141,104,248,122,197, 64,45,27,188,143,55,61,208,173,7,240,23,9,170,104,20, 160,113,244,232,209,11,76,254,127,19,225,166,226,59,124,0, 0,0,0,0,16,0,80,155,146,223,60,240,192,3,87,155, 191,143,50,58,212,104,120,195,215,123,7,184,211,3,179,173, 7,40,191,243,55,121,140,206,166,240,239,211,142,29,59,114, 67,75,224,240,1,0,0,0,0,64,0,64,109,170,83,167, 78,90,37,240,211,246,237,219,79,53,255,63,210,104,140,55, 51,32,170,30,192,79,5,84,36,10,96,11,255,46,223,119, 223,125,183,208,1,22,215,225,235,61,122,47,63,64,148,100, 209,126,185,127,12,0,210,75,254,211,172,13,112,151,249,123, 162,209,88,103,102,192,1,97,61,192,158,97,61,128,93,36, 200,79,5,84,164,32,176,209,108,117,184,147,161,151,205,149, 220,232,167,90,26,64,177,28,190,62,67,159,69,7,138,112, 32,136,251,87,251,0,32,255,41,63,106,162,232,199,135,245, 0,182,40,80,245,0,3,194,122,0,119,145,160,174,25,102, 5,148,103,244,111,78,250,250,190,125,251,110,173,199,6,80, 42,135,95,138,31,32,33,112,236,175,103,7,194,253,63,20, 0,168,242,115,148,31,53,3,234,123,205,223,199,122,245,0, 3,99,82,1,238,172,128,178,71,1,82,185,127,83,252,183, 57,9,43,254,21,163,1,148,203,225,3,0,56,0,0,128, 251,15,0,212,215,253,151,31,149,63,53,181,0,179,204,255, 143,49,26,23,147,10,240,103,5,216,130,192,178,69,1,236, 232,127,185,89,204,96,51,69,123,197,117,248,0,0,14,0, 0,224,254,3,0,245,39,249,83,179,87,192,77,230,239,41, 70,71,59,169,128,131,194,84,128,63,43,160,34,5,129,250, 224,54,6,0,254,92,75,149,255,213,226,240,1,0,28,0, 0,192,253,7,0,234,79,242,167,198,175,126,98,254,158,233, 164,2,52,43,96,88,56,43,64,171,4,246,113,10,2,119, 204,80,16,88,26,0,24,51,102,204,164,193,131,7,127,158, 228,31,64,181,58,124,126,128,136,34,50,196,253,171,79,0, 148,95,237,215,175,223,5,230,239,105,97,42,64,179,2,180, 84,176,86,9,220,207,41,8,204,38,10,80,146,209,127,43, 179,122,209,186,221,118,219,45,57,13,96,204,152,196,56,124, 58,80,132,3,65,220,191,250,4,0,249,213,17,35,70,188, 99,254,214,140,128,227,140,180,97,208,97,49,5,129,221,179, 152,22,88,92,0,48,206,191,167,9,83,108,109,213,170,85, 98,66,250,127,105,251,173,196,56,124,82,0,132,128,73,1, 112,255,73,1,212,231,253,151,95,149,127,237,218,181,235,2, 243,255,233,70,147,156,130,64,187,87,128,93,38,184,103,76, 20,160,36,0,208,24,134,255,207,48,11,23,108,174,102,135, 159,164,17,62,0,64,7,0,0,112,255,1,0,238,127,195, 55,11,3,109,30,56,112,224,13,230,239,89,97,65,224,4, 39,10,48,200,137,2,216,205,130,226,102,4,20,181,24,208, 110,250,243,139,110,221,186,225,240,1,0,58,0,236,7,0, 0,0,84,100,201,191,154,72,251,255,51,127,207,49,154,145, 38,10,16,87,11,80,146,52,64,163,89,174,240,219,38,60, 241,69,99,99,35,14,31,0,72,84,13,136,41,157,13,246, 75,208,125,7,0,0,0,0,160,62,37,255,170,52,64,151, 46,93,78,137,137,2,12,244,162,0,254,186,0,69,47,6, 180,225,255,197,102,174,98,89,119,253,43,134,195,95,48,248, 96,138,112,234,72,102,181,140,96,90,67,171,224,170,198,214, 193,107,141,77,193,159,195,246,243,186,249,155,235,83,217,246, 139,216,12,8,0,204,106,77,128,77,7,28,112,192,53,105, 162,0,238,140,0,119,117,64,191,24,176,40,105,0,91,253, 255,186,217,195,184,42,59,144,116,35,252,36,143,0,0,128, 204,206,126,122,132,179,143,146,158,227,154,1,0,8,0,168, 118,201,207,14,31,62,252,31,205,223,39,133,81,128,227,194, 40,192,161,222,186,0,118,117,64,119,143,128,162,23,3,234, 3,154,204,82,133,91,155,154,154,82,23,212,149,123,161,139, 253,120,92,67,84,101,191,59,194,87,152,183,28,231,83,238, 199,163,126,128,181,96,87,62,143,207,26,58,44,88,221,127, 64,179,179,255,180,169,67,206,29,216,9,230,51,234,237,186, 85,242,113,156,102,237,1,0,237,188,244,143,203,207,202,223, 154,191,53,27,224,196,112,70,128,187,46,128,221,40,104,175, 52,83,2,139,146,6,176,163,255,193,195,134,13,251,188,210, 35,136,124,114,248,245,30,1,72,162,253,125,114,24,217,103, 35,189,95,41,128,122,172,3,168,166,26,0,68,4,128,8, 64,118,146,191,221,125,247,221,207,49,127,207,11,215,5,112, 87,7,28,218,240,245,30,1,125,195,41,129,153,138,1,11, 3,0,67,35,103,86,98,231,191,122,15,129,215,3,0,148, 194,217,191,97,62,231,26,243,121,51,204,231,206,30,58,148, 34,176,42,3,0,28,64,117,219,95,75,0,144,84,201,223, 30,120,224,129,55,155,191,231,27,205,54,154,218,240,205,30, 1,35,156,41,129,254,242,192,238,86,193,197,1,0,51,253, 239,197,114,231,255,1,128,218,3,128,82,59,251,239,226,0, 0,0,238,63,0,80,3,10,235,0,214,153,191,23,198,20, 3,218,52,128,54,9,234,21,22,3,186,105,0,119,54,64, 65,0,208,218,0,192,127,117,232,208,1,7,8,0,85,181, 179,71,201,40,2,228,218,112,223,0,192,244,146,191,53,126, 247,247,230,239,197,94,49,224,248,176,24,48,83,26,192,157, 13,144,255,232,223,132,34,58,153,20,192,182,74,204,255,7, 0,146,241,3,44,149,179,191,26,103,143,35,65,220,183,58, 4,0,249,91,249,93,83,16,184,204,73,3,104,147,160,137, 49,105,128,168,217,0,133,3,128,161,144,161,67,134,12,249, 140,16,56,246,39,209,217,19,2,38,5,192,253,7,0,146, 40,249,221,61,246,216,227,252,48,13,48,215,75,3,100,154, 13,224,46,10,84,80,1,224,236,1,3,6,124,142,3,172, 63,251,107,97,100,143,3,0,0,184,255,0,64,18,37,191, 107,246,6,184,202,73,3,216,93,2,237,108,128,33,13,219, 47,13,236,47,10,84,16,0,180,54,0,176,170,79,159,62, 95,18,2,175,109,0,40,213,212,187,74,135,241,113,0,0, 0,247,159,212,77,18,37,191,123,208,65,7,253,192,252,125, 74,196,108,0,187,52,176,22,5,234,23,46,10,212,51,166, 14,32,127,0,48,107,0,60,220,163,71,15,114,224,53,4, 0,174,179,127,189,134,156,61,14,0,0,224,254,3,0,181, 34,249,93,147,6,120,193,252,189,204,153,13,96,23,5,58, 194,104,100,195,246,59,4,238,26,81,7,144,95,248,95,225, 3,3,0,63,173,212,14,128,164,0,10,183,191,158,156,61, 162,152,12,113,223,106,165,255,151,223,53,83,1,223,55,127, 159,106,180,40,172,3,152,105,52,57,98,58,160,91,7,224, 111,14,84,16,0,124,212,169,83,39,0,32,1,246,227,236, 17,142,4,113,223,106,163,255,151,223,29,49,98,196,70,243, 247,247,141,150,120,211,1,227,234,0,236,122,0,157,157,245, 0,242,7,0,51,11,224,15,237,219,183,7,0,170,204,126, 235,236,175,198,217,19,2,38,5,192,253,39,5,80,115,247, 95,126,215,248,223,63,154,191,79,15,235,0,78,54,58,161, 225,155,45,130,181,30,192,176,136,245,0,252,66,192,252,242, 255,13,95,111,2,180,89,155,19,0,0,149,183,31,103,143, 3,40,135,253,38,118,24,60,102,54,219,234,5,0,0,0, 0,64,197,20,110,10,180,201,252,125,134,209,210,134,111,54, 7,114,215,3,80,33,224,192,134,111,118,7,140,42,4,44, 8,0,190,104,221,186,117,34,0,192,160,80,176,166,161,77, 176,209,56,183,207,141,147,251,155,209,182,2,157,37,66,91, 76,27,250,216,232,61,211,174,214,26,199,184,200,64,83,175, 26,238,0,87,154,77,183,100,247,95,141,205,139,141,173,217, 190,79,215,68,215,70,215,72,215,138,182,83,91,74,98,251, 79,178,228,119,229,127,205,223,103,198,20,2,142,245,10,1, 227,22,4,202,31,0,198,140,25,243,101,37,86,1,204,5, 0,174,49,29,150,70,181,219,248,145,162,50,73,96,185,206, 116,136,83,114,112,144,73,0,0,141,254,255,228,65,243,171, 198,206,116,29,190,174,129,174,5,176,77,251,71,197,93,13, 80,254,55,4,0,191,16,208,95,16,168,127,88,8,232,111, 12,148,23,0,180,10,1,160,109,53,229,16,253,231,151,153, 198,183,137,78,7,85,88,31,154,142,112,84,141,108,53,108, 71,255,190,162,162,1,178,249,67,70,250,180,255,42,110,255, 73,79,1,133,231,127,86,88,8,168,5,129,220,237,129,143, 12,103,2,28,220,240,205,198,64,81,51,1,242,2,128,54, 213,10,0,166,178,33,248,255,232,120,80,149,233,70,83,83, 209,148,224,206,38,106,244,239,75,209,0,21,160,202,86,238, 57,170,246,246,95,35,0,112,182,209,105,49,51,1,70,135, 51,1,246,107,136,222,25,176,182,0,192,204,115,8,62,73, 211,73,253,197,60,247,130,169,3,80,116,192,32,81,48,126, 212,168,196,222,124,85,249,247,41,66,3,210,143,178,123,56, 98,91,110,70,120,111,165,129,167,13,230,185,174,20,1,182, 44,198,73,240,245,203,197,126,127,244,31,23,210,255,34,205, 239,79,215,70,215,72,215,170,123,120,237,112,0,180,127,238, 127,81,0,192,159,9,48,62,156,10,56,52,205,84,192,130, 0,160,93,53,1,128,156,255,127,71,116,62,202,253,171,240, 111,124,68,24,138,14,32,218,254,190,230,90,173,49,5,61, 81,157,252,7,53,4,1,165,186,255,73,185,126,217,218,31, 53,250,95,101,58,123,133,253,255,154,33,42,160,107,160,107, 209,183,10,195,192,252,254,105,255,53,112,255,206,113,166,2, 206,207,48,21,48,106,79,128,252,214,0,168,54,0,136,26, 249,111,53,143,45,161,16,37,239,31,192,8,243,67,222,24, 113,93,69,242,77,116,128,25,85,237,215,47,91,251,253,209, 191,96,160,147,83,221,255,90,204,168,79,182,143,168,226,250, 7,0,160,190,219,127,141,220,63,11,0,75,27,162,247,4, 16,0,216,61,1,122,215,44,0,68,205,103,255,54,141,164, 224,14,192,76,24,13,222,140,232,224,149,211,163,3,108,72, 244,245,203,198,254,168,209,191,128,192,125,77,84,206,95,54, 119,225,254,211,254,107,184,255,168,34,0,136,90,11,224,104, 103,45,128,184,77,129,106,19,0,228,252,59,231,144,3,87, 77,192,59,166,145,126,90,7,179,6,20,178,149,173,178,57, 91,10,111,23,243,35,174,149,234,246,82,43,201,215,47,221, 232,223,86,251,71,57,255,118,220,119,84,229,237,191,70,82, 0,231,134,0,176,44,207,197,128,106,11,0,182,230,48,242, 159,54,124,68,240,126,29,207,24,144,237,61,114,32,121,63, 156,167,41,62,116,112,181,123,253,178,25,253,251,83,253,100, 99,23,238,55,74,64,251,175,65,0,88,24,2,192,116,7, 0,70,20,27,0,82,139,0,85,35,0,168,224,47,219,156, 191,70,191,31,117,218,177,238,167,231,188,159,67,62,78,57, 61,191,176,39,201,139,125,148,187,253,86,219,245,203,100,127, 166,209,191,206,221,47,248,27,145,160,168,16,41,0,218,127, 141,0,64,220,106,128,71,68,0,128,93,13,176,96,0,216, 161,218,0,96,99,14,68,185,204,235,188,234,89,203,114,248, 17,170,186,215,125,239,186,4,71,1,42,209,126,171,233,250, 165,179,63,155,209,255,58,111,244,47,219,184,255,216,95,11, 237,191,198,0,32,106,57,224,218,3,128,241,57,140,62,126, 234,117,94,239,238,180,107,48,99,216,240,212,69,173,101,201, 70,217,234,218,254,78,14,63,194,190,94,206,87,68,223,139, 14,48,145,215,47,157,253,153,70,255,58,103,127,52,215,55, 97,53,33,0,64,125,183,255,26,185,127,153,0,96,100,93, 0,128,22,249,201,229,253,126,193,95,61,56,127,23,2,252, 194,192,92,174,157,191,216,199,162,132,166,1,42,213,126,171, 229,250,197,217,159,205,232,127,145,23,65,123,43,129,145,32, 0,160,190,219,127,141,220,191,243,242,4,128,174,53,5,0, 90,225,175,144,247,215,139,243,183,42,100,59,207,229,222,232, 112,109,194,66,191,149,86,181,95,191,76,163,127,105,173,23, 202,93,222,192,180,46,148,172,246,95,35,41,0,11,0,118, 67,32,1,192,140,134,236,118,4,172,29,0,88,150,35,69, 2,0,249,3,128,63,245,235,61,102,3,212,204,245,203,102, 244,47,249,251,109,48,37,20,37,173,253,215,40,0,204,13, 1,96,82,93,1,192,128,2,223,15,0,100,127,237,186,123, 215,239,227,28,83,8,245,30,2,173,150,235,23,101,127,54, 163,127,233,115,15,18,190,203,253,199,254,26,104,255,0,64, 66,1,160,3,0,80,54,0,104,242,174,223,22,0,32,145, 215,207,183,63,219,209,127,167,136,2,192,11,18,152,2,0, 0,104,255,0,64,141,0,64,3,0,80,54,0,40,198,245, 175,119,7,80,13,215,207,183,63,219,209,191,255,58,169,19, 247,31,251,235,172,255,0,0,0,0,0,0,0,168,9,0, 200,101,244,255,167,136,37,179,185,255,216,15,0,0,0,0, 0,0,128,18,120,253,10,25,253,115,255,81,45,68,192,0, 0,0,0,0,0,0,234,14,0,10,29,253,115,255,17,0, 0,0,0,0,0,0,33,208,4,118,128,133,142,254,185,255, 164,0,0,0,0,0,0,0,0,232,0,19,214,1,22,99, 244,207,253,7,0,0,0,0,0,0,168,99,0,64,133,169, 82,29,72,49,70,255,8,37,177,253,83,4,8,0,0,0, 0,64,221,118,128,71,143,26,85,148,209,63,66,0,0,0, 0,0,0,0,40,65,29,96,190,163,127,96,0,213,74,4, 12,0,0,0,0,0,0,160,238,58,192,66,114,255,164,3, 16,0,0,0,0,0,0,0,69,96,9,45,130,42,100,244, 223,137,251,79,17,32,69,128,0,0,0,0,0,224,0,146, 215,1,22,58,250,231,254,3,0,0,0,0,0,0,0,0, 56,128,4,118,128,133,142,254,185,255,0,0,75,1,3,0, 0,0,0,128,3,72,88,7,88,140,209,63,247,31,0,0, 0,0,0,0,0,0,224,7,156,176,14,176,24,163,127,238, 63,34,5,0,0,0,0,0,0,14,32,65,29,96,177,70, 255,220,127,4,0,0,0,85,5,0,77,0,64,217,0,160, 201,187,126,91,140,179,32,4,90,253,29,160,63,170,255,107, 219,14,121,141,254,73,1,144,2,0,0,0,128,170,2,128, 238,0,64,217,0,160,187,119,253,62,6,0,170,190,3,140, 26,213,223,179,231,94,121,71,9,0,0,0,0,0,0,0, 170,166,1,141,106,104,4,0,202,4,0,186,214,238,123,223, 107,108,162,3,172,242,14,48,106,84,175,165,128,243,173,17, 0,0,0,0,138,0,1,128,170,105,64,203,35,70,41,0, 64,105,0,96,185,231,36,214,54,182,161,3,172,226,14,48, 110,84,239,219,159,237,232,31,0,0,0,0,0,0,160,170, 0,224,173,28,71,161,159,122,29,221,140,97,195,235,198,249, 203,214,22,185,224,28,67,248,186,214,238,251,23,53,180,226, 7,89,197,29,96,177,42,255,233,192,17,41,0,0,160,42, 1,64,234,155,67,26,224,167,158,19,123,119,167,93,235,2, 2,100,163,108,117,109,127,39,7,120,234,235,133,255,183,25, 39,209,139,14,173,106,59,192,98,86,254,3,0,8,0,0, 0,170,22,0,214,228,16,138,94,102,70,173,108,194,241,181, 150,229,48,130,215,53,118,223,187,46,161,249,255,122,9,129, 166,27,213,187,246,231,50,250,39,5,64,10,0,0,0,0, 170,14,0,52,26,29,145,101,20,64,83,217,62,234,180,99, 221,59,255,247,141,3,207,118,10,165,174,237,54,111,148,56, 37,193,225,255,90,239,0,51,141,234,173,253,185,142,254,1, 0,0,0,0,0,0,170,14,0,164,141,166,35,235,146,229, 103,244,48,122,223,75,5,212,147,4,64,61,178,188,86,186, 166,27,61,39,241,97,130,71,255,245,208,1,102,26,213,91, 251,115,29,253,3,0,0,0,69,128,0,64,85,2,128,244, 166,113,76,237,114,88,212,70,33,112,229,193,253,194,192,90, 148,108,148,173,178,121,236,232,209,89,93,35,93,203,55,35, 64,41,215,169,151,116,128,229,235,0,179,25,213,203,254,124, 70,255,0,0,0,0,0,0,0,85,11,0,22,2,186,208, 1,20,172,46,49,206,255,198,198,214,92,159,42,238,0,75, 81,249,79,7,142,72,1,0,0,85,9,0,27,34,156,212, 198,12,53,1,0,64,230,156,255,198,136,168,200,134,28,234, 6,80,249,59,192,168,81,253,95,204,255,229,236,59,21,80, 249,15,0,32,0,0,0,168,74,0,48,214,4,31,68,64, 128,138,214,84,185,30,53,69,144,16,224,161,177,83,253,116, 205,182,69,56,127,93,227,174,132,64,171,186,3,92,225,141, 234,253,17,190,5,1,45,5,156,207,232,159,20,0,237,31, 0,0,0,170,110,55,64,57,166,13,105,138,250,180,128,141, 86,177,83,238,90,235,217,103,155,3,175,229,14,64,35,121, 93,11,93,19,93,155,183,210,92,191,13,53,228,252,107,185, 3,236,20,134,246,255,148,166,166,69,207,125,214,212,33,175, 209,63,0,64,251,7,0,0,128,170,220,14,88,14,77,249, 105,230,247,23,87,186,166,77,116,128,137,155,6,152,9,4, 242,25,253,3,0,180,127,138,0,1,128,29,170,185,1,105, 68,251,97,29,79,241,43,150,116,13,147,94,237,95,239,29, 96,54,32,176,178,192,189,52,184,255,216,15,0,212,33,0, 184,114,79,172,212,143,71,53,160,168,215,107,161,154,15,118, 220,57,216,134,51,207,90,202,253,107,133,191,139,250,239,23, 28,58,102,76,69,238,111,173,63,158,110,51,170,82,125,175, 118,255,83,222,223,7,129,191,182,237,208,98,103,192,106,61, 127,30,167,253,243,248,161,68,0,242,33,72,173,87,175,77, 107,180,115,157,182,175,253,131,233,4,183,182,222,161,238,157, 253,22,115,29,116,45,116,77,116,109,116,141,88,219,191,182, 71,64,126,68,32,215,209,63,35,56,68,13,0,17,128,29, 146,30,66,34,4,136,253,245,220,1,106,212,191,194,155,26, 72,10,128,246,15,0,0,0,0,0,29,0,246,211,1,2, 0,180,127,218,63,0,80,155,0,64,17,9,29,32,29,32, 0,64,251,167,255,6,0,0,0,58,0,236,7,0,0,0, 218,63,253,55,0,64,10,0,33,58,64,58,112,4,0,3, 0,0,0,66,56,80,0,0,1,0,0,0,0,64,8,16, 251,73,1,144,2,160,253,3,0,0,0,0,64,7,128,253, 0,0,247,31,251,1,0,0,128,16,36,29,0,246,3,0, 220,127,236,167,255,6,0,104,64,116,0,216,15,0,112,255, 177,159,254,27,0,32,5,128,16,69,128,136,246,79,10,0, 0,0,0,16,29,32,231,143,104,255,0,0,0,0,0,240, 3,32,4,74,10,128,246,79,251,231,254,3,0,0,0,63, 0,58,64,0,128,246,79,251,231,254,3,0,132,32,249,1, 208,1,2,0,180,127,218,63,2,0,104,64,116,128,116,128, 0,0,237,159,246,15,0,0,0,116,0,136,34,40,206,31, 209,126,72,1,0,0,0,0,162,3,228,252,17,237,7,0, 0,0,0,0,236,39,4,74,10,128,246,79,251,7,0,0, 0,58,0,236,167,3,4,0,104,255,180,127,0,128,34,64, 58,0,236,167,3,4,0,104,255,244,223,0,0,0,64,7, 128,253,0,0,0,64,251,167,255,6,0,72,1,32,68,7, 72,7,142,0,96,0,0,0,64,8,0,0,0,16,0,0, 0,0,0,132,0,177,159,20,0,247,31,251,1,0,0,0, 0,160,3,192,126,0,128,251,143,253,0,0,0,64,8,146, 14,0,251,1,0,238,63,246,211,127,3,0,52,32,58,0, 236,7,0,104,255,180,127,250,111,0,128,20,0,162,8,138, 34,64,68,251,7,0,1,0,0,0,209,1,114,254,136,246, 15,0,0,0,0,0,63,0,66,160,164,0,104,255,180,127, 238,63,0,0,0,240,3,160,3,4,0,104,255,180,127,238, 63,0,64,8,146,14,144,14,16,0,160,253,211,254,41,2, 4,0,104,16,116,128,116,128,0,0,237,159,246,15,0,0, 0,116,0,136,34,40,206,31,209,126,72,1,0,0,0,0, 162,3,228,252,17,237,7,0,0,0,0,0,236,39,4,74, 10,128,246,79,251,7,0,0,0,58,0,236,167,3,4,0, 104,255,180,127,0,128,34,64,58,0,236,167,3,4,0,104, 255,244,223,0,0,0,64,7,128,253,0,0,0,64,251,167, 255,6,0,72,1,32,68,7,72,7,142,72,1,0,0,0, 0,66,0,0,247,20,1,0,0,0,0,64,8,16,251,73, 1,112,255,177,31,0,0,0,0,0,58,0,236,7,0,184, 255,216,15,0,0,0,165,108,64,8,37,89,73,7,128,122, 179,31,0,160,255,174,146,246,11,0,32,68,7,2,0,0, 0,0,0,0,0,0,32,68,7,146,176,223,31,133,113,245, 93,4,200,239,23,0,0,0,16,0,0,0,32,0,128,223, 47,0,64,17,32,33,80,138,160,106,181,8,144,20,0,237, 159,34,88,0,0,0,0,0,232,0,1,0,218,63,237,31, 0,0,0,88,136,132,14,144,14,16,0,160,253,211,254,89, 200,8,0,0,0,232,0,233,0,1,0,218,63,237,31,0, 0,0,72,1,160,90,45,130,170,183,243,7,224,105,255,164, 0,0,0,0,0,209,1,2,0,180,9,218,63,0,0,0, 0,0,132,64,9,129,146,2,160,253,211,254,1,0,0,0, 0,160,3,164,3,4,0,104,255,180,127,0,0,0,32,132, 72,7,72,7,8,0,208,254,105,255,164,48,0,0,0,128, 14,144,14,16,0,160,253,211,254,1,0,0,128,14,0,209, 129,80,4,136,104,255,164,0,0,0,0,0,209,1,2,0, 136,246,15,0,0,0,0,0,246,19,2,37,5,64,251,167, 253,3,0,0,0,29,0,246,211,1,2,0,180,127,218,63, 0,80,8,0,184,114,79,172,212,143,71,221,128,74,158,79, 18,31,183,207,113,125,202,255,184,223,126,235,237,252,171,225, 247,75,251,167,253,39,253,252,137,0,144,67,100,4,196,8, 136,8,0,237,159,246,207,44,0,0,128,14,0,209,129,80, 4,136,104,255,164,0,0,0,0,0,209,1,2,0,136,246, 15,0,0,0,0,0,246,19,2,37,5,64,251,167,253,3, 0,0,0,29,0,246,211,1,2,0,180,127,218,63,0,64, 17,32,29,0,246,211,1,2,0,180,127,250,111,0,0,0, 160,3,192,126,0,0,0,160,253,211,127,3,0,164,0,16, 162,3,164,8,16,1,192,0,0,0,128,16,0,0,0,32, 0,0,0,0,0,8,1,98,63,29,32,41,0,218,63,237, 31,0,0,0,232,0,176,159,14,16,0,160,253,211,254,1, 0,66,168,116,0,216,79,7,8,0,208,254,73,129,1,0, 52,32,58,0,58,64,0,0,0,160,253,3,0,0,0,41, 0,68,17,20,69,128,180,9,218,63,41,0,0,0,0,64, 116,128,0,0,162,253,3,0,0,64,156,182,52,182,109,113, 3,154,0,0,236,79,136,253,77,94,7,162,182,156,52,251, 11,253,253,145,2,160,253,39,185,253,3,0,21,6,128,63, 120,29,80,119,58,0,236,79,136,253,221,189,14,228,227,4, 118,128,133,254,254,0,0,218,127,146,219,63,0,80,97,0, 120,175,177,169,197,13,24,213,208,72,104,141,14,48,17,246, 171,173,186,109,87,109,57,105,246,23,250,251,3,0,104,255, 149,110,255,20,1,38,24,0,214,54,182,105,113,3,150,55, 180,198,169,211,1,38,194,126,181,85,183,237,170,45,39,205, 254,66,127,127,0,0,237,191,210,237,31,0,72,48,0,44, 106,104,213,226,6,188,149,7,69,82,4,136,42,161,183,188, 209,179,218,114,210,108,40,244,247,71,17,32,237,191,210,237, 159,20,64,130,1,160,151,209,54,47,15,217,55,199,48,36, 0,128,202,173,190,94,248,83,109,184,87,2,237,40,244,247, 7,0,208,254,43,221,254,1,128,4,3,128,180,206,35,201, 53,57,134,146,8,1,98,127,185,191,115,141,23,58,95,87, 193,252,103,161,246,23,242,251,35,5,64,251,79,114,251,7, 0,170,0,0,166,120,97,72,209,228,136,28,70,33,116,0, 216,95,206,239,83,219,244,71,205,83,42,24,254,47,212,254, 66,126,127,0,0,237,63,201,237,31,0,168,2,0,144,62, 244,70,33,27,77,3,235,66,168,141,14,176,202,236,87,155, 220,232,117,126,31,86,184,250,185,24,246,231,251,251,3,0, 104,255,31,38,176,250,31,0,168,50,0,56,196,203,41,73, 111,154,134,213,14,7,79,7,88,37,246,171,45,190,233,57, 202,106,152,186,90,12,251,243,253,253,1,0,180,255,36,79, 221,6,0,170,4,0,164,27,27,91,71,118,66,93,232,0, 80,133,213,37,166,243,83,155,173,21,27,243,249,253,81,4, 72,251,79,50,0,1,0,85,4,0,90,90,114,67,68,35, 219,152,33,39,9,0,160,82,231,60,253,176,167,164,182,218, 84,67,118,230,243,251,3,0,104,255,0,0,0,80,52,25, 107,130,15,34,58,33,21,157,168,242,52,106,138,18,33,64, 236,47,213,84,39,181,185,109,17,157,159,218,104,215,26,180, 63,215,223,31,41,0,218,63,0,0,0,20,29,2,162,70, 34,238,98,37,90,133,74,185,39,173,71,61,118,244,104,58, 0,236,47,120,244,171,182,164,54,165,182,245,86,154,246,183, 161,138,58,191,82,220,255,92,126,127,0,0,237,31,0,0, 0,74,18,142,140,202,73,34,84,73,169,77,54,213,1,0, 242,251,67,245,40,0,160,202,36,34,253,48,13,141,34,84, 14,169,13,86,107,181,115,41,127,191,252,254,16,0,0,0, 84,92,90,104,66,171,77,69,229,163,16,42,133,212,214,212, 230,166,36,112,141,127,126,127,8,1,0,53,3,0,86,90, 111,90,155,78,104,231,41,109,63,169,253,204,183,182,222,129, 198,139,10,210,22,211,142,212,150,212,166,212,182,212,198,122, 213,185,211,207,230,247,71,219,65,0,0,0,64,17,28,246, 99,63,246,99,63,246,99,63,0,64,3,192,126,236,199,126, 236,199,126,236,7,0,16,63,0,236,199,126,236,199,126,4, 0,32,58,0,236,199,126,236,199,126,4,0,240,3,64,8, 33,68,10,0,0,0,0,16,66,8,1,0,0,0,0,128, 253,216,143,253,216,143,253,0,0,0,192,15,0,251,177,31, 251,177,31,251,1,0,0,0,209,1,96,63,246,99,63,253, 32,0,0,0,208,1,96,63,246,99,63,246,35,0,128,20, 0,66,8,33,0,8,0,0,0,16,66,8,1,0,0,0, 0,192,15,0,251,177,31,251,177,31,251,1,0,26,0,246, 99,63,246,99,63,246,99,63,0,128,248,1,96,63,246,99, 63,246,35,0,0,209,1,96,63,246,99,63,246,35,0,128, 31,0,66,8,33,250,127,0,128,6,128,16,66,136,254,31, 0,160,1,96,63,246,99,63,246,99,63,246,3,0,52,0, 236,199,126,236,199,126,236,199,126,0,0,241,3,192,126,236, 199,126,236,71,0,0,162,3,192,126,236,199,126,236,7,0, 0,0,126,0,8,33,132,72,1,0,0,0,0,66,8,33, 0,0,0,0,0,176,31,251,177,31,251,177,31,0,0,0, 248,1,96,63,246,99,63,246,99,63,0,64,17,32,63,0, 236,199,126,236,199,126,4,0,0,0,116,0,216,143,253,216, 143,253,8,0,32,5,128,16,66,8,0,2,0,0,0,132, 16,66,0,0,0,96,78,192,149,123,98,60,94,253,143,219, 231,184,62,60,78,251,231,250,240,120,242,30,39,2,64,4, 0,251,177,31,251,177,31,251,137,0,0,0,136,14,0,251, 177,31,251,177,159,34,64,0,0,209,1,96,63,246,99,63, 246,3,0,0,0,63,0,132,16,66,164,0,0,0,0,0, 33,132,0,0,0,0,0,224,7,128,253,216,143,253,216,143, 253,0,0,0,192,15,0,251,177,31,251,177,31,251,1,0, 138,0,249,1,96,63,246,99,63,246,35,0,0,0,160,3, 192,126,236,199,126,236,71,0,0,63,0,132,16,66,244,255, 0,0,13,0,33,132,16,253,63,0,64,3,192,126,236,199, 126,236,199,126,236,7,0,104,0,216,143,253,216,143,253,216, 143,253,0,64,101,244,101,171,118,8,21,77,116,128,216,143, 253,8,0,0,0,16,0,128,3,192,126,236,71,0,0,0, 128,0,128,218,85,155,54,109,82,29,209,217,103,159,29,92, 114,201,37,193,63,252,195,63,32,84,147,82,251,86,59,87, 123,87,187,7,0,0,0,132,234,22,0,212,9,46,94,188, 56,88,186,116,105,48,113,226,196,84,135,132,80,45,75,237, 92,237,93,237,190,210,16,0,0,20,17,0,8,1,98,127, 189,181,159,66,239,191,222,175,206,16,199,128,234,77,182,221, 3,0,0,0,0,0,0,212,229,253,87,56,148,145,63,170, 215,72,128,218,63,0,0,0,0,0,0,64,93,222,127,229, 68,113,6,168,94,165,246,15,0,0,0,0,0,0,80,151, 247,95,133,81,56,2,84,175,82,251,7,0,0,0,84,135, 162,253,0,0,8,0,0,0,232,192,17,0,0,0,32,4, 0,0,0,116,224,164,0,72,1,144,2,64,8,0,0,0, 0,0,0,0,0,0,0,16,2,0,0,0,0,0,0,0, 0,0,0,132,0,0,0,0,0,0,0,0,0,0,0,33, 0,0,0,0,0,16,237,135,34,64,132,0,0,0,128,14, 28,209,126,0,0,132,0,0,0,128,14,156,20,0,41,0, 82,0,8,1,0,0,0,0,0,0,0,0,0,0,66,0, 0,0,0,0,0,0,0,0,0,128,16,0,0,0,0,0, 0,0,0,0,0,32,4,0,0,0,0,0,162,253,84,107, 17,224,141,55,222,24,188,247,222,123,57,233,221,119,223,13, 94,123,237,181,224,238,187,239,14,46,188,240,194,96,246,236, 217,193,97,135,29,86,212,14,126,213,170,85,193,199,31,127, 156,149,230,206,157,91,19,215,161,210,231,144,233,251,159,124, 242,73,0,0,0,160,3,71,180,159,90,1,128,87,94,121, 37,40,198,241,63,255,243,63,193,207,127,254,243,96,201,146, 37,69,57,175,119,222,121,39,235,239,94,187,118,109,77,92, 135,74,159,67,166,239,255,207,255,252,79,0,0,0,160,3, 39,5,64,10,160,86,82,0,197,114,58,246,248,234,171,175, 130,245,235,215,7,115,230,204,201,251,156,38,78,156,24,252, 237,111,127,203,250,59,127,247,187,223,213,196,117,168,244,57, 0,0,0,0,0,0,0,0,0,0,64,193,199,255,254,239, 255,6,215,95,127,125,94,231,116,221,117,215,229,252,125,203, 150,45,75,252,117,168,244,57,0,0,0,0,0,0,0,0, 0,0,64,81,142,191,255,253,239,193,165,151,94,154,243,57, 125,240,193,7,57,127,215,83,79,61,149,248,235,80,233,115, 0,0,0,0,0,0,0,0,0,0,128,162,29,155,54,109, 10,38,79,158,156,245,249,76,155,54,45,229,172,114,61,254, 242,151,191,4,135,31,126,120,162,175,67,165,207,1,0,0, 0,0,0,68,251,161,8,176,168,71,46,213,227,183,223,126, 123,222,223,115,222,121,231,37,250,58,84,250,28,0,0,0, 128,14,28,209,126,0,128,22,163,198,75,46,185,164,89,154, 42,246,208,67,15,5,111,189,245,86,240,229,151,95,102,229, 116,62,251,236,179,172,167,166,125,244,209,71,121,59,55,217, 146,228,235,80,233,115,0,0,0,0,58,112,82,0,164,0, 72,1,100,213,233,207,154,53,43,120,251,237,183,179,114,60, 75,151,46,205,120,46,39,158,120,98,218,207,248,217,207,126, 22,108,220,184,49,246,249,205,155,55,7,227,198,141,75,236, 117,168,244,57,0,0,0,0,0,0,0,0,0,0,64,214, 157,190,28,238,127,252,199,127,100,116,58,183,220,114,75,198, 115,185,239,190,251,210,126,134,70,188,15,60,240,64,218,215, 172,94,189,58,177,215,161,210,231,0,0,0,0,0,0,0, 0,0,0,0,57,117,250,90,181,47,211,177,102,205,154,140, 159,163,239,74,119,204,152,49,35,53,122,77,119,104,1,161, 164,94,135,74,159,3,0,0,0,0,0,0,0,0,0,0, 228,212,233,31,127,252,241,25,157,206,211,79,63,157,246,51, 22,47,94,156,246,253,191,249,205,111,154,95,251,231,63,255, 57,246,117,219,182,109,75,45,36,148,196,235,80,233,115,0, 0,0,128,146,168,151,209,162,134,86,193,218,198,54,193,123, 141,77,219,117,224,8,21,34,181,41,181,45,181,177,94,20, 1,150,221,241,41,244,156,233,120,248,225,135,211,126,198,99, 143,61,150,246,253,15,62,248,96,243,107,159,123,238,185,180, 175,213,66,66,73,188,14,149,62,7,0,0,0,40,170,166, 152,14,121,157,233,156,183,53,182,197,81,161,178,72,109,77, 109,78,109,15,0,40,143,227,91,184,112,97,65,211,207,84, 149,254,167,63,253,41,235,149,254,86,172,88,145,246,181,31, 126,248,97,34,175,67,165,207,1,0,0,0,138,162,81,13, 141,193,135,140,244,81,133,165,54,168,182,72,10,160,180,142, 239,182,219,110,203,232,116,238,188,243,206,216,247,159,121,230, 153,25,23,249,241,71,185,91,182,108,73,187,234,221,244,233, 211,19,119,29,42,125,14,0,0,0,80,144,204,137,5,55, 54,182,198,249,160,170,146,218,100,19,0,80,146,78,95,91, 241,110,221,186,53,163,211,153,63,127,126,236,103,188,240,194, 11,105,223,251,210,75,47,109,247,158,13,27,54,164,125,207, 93,119,221,149,184,235,80,233,115,0,0,0,128,188,101,44, 10,54,164,25,245,191,101,158,91,222,208,58,53,34,235,30, 194,2,69,112,216,95,40,112,118,15,35,78,106,91,111,165, 105,127,106,155,93,1,128,162,118,250,23,95,124,113,106,39, 190,76,199,39,159,124,18,251,25,99,199,142,13,254,251,191, 255,59,237,251,85,217,238,191,239,138,43,174,72,251,158,95, 255,250,215,137,186,14,213,112,14,0,0,0,144,183,243,255, 32,162,243,85,62,118,141,41,206,234,91,165,97,88,0,160, 246,236,87,91,83,155,139,170,59,249,160,138,32,32,41,0, 240,249,231,159,167,66,244,103,157,117,86,106,245,57,45,213, 251,248,227,143,103,53,223,60,155,21,250,86,174,92,153,246, 189,218,22,248,168,163,142,138,220,50,88,187,220,165,59,52, 34,78,202,117,168,134,115,0,0,0,128,188,70,97,81,35, 255,141,166,3,30,145,198,241,215,187,3,68,165,149,218,222, 198,8,8,80,91,173,133,232,83,146,246,2,56,247,220,115, 99,191,127,221,186,117,105,223,251,238,187,239,198,190,247,159, 254,233,159,210,190,119,237,218,181,137,185,14,213,112,14,0, 0,0,144,179,162,114,254,111,154,78,182,11,35,96,84,97, 169,13,190,25,1,167,106,179,0,64,121,156,78,186,105,111, 227,199,143,15,190,248,226,139,188,167,244,105,69,187,116,135, 66,226,73,184,14,213,114,14,0,0,0,144,115,181,127,148, 243,111,71,8,28,251,171,196,254,118,49,16,80,233,217,1, 245,176,29,176,70,232,233,182,232,205,148,199,255,234,171,175, 130,41,83,166,196,190,127,230,204,153,69,217,131,160,210,215, 161,90,206,1,0,0,0,114,146,63,213,79,33,215,46,56, 64,236,175,50,251,213,38,253,116,128,218,46,0,80,58,167, 163,77,123,142,61,246,216,180,223,253,243,159,255,60,237,103, 252,219,191,253,91,112,228,145,71,166,213,191,255,251,191,167, 253,140,167,158,122,170,234,175,67,181,156,3,0,0,0,228, 180,200,143,95,240,55,130,98,63,0,160,74,237,87,219,244, 11,3,43,185,88,80,173,2,192,111,127,251,219,224,210,75, 47,205,248,189,114,72,153,138,248,138,113,104,13,129,74,140, 190,179,189,14,213,116,14,0,0,0,144,181,214,121,163,127, 85,94,227,216,1,128,106,182,95,109,212,109,179,235,42,24, 5,168,53,0,248,227,31,255,152,202,215,103,227,108,165,155, 110,186,41,40,215,145,169,248,174,146,215,161,154,206,1,0, 0,0,178,94,219,223,31,77,229,58,213,143,34,64,84,110, 245,245,106,86,212,134,147,186,119,64,57,1,224,87,191,250, 85,179,254,249,159,255,57,120,245,213,87,131,251,239,191,63, 184,250,234,171,131,211,79,63,61,181,234,158,150,243,205,229, 123,127,241,139,95,148,13,0,50,77,191,171,228,117,168,166, 115,0,0,0,128,172,180,200,11,255,191,149,199,72,10,0, 64,149,144,191,88,208,162,132,238,25,80,77,75,1,231,42, 237,88,167,2,191,114,29,155,55,111,78,45,31,92,237,206, 175,210,231,0,0,0,0,89,105,173,23,74,213,42,108,0, 0,246,39,193,126,181,85,183,237,174,173,80,234,170,150,246, 2,200,85,119,223,125,119,80,238,67,11,231,0,0,0,0, 0,80,4,249,91,250,230,51,165,10,7,136,253,213,48,117, 245,189,10,213,1,212,51,0,252,230,55,191,41,59,0,188, 253,246,219,0,0,0,0,0,20,67,108,48,211,82,0,64, 114,236,239,238,181,223,143,77,29,0,0,80,190,78,255,228, 147,79,14,42,113,104,73,97,45,31,12,0,0,0,0,0, 0,0,0,212,41,0,52,121,237,119,11,0,80,214,78,255, 193,7,31,204,232,172,181,147,223,73,39,157,148,147,158,120, 226,137,140,159,155,110,85,65,0,0,0,72,28,0,184,114, 79,172,212,143,227,244,91,170,220,215,159,199,11,123,60,234, 254,37,205,174,164,2,192,239,127,255,251,180,223,167,181,1, 38,79,158,156,243,231,46,92,184,48,35,0,124,240,193,7, 0,64,1,223,175,233,133,83,167,78,205,75,233,162,47,133, 0,64,37,127,143,68,0,80,222,17,0,212,80,53,237,151, 89,0,229,113,58,167,158,122,106,70,39,253,47,255,242,47, 121,127,254,31,254,240,135,180,159,253,247,191,255,61,152,54, 109,26,0,80,129,165,136,117,111,136,0,212,40,0,212,91, 8,28,232,73,54,64,85,3,0,212,99,10,224,233,167,159, 206,42,252,95,202,207,191,243,206,59,1,128,10,0,192,39, 159,124,2,0,0,0,0,0,2,0,234,17,0,180,42,221, 95,255,250,215,140,142,98,206,156,57,121,127,199,121,231,157, 151,213,66,58,0,0,0,0,0,212,121,8,21,0,0,0, 0,128,242,57,157,229,203,151,103,116,18,133,126,215,216,177, 99,131,45,91,182,100,252,158,185,115,231,2,0,0,0,0, 0,0,0,0,0,0,0,80,14,167,163,37,107,51,29,143, 62,250,104,193,223,179,97,195,134,140,223,179,102,205,26,0, 0,0,0,0,72,1,0,64,245,120,253,40,2,44,175,211, 209,182,189,217,140,204,79,59,237,180,130,191,235,138,43,174, 200,106,151,60,0,0,0,0,0,0,0,0,0,0,168,107, 0,120,230,153,103,242,202,153,231,162,5,11,22,100,116,16, 159,125,246,89,94,59,232,249,154,52,105,82,198,109,134,245, 188,94,87,238,235,80,13,247,162,144,239,47,116,107,98,0, 0,0,0,0,88,8,8,0,168,162,20,0,66,73,20,0, 0,0,96,63,0,0,0,32,4,0,0,0,132,80,1,0, 0,0,0,64,8,0,0,0,0,0,0,0,0,0,0,16, 2,0,0,0,28,32,0,196,245,163,8,16,33,0,0,0, 0,0,0,0,0,0,0,64,8,0,0,0,0,0,236,7, 0,72,1,32,4,0,0,0,56,64,236,7,0,0,0,132, 0,0,0,128,16,56,0,0,0,0,0,8,1,0,0,0, 0,0,0,0,0,0,0,66,0,0,0,64,10,128,34,64, 0,128,34,64,132,0,0,0,0,0,64,0,0,0,128,16, 0,0,0,0,0,164,0,0,0,82,0,8,1,0,0,0, 0,0,0,0,0,0,0,66,0,0,69,128,0,0,0,0, 0,0,0,8,1,0,0,0,0,192,245,3,0,0,0,132, 0,0,82,0,56,48,0,128,251,7,0,32,4,0,0,0, 56,48,0,128,251,7,0,32,4,0,0,0,164,0,0,0, 82,0,56,2,4,0,0,0,56,64,236,7,0,0,0,132, 0,0,0,128,16,42,0,0,0,0,0,8,1,0,0,0, 0,0,0,0,0,0,0,66,0,0,0,128,3,4,128,0, 0,138,0,43,171,227,142,59,46,56,236,176,195,112,102,8, 0,0,0,112,96,0,0,247,175,82,0,176,112,225,194,224, 186,235,174,219,78,87,95,125,117,176,106,213,170,224,140,51, 206,8,102,206,156,89,180,239,91,179,102,77,176,97,195,134, 224,137,39,158,192,153,33,0,0,0,32,5,0,0,144,2, 168,20,0,92,117,213,85,41,135,156,73,143,61,246,88,176, 100,201,146,130,190,235,136,35,142,8,94,123,237,181,230,207, 156,49,99,70,73,28,132,162,11,71,30,121,100,112,248,225, 135,227,48,1,0,0,0,7,136,253,0,0,0,80,8,0, 72,235,215,175,15,86,172,88,81,208,247,61,255,252,243,169, 207,122,253,245,215,83,78,186,20,14,226,252,243,207,79,125, 199,141,55,222,136,195,4,0,0,0,66,168,0,0,0,0, 0,100,2,128,179,207,62,59,53,106,30,59,118,108,42,79, 127,210,73,39,5,215,95,127,125,202,89,219,215,188,241,198, 27,193,164,73,147,242,254,190,233,211,167,7,23,94,120,97, 112,194,9,39,148,204,65,220,119,223,125,169,115,189,245,214, 91,113,152,0,0,0,0,0,0,0,0,0,0,144,9,0, 206,60,243,204,200,215,204,158,61,59,229,248,237,235,46,186, 232,162,170,117,14,71,29,117,84,240,230,155,111,2,0,0, 0,0,128,3,4,128,184,126,20,1,22,10,0,210,53,215, 92,211,252,186,155,110,186,169,249,241,227,143,63,190,185,112, 80,206,87,249,247,249,243,231,167,94,175,145,184,138,253,220, 215,11,30,244,218,203,46,187,108,187,239,176,207,157,118,218, 105,169,255,207,153,51,39,184,228,146,75,130,123,238,185,39, 120,234,169,167,130,7,30,120,32,184,249,230,155,131,83,79, 61,53,242,28,23,47,94,220,92,100,40,41,221,224,22,54, 94,126,249,229,145,117,9,138,124,40,210,241,208,67,15,5, 143,63,254,120,112,199,29,119,4,23,95,124,113,48,113,226, 196,216,235,225,159,171,162,34,231,157,119,94,240,163,31,253, 40,88,187,118,109,234,179,230,206,157,139,195,6,0,0,0, 0,0,1,0,201,6,128,115,207,61,183,249,117,114,194,246, 113,57,79,251,184,96,64,163,110,191,118,64,32,96,95,255, 220,115,207,165,30,211,40,221,255,14,251,220,253,247,223,159, 154,129,160,154,131,184,122,4,189,70,206,219,190,87,179,21, 50,213,48,172,91,183,110,187,233,136,15,63,252,112,236,235, 95,124,241,197,20,84,68,93,15,123,174,119,221,117,87,48, 107,214,172,224,213,87,95,221,238,253,243,230,205,195,97,3, 0,0,0,0,64,10,0,0,72,110,10,64,210,72,220,190, 78,163,229,40,0,120,244,209,71,91,56,192,151,94,122,41, 85,63,224,190,62,27,0,112,165,212,131,70,229,26,253,191, 252,242,203,45,158,91,189,122,117,243,123,85,175,240,224,131, 15,6,207,60,243,76,139,247,234,49,171,123,239,189,183,249, 245,199,28,115,76,139,207,19,28,232,123,100,131,59,83,65, 16,18,85,175,96,207,245,133,23,94,72,201,190,94,118,9, 28,244,190,116,17,4,4,0,0,0,0,0,0,0,0,84, 61,0,104,164,173,105,128,246,117,231,156,115,78,36,0,88, 221,125,247,221,193,228,201,147,211,142,156,179,1,128,219,110, 187,45,24,63,126,124,243,243,227,198,141,107,17,97,144,3, 247,63,67,105,3,251,124,186,26,128,27,110,184,161,249,117, 74,83,184,107,29,200,113,187,169,4,133,244,51,157,171,34, 0,74,7,48,245,16,0,0,0,112,96,0,0,0,80,19, 0,112,236,177,199,166,194,220,238,72,121,218,180,105,177,0, 160,252,121,186,239,203,22,0,20,154,143,122,191,166,14,186, 35,247,9,19,38,228,12,0,83,166,76,73,217,97,207,195, 181,199,74,57,125,183,240,81,233,141,184,115,213,103,248,207, 35,0,0,0,192,129,1,0,0,64,98,0,64,35,247,149, 43,87,6,151,94,122,105,106,14,189,114,247,126,30,94,115, 236,221,247,187,0,160,208,185,66,235,197,0,128,184,66,63, 119,154,159,228,135,231,179,1,128,179,206,58,43,171,40,193, 15,127,248,195,216,243,113,207,85,215,16,199,12,0,0,0, 164,0,16,0,144,216,34,192,116,210,104,56,170,138,222,5, 0,85,235,103,250,190,108,1,32,106,84,110,165,212,128,125, 157,28,126,174,0,32,192,177,175,81,205,128,236,138,146,106, 24,236,235,52,43,32,238,92,53,77,18,199,12,0,0,0, 0,0,2,0,106,6,0,20,106,127,228,145,71,130,43,175, 188,50,182,160,205,5,0,189,174,88,0,160,124,127,220,103, 184,117,0,249,0,192,237,183,223,158,245,234,135,113,182,185, 231,90,170,21,13,17,0,0,0,144,2,0,0,72,1,148, 5,0,84,233,175,252,184,166,199,165,115,192,113,0,144,205, 2,65,217,0,64,212,115,197,4,0,205,213,247,103,43,100, 146,86,47,204,231,92,17,0,0,0,0,0,0,0,0,144, 232,105,128,181,4,0,238,12,0,45,222,147,207,117,3,0, 0,0,0,128,34,64,0,0,0,0,0,18,6,0,23,92, 112,65,228,154,6,0,0,0,0,0,0,0,0,0,0,0, 0,212,0,0,196,77,37,92,180,104,81,243,107,180,80,81, 220,154,5,0,0,0,0,0,224,0,1,32,0,160,238,151, 2,78,10,0,200,153,187,249,253,168,157,11,181,95,129,86, 253,179,175,209,162,63,113,59,28,106,157,129,168,57,254,0, 0,0,0,0,0,0,56,117,0,0,0,168,34,0,144,115, 119,151,242,213,42,127,154,246,167,81,191,230,255,235,121,189, 238,228,147,79,110,1,10,175,188,242,74,106,115,31,93,219, 229,203,151,167,222,163,245,6,180,96,144,150,250,5,0,0, 0,0,0,0,32,5,0,0,144,2,168,98,0,144,180,71, 64,220,116,62,119,58,163,22,53,114,87,251,139,19,0,0, 0,0,0,56,64,236,7,0,106,14,0,244,89,214,209,45, 91,182,44,231,247,107,253,123,251,254,21,43,86,100,124,189, 182,245,141,91,199,63,221,115,174,84,184,23,183,68,175,221, 187,64,11,249,216,229,126,221,37,123,143,62,250,232,22,175, 213,130,67,154,22,168,8,128,239,248,181,10,162,246,1,208, 78,136,249,158,43,2,0,0,0,28,24,0,0,0,84,37, 0,212,178,198,142,29,155,218,228,71,210,146,193,153,214,54, 80,253,192,140,25,51,130,185,115,231,6,83,167,78,101,129, 31,0,0,0,0,0,0,0,0,0,0,64,8,0,0,0, 72,1,80,4,8,0,212,80,17,32,66,0,0,0,0,0, 0,0,0,0,0,128,16,0,0,0,0,0,216,15,0,144, 2,64,8,0,0,0,112,128,216,15,0,0,0,8,1,0, 0,0,69,128,0,0,0,0,0,32,4,0,0,0,56,48, 0,0,0,0,0,16,2,0,0,0,82,0,8,0,160,8, 16,33,0,0,0,0,0,16,0,0,0,32,4,0,0,0, 0,0,41,0,82,0,164,0,16,2,0,0,0,0,0,0, 0,0,0,0,132,0,128,92,0,192,149,123,98,165,126,60, 170,3,173,228,249,148,251,113,223,254,124,62,199,62,87,79, 215,173,90,218,79,212,253,75,218,245,4,0,16,0,80,217, 223,35,17,0,102,1,16,1,32,2,64,4,0,33,34,0, 0,0,41,0,4,0,80,4,88,105,253,248,199,63,14,158, 121,230,153,224,145,71,30,137,124,254,184,227,142,11,14,59, 236,176,68,217,116,212,81,71,5,19,38,76,224,254,2,0, 0,0,0,0,0,0,0,165,5,128,211,79,63,61,184,243, 206,59,83,154,53,107,86,162,156,195,115,207,61,23,108,216, 176,33,120,243,205,55,183,123,110,205,154,53,169,231,158,120, 226,137,196,216,179,112,225,194,148,45,235,214,173,75,221,23, 0,0,0,0,0,72,1,0,0,164,0,74,166,159,252,228, 39,41,71,41,221,116,211,77,53,1,0,71,28,113,68,240, 218,107,175,53,219,53,99,198,140,170,56,223,113,227,198,165, 20,247,252,197,23,95,220,124,206,2,50,0,0,0,0,0, 0,0,0,0,0,40,137,78,58,233,164,102,135,35,201,105, 30,121,228,145,53,17,1,120,254,249,231,83,207,189,254,250, 235,85,97,147,82,17,47,191,252,114,176,126,253,250,96,242, 228,201,145,175,57,247,220,115,155,239,197,101,151,93,6,0, 0,0,20,1,2,0,0,0,0,80,154,206,245,182,219,110, 107,1,0,210,57,231,156,83,19,0,48,125,250,244,224,194, 11,47,12,78,56,225,132,170,56,215,121,243,230,53,95,227, 41,83,166,68,190,70,145,11,133,254,207,62,251,236,180,145, 2,0,0,0,0,0,0,0,0,0,0,200,91,227,199,143, 15,222,120,227,141,230,81,178,117,78,15,61,244,80,77,0, 64,181,233,138,43,174,200,8,0,8,0,32,5,128,3,3, 0,184,127,37,7,128,243,207,63,191,217,33,201,57,169,146, 222,254,127,230,204,153,177,239,59,254,248,227,131,235,174,187, 46,37,133,214,143,62,250,232,96,197,138,21,193,173,183,222, 26,60,246,216,99,193,195,15,63,28,220,112,195,13,169,72, 130,70,180,165,250,140,76,0,112,209,69,23,165,62,63,93, 40,93,97,121,141,184,175,186,234,170,224,222,123,239,13,158, 122,234,169,20,0,221,117,215,93,193,234,213,171,131,169,83, 167,110,247,30,123,174,55,222,120,99,106,22,194,227,143,63, 158,58,223,155,111,190,57,56,243,204,51,183,155,117,112,204, 49,199,164,206,225,213,87,95,109,145,223,183,246,75,138,14, 248,215,101,238,220,185,25,207,251,234,171,175,78,157,131,206, 91,231,175,251,120,202,41,167,100,117,239,52,219,64,58,235, 172,179,82,215,90,247,95,5,147,63,252,225,15,131,203,47, 191,60,117,222,0,0,0,0,0,0,0,0,64,13,2,128, 28,151,117,72,115,230,204,9,86,173,90,213,252,255,107,175, 189,54,246,125,10,79,219,215,157,113,198,25,193,211,79,63, 189,93,26,193,141,38,68,229,187,139,241,25,153,0,32,83, 116,64,163,240,7,31,124,48,246,123,165,219,111,191,189,197, 123,228,56,245,121,233,222,35,24,24,59,118,108,243,123,210, 217,102,165,226,63,255,186,8,96,242,61,111,57,241,168,169, 132,238,231,11,86,158,124,242,201,216,207,80,61,200,178,101, 203,0,0,0,0,0,32,5,0,0,212,82,10,224,196,19, 79,108,238,232,53,122,212,99,147,38,77,74,21,168,233,49, 21,171,197,141,188,93,39,226,74,14,87,115,242,245,94,255, 113,255,179,138,241,25,133,0,128,128,194,157,37,32,41,29, 162,107,241,210,75,47,53,63,246,253,239,127,191,197,251,4, 4,238,235,31,125,244,209,212,8,252,217,103,159,109,241,89, 110,212,65,81,13,57,108,77,237,179,207,203,241,234,49,171, 83,79,61,53,43,0,208,61,114,35,9,146,138,29,5,73, 254,53,211,117,116,65,36,221,117,127,225,133,23,130,7,30, 120,32,5,133,182,13,72,186,22,149,94,147,0,0,0,0, 176,31,0,0,0,138,40,77,247,179,157,188,70,254,246,241, 31,253,232,71,205,143,199,205,67,247,157,136,28,237,201,39, 159,220,226,53,23,92,112,65,11,71,162,116,67,177,63,163, 16,0,184,229,150,91,90,140,116,79,59,237,180,22,161,123, 77,27,92,190,124,249,118,14,84,97,121,57,124,157,191,59, 179,64,239,189,230,154,107,154,63,83,163,126,255,59,21,162, 207,84,3,144,9,0,220,243,126,241,197,23,183,187,102,178, 195,5,27,165,49,210,93,119,125,134,159,50,80,58,226,149, 87,94,105,126,141,210,29,0,0,0,64,17,32,0,0,0, 212,0,0,200,113,89,39,161,81,169,155,235,213,136,215,118, 252,247,223,127,127,86,0,48,127,254,252,200,215,93,122,233, 165,45,70,169,197,254,140,124,1,64,179,3,92,176,208,84, 200,98,92,87,193,130,117,156,186,174,135,31,126,120,81,1, 64,80,226,158,119,220,162,77,114,232,246,53,42,238,84,205, 66,220,117,95,188,120,113,198,245,8,210,165,131,0,0,0, 0,7,6,0,0,0,9,2,0,21,214,217,206,253,158,123, 238,217,110,26,154,59,250,139,42,130,115,157,136,10,246,226, 190,71,142,199,206,50,144,92,208,40,198,103,228,11,0,238, 119,107,181,192,98,94,91,133,226,227,206,181,80,0,112,159, 83,164,38,221,121,232,154,70,57,121,247,51,210,173,144,232, 78,89,188,251,238,187,1,0,0,128,20,0,69,128,0,64, 45,20,1,170,72,205,118,238,170,0,183,171,211,89,41,103, 109,159,87,53,120,58,39,165,117,4,210,125,151,242,202,110, 161,97,49,63,35,95,0,80,126,222,126,158,214,9,200,231, 26,42,47,174,115,81,1,163,70,203,250,76,205,36,112,107, 1,138,13,0,186,23,246,57,165,71,178,157,114,232,166,78, 220,207,247,11,28,93,9,252,236,235,116,222,0,0,0,0, 0,0,0,0,64,194,1,64,211,192,50,85,164,251,197,97, 254,180,54,215,137,68,1,130,171,59,238,184,163,249,181,202, 79,23,243,51,242,5,0,183,144,207,47,242,203,36,229,220, 53,34,118,67,241,113,42,54,0,104,234,96,182,231,173,188, 125,84,8,223,253,252,43,175,188,50,246,253,58,63,0,0, 0,0,0,72,1,0,0,53,148,2,208,52,182,92,0,64, 242,139,196,114,113,222,110,209,154,162,13,197,252,140,124,1, 192,45,116,92,178,100,73,214,215,78,163,253,168,169,114,170, 232,191,239,190,251,130,31,252,224,7,37,5,0,247,188,253, 235,144,46,205,163,104,64,182,69,134,0,0,0,128,3,196, 126,0,160,6,1,192,45,82,179,33,96,133,251,163,228,230, 178,253,58,1,215,137,200,57,103,218,174,215,190,118,193,130, 5,69,253,140,124,1,64,139,222,228,186,236,177,166,13,186, 243,255,229,240,253,10,124,63,189,82,108,0,112,65,72,123, 6,164,59,223,75,46,185,36,114,38,0,0,0,0,16,2, 7,0,0,128,58,4,0,119,4,171,185,231,233,94,171,28, 176,13,115,171,162,93,243,207,115,45,162,83,234,64,41,132, 168,130,194,98,124,70,190,0,224,46,120,164,169,123,217,92, 59,165,30,236,123,52,87,62,110,141,4,119,142,126,177,1, 192,173,93,200,84,153,239,166,77,220,233,156,0,0,0,0, 0,0,0,0,64,29,2,192,218,181,107,155,59,117,57,130, 76,175,215,52,64,251,122,141,40,163,156,136,32,33,206,153, 185,83,10,5,17,174,211,44,198,103,228,11,0,90,221,206, 126,166,28,182,150,195,205,198,9,101,42,90,244,119,86,76, 7,0,113,211,239,210,57,104,247,243,211,157,247,177,199,30, 219,98,209,33,119,51,36,0,0,0,192,1,2,64,0,64, 157,21,1,106,238,123,174,91,254,106,228,232,174,42,23,55, 151,92,185,111,127,193,156,227,142,59,174,197,18,184,90,39, 63,221,58,0,249,124,70,190,0,160,239,177,207,217,117,249, 253,239,182,83,34,163,182,233,213,234,120,254,28,127,57,123, 173,34,152,14,0,220,197,151,148,134,200,21,0,20,13,113, 103,25,40,133,227,3,145,160,192,5,13,63,186,2,0,0, 0,0,0,0,0,0,212,25,0,168,226,219,118,232,114,68, 217,188,71,206,197,93,94,214,230,223,163,150,147,85,88,92, 161,117,77,57,83,206,217,13,219,107,52,42,103,158,105,73, 218,92,63,163,144,149,0,253,130,62,205,137,87,49,162,34, 29,43,87,174,76,1,137,22,209,177,211,237,220,165,147,37, 77,77,212,107,181,158,190,22,43,242,151,231,141,2,0,55, 245,160,115,82,78,95,197,124,58,23,187,168,79,38,7,173, 162,69,247,59,180,20,177,189,102,154,142,232,66,136,34,43, 254,86,200,0,0,0,0,0,96,63,0,80,71,41,0,141, 86,221,245,237,211,237,50,231,75,185,102,251,62,229,149,125, 39,226,110,35,28,37,57,186,168,130,181,98,124,70,33,0, 160,209,180,107,91,166,77,122,36,237,16,152,233,60,221,77, 122,124,0,208,246,203,254,122,253,86,215,95,127,125,214,14, 90,14,63,211,52,68,93,211,76,215,29,0,0,0,112,128, 216,15,0,212,56,0,104,107,95,119,19,154,124,215,13,176, 75,241,186,78,68,35,100,85,210,107,228,233,58,37,141,216, 181,189,172,191,112,79,148,35,202,247,51,36,59,226,149,99, 205,229,57,171,133,11,23,166,70,209,254,14,127,178,85,59, 234,185,75,20,43,109,162,197,126,252,215,202,217,170,190,66, 81,2,125,158,61,247,168,77,116,244,26,119,23,70,191,24, 209,45,54,76,183,6,191,162,49,154,137,224,127,142,34,17, 154,53,161,148,79,166,98,198,116,159,175,162,79,91,71,160, 104,8,0,0,0,80,4,8,0,0,0,53,178,25,80,33, 138,27,69,42,255,44,199,35,104,136,171,146,47,230,103,20, 83,138,146,104,173,125,201,157,241,16,151,26,209,57,10,172, 166,77,155,182,221,66,73,217,72,209,1,189,95,64,16,181, 220,114,182,82,84,65,97,126,157,119,84,138,164,22,4,0, 0,0,0,0,0,0,0,84,57,0,148,251,51,80,125,8, 0,32,5,0,0,113,253,40,2,4,0,16,0,0,0,0, 0,56,16,174,31,0,0,0,32,0,0,0,0,0,176,31, 0,32,5,0,0,32,0,0,0,192,1,98,63,0,0,0, 0,0,8,0,0,0,8,161,2,0,0,0,0,80,68,205, 155,55,47,53,55,92,202,101,55,189,98,127,6,2,0,0, 0,0,0,0,0,0,0,0,132,0,0,0,0,7,8,0, 113,253,40,2,68,8,0,0,0,0,0,28,8,0,0,0, 32,4,0,0,0,0,0,246,3,0,164,0,16,2,0,0, 0,28,32,246,3,0,0,0,66,0,0,0,64,8,28,0, 0,0,0,0,132,0,0,0,0,0,0,0,0,0,0,0, 33,0,0,0,32,5,64,17,32,0,64,17,32,170,37,105, 235,224,103,158,121,38,120,240,193,7,1,0,0,0,0,0, 0,0,128,90,6,0,237,87,127,221,117,215,101,45,109,55, 139,163,172,93,61,247,220,115,169,213,24,95,125,245,85,0, 0,0,0,0,176,31,0,168,229,20,192,85,87,93,213,188, 4,111,54,58,254,248,227,113,148,0,0,0,0,0,0,0, 0,0,0,80,111,0,48,125,250,116,28,37,0,0,0,0, 0,20,1,162,194,5,0,84,15,0,156,127,254,249,193,132, 9,19,98,69,248,31,0,0,0,0,0,0,0,1,0,53, 8,0,103,158,121,38,78,16,0,0,0,0,0,66,224,0, 0,0,80,15,69,128,249,2,128,182,236,85,81,224,105,167, 157,150,250,255,164,73,147,130,243,206,59,47,248,209,143,126, 20,172,93,187,54,120,232,161,135,130,185,115,231,182,120,207, 201,39,159,28,92,113,197,21,193,157,119,222,25,60,252,240, 195,193,99,143,61,150,218,253,239,242,203,47,15,166,76,153, 18,249,61,170,57,176,5,136,71,29,117,84,112,204,49,199, 4,203,151,47,15,110,191,253,246,224,201,39,159,12,238,187, 239,190,224,178,203,46,11,22,47,94,220,252,158,195,14,59, 44,88,180,104,81,234,113,157,139,94,119,207,61,247,4,87, 94,121,101,234,60,51,217,54,107,214,172,224,146,75,46,9, 126,240,131,31,164,222,187,102,205,154,224,154,107,174,9,78, 57,229,148,237,94,123,241,197,23,55,159,223,216,177,99,35, 63,239,220,115,207,109,126,205,210,165,75,35,95,51,117,234, 212,230,215,156,113,198,25,219,61,175,212,203,170,85,171,130, 219,110,187,45,120,244,209,71,83,186,229,150,91,130,11,46, 184,32,152,60,121,114,172,45,254,245,211,181,153,63,127,126, 202,30,93,187,39,158,120,34,184,233,166,155,178,6,128,25, 51,102,84,188,40,20,0,0,0,0,0,0,0,0,168,32, 0,88,71,113,215,93,119,165,28,166,28,134,95,47,160,45, 126,245,218,19,78,56,33,120,250,233,167,211,214,22,188,249, 230,155,193,233,167,159,190,221,247,156,125,246,217,205,175,145, 35,181,223,27,37,93,151,195,15,63,60,117,78,113,175,209, 121,10,14,226,236,146,67,93,183,110,93,236,251,5,30,114, 164,246,245,79,61,245,84,243,115,11,22,44,216,238,243,116, 62,175,188,242,74,243,107,4,70,81,223,123,225,133,23,54, 191,230,214,91,111,221,238,156,222,120,227,141,216,115,210,231, 159,122,234,169,145,159,235,94,63,193,128,62,219,127,191,64, 32,27,0,16,164,189,240,194,11,205,239,19,92,141,27,55, 14,0,168,103,0,64,197,209,150,198,182,193,111,141,214,55, 54,5,87,54,182,14,70,54,52,214,108,26,165,222,83,88, 181,144,2,176,142,66,14,193,117,10,114,228,47,190,248,98, 176,126,253,250,96,226,196,137,169,215,78,155,54,173,133,195, 209,123,53,170,126,224,129,7,82,175,119,29,217,209,71,31, 29,235,192,172,244,217,63,249,201,79,82,115,213,93,231,42, 61,251,236,179,45,156,189,141,52,232,61,246,113,193,200,17, 71,28,177,157,77,138,78,184,159,37,187,30,121,228,145,212, 119,185,143,255,240,135,63,108,126,207,181,215,94,219,252,184, 34,25,254,103,10,10,252,115,143,138,66,40,114,98,95,227, 70,0,252,34,77,93,47,217,163,145,187,15,42,138,192,164, 3,0,69,13,220,215,191,244,210,75,193,235,175,191,30,92, 127,253,245,25,1,64,231,172,245,1,236,123,117,93,92,16, 2,0,0,0,84,100,253,210,192,192,156,134,86,0,0,0, 80,181,0,224,58,91,165,3,52,226,141,122,253,205,55,223, 156,10,25,43,26,224,135,148,5,12,81,206,47,10,0,228, 188,221,207,80,8,90,139,215,248,144,32,187,92,39,47,71, 236,194,134,155,50,144,142,61,246,216,22,207,175,94,189,58, 21,46,183,207,235,218,184,207,43,140,174,199,151,44,89,146, 118,116,47,231,234,159,155,239,168,117,205,94,123,237,181,230, 231,93,112,114,157,188,66,254,110,154,65,176,164,20,138,125, 254,229,151,95,222,46,36,31,5,80,119,223,125,119,108,218, 32,10,0,244,61,74,133,216,247,11,62,84,20,74,13,0, 0,128,202,160,183,13,8,244,1,0,10,86,147,215,126,21, 117,1,0,190,1,0,229,200,103,207,158,29,43,235,152,124, 0,144,99,44,100,125,0,229,182,221,48,126,58,7,54,103, 206,156,237,222,239,71,24,228,20,163,190,231,134,27,110,104, 126,205,138,21,43,182,3,20,251,156,254,142,122,191,59,218, 87,125,128,30,59,242,200,35,155,157,180,254,213,255,221,90, 4,55,58,18,119,126,170,149,112,71,233,246,241,59,238,184, 35,50,234,224,74,223,161,209,184,123,15,211,93,63,125,102, 46,69,128,26,229,187,145,3,69,67,84,135,65,17,96,5, 1,96,204,152,49,95,54,54,54,2,0,117,164,79,140,179, 58,180,70,210,2,149,2,128,238,94,251,253,24,0,200,105, 29,0,229,191,163,0,64,159,81,200,57,40,39,111,63,75, 78,58,206,129,201,209,197,125,134,91,131,224,143,238,173,206, 58,235,172,88,71,233,166,14,226,10,18,85,235,224,142,130, 237,227,202,133,71,125,183,235,216,149,123,183,33,116,1,147, 27,62,87,180,193,190,78,105,136,168,107,124,226,137,39,198, 218,190,108,217,178,230,215,41,229,17,119,253,20,101,200,228, 188,93,0,80,4,69,105,26,55,189,162,72,73,61,207,2, 144,223,149,255,173,40,0,140,30,61,250,139,214,173,91,87, 228,2,188,103,70,163,254,40,202,119,86,95,152,199,158,104, 108,19,204,54,225,235,239,132,35,47,22,194,201,60,58,213, 181,210,53,211,181,251,34,226,186,110,174,33,8,168,132,70, 153,107,231,94,79,181,101,138,0,11,7,0,69,7,178,249, 46,141,86,53,90,151,147,212,154,3,178,67,14,207,29,233, 166,3,0,191,56,206,149,91,140,167,138,250,168,215,232,123, 237,107,52,154,183,143,203,209,185,53,2,202,229,71,73,41, 12,55,234,97,83,4,154,49,16,5,67,238,181,85,106,227, 198,27,111,108,254,191,157,61,33,169,30,194,79,45,40,212, 111,207,41,211,148,60,189,214,173,163,136,187,126,154,9,145, 109,106,71,159,227,22,12,170,94,32,238,186,214,19,0,200, 239,202,255,86,26,0,54,55,53,85,166,243,90,107,156,83, 186,145,234,179,230,249,189,51,56,41,0,32,179,116,13,159, 141,184,214,138,4,244,225,250,228,165,229,13,173,91,92,75, 181,101,0,224,170,22,185,97,85,163,199,73,211,248,162,0, 192,13,123,71,73,121,105,77,151,123,254,249,231,51,66,70, 58,0,112,71,199,233,0,32,110,58,94,28,0,248,41,132, 108,101,43,224,53,58,143,10,225,219,153,15,74,13,40,143, 174,105,132,126,40,94,231,106,107,11,84,144,103,235,22,84, 27,97,95,171,252,123,166,251,168,252,191,125,189,155,159,119, 175,159,31,245,200,166,182,35,46,178,80,175,0,32,191,123, 200,33,135,108,170,40,0,140,26,53,234,15,237,219,183,175, 200,5,88,100,70,168,81,142,127,155,113,76,167,103,89,176, 166,155,168,17,239,50,243,250,119,204,40,236,211,136,209,110, 173,233,175,198,70,217,42,155,199,142,30,157,245,245,94,105, 156,214,54,239,250,188,157,208,145,107,165,1,240,45,47,122, 181,168,66,5,150,181,84,4,40,231,149,238,117,130,3,191, 250,92,35,91,193,128,138,230,52,42,125,252,241,199,179,2, 0,173,61,144,13,0,184,197,123,217,0,128,235,192,237,104, 55,147,20,14,183,206,90,223,103,29,176,108,147,3,86,77, 132,253,188,251,239,191,191,217,217,203,201,219,81,189,138,255, 220,89,2,238,8,221,61,39,93,235,76,247,195,194,149,159, 94,200,246,250,101,2,0,73,16,87,239,0,32,191,107,252, 239,31,35,0,96,78,8,0,199,148,28,0,70,142,28,249, 81,167,78,157,42,114,1,122,25,109,139,112,216,203,114,232, 76,167,13,31,17,188,239,117,198,245,164,143,58,237,24,244, 200,225,154,175,244,70,174,82,146,103,7,84,2,0,250,122, 225,127,181,225,94,9,181,63,73,0,224,22,222,105,46,187, 34,9,254,84,63,215,49,87,2,0,148,23,183,143,107,70, 66,62,215,81,139,27,217,207,208,98,63,110,94,223,45,56, 84,241,160,27,238,87,106,33,234,117,199,29,119,92,139,233, 136,153,82,43,110,33,162,107,127,33,0,160,200,131,155,6, 208,103,159,116,210,73,117,13,0,157,59,119,14,70,140,24, 177,209,1,128,101,70,11,67,0,152,30,2,192,17,165,6, 128,159,118,235,214,173,98,23,225,115,15,0,54,54,52,229, 148,235,174,103,231,111,165,107,208,148,195,53,127,206,75,7, 252,50,193,81,128,74,0,192,26,239,250,173,171,224,245,171, 39,0,112,29,115,212,10,119,118,161,155,74,2,128,95,68, 232,206,118,200,86,231,156,115,78,243,251,175,190,250,234,84, 200,60,106,222,191,174,177,125,157,86,42,84,20,196,254,95, 169,8,55,175,111,107,0,244,175,15,77,254,42,129,110,149, 126,62,215,207,191,175,130,53,165,110,116,45,21,193,112,97, 36,159,235,83,43,0,32,191,59,124,248,240,247,205,223,231, 230,9,0,93,10,6,0,67,32,143,244,232,209,163,34,23, 96,164,55,146,146,110,110,200,190,32,113,89,76,10,161,30, 149,75,212,68,53,1,126,97,96,82,23,11,42,55,0,140, 48,215,201,143,90,77,169,96,4,165,94,0,64,206,195,45, 174,211,168,54,234,117,174,131,169,20,0,184,142,88,197,126, 185,94,71,85,199,187,78,216,218,173,197,138,220,215,41,61, 96,71,235,74,125,216,191,149,82,72,119,93,46,189,244,210, 172,162,44,170,227,40,6,0,184,133,135,138,144,184,107,53, 104,229,192,184,107,92,235,0,32,191,59,100,200,144,23,179, 0,128,17,165,2,128,118,166,8,225,226,62,125,250,124,89, 137,11,160,85,234,220,142,244,111,166,99,253,118,14,239,255, 169,55,250,127,119,167,93,131,25,195,134,215,252,38,27,178, 81,182,186,182,191,147,227,40,244,9,111,20,171,123,65,113, 95,122,153,95,91,176,209,115,254,31,38,188,134,34,73,17, 0,119,30,188,70,250,233,150,192,173,36,0,168,42,223,13, 117,107,202,96,220,119,105,214,67,212,18,184,238,57,88,173, 92,185,50,173,99,143,179,219,143,42,200,33,71,133,223,85, 88,232,46,80,228,22,106,22,11,0,236,66,74,46,204,21, 179,13,38,9,0,228,119,7,13,26,116,79,150,0,48,208, 232,123,70,123,22,3,0,90,25,181,17,0,152,34,132,121, 3,6,12,248,188,18,23,96,189,231,192,55,228,216,153,250, 5,127,245,224,252,93,8,240,11,3,115,185,118,179,189,232, 201,250,132,59,178,82,171,157,209,155,17,233,166,81,9,159, 74,153,36,0,112,151,184,213,212,50,57,58,229,186,229,220, 52,146,204,101,22,64,41,1,32,202,49,235,255,202,229,171, 248,77,139,21,233,220,108,177,157,246,37,72,183,80,80,186, 168,135,166,64,250,175,211,92,254,168,8,138,27,153,16,152, 40,58,161,247,235,26,106,193,34,215,41,71,45,22,84,44, 0,144,148,178,112,11,57,163,246,62,168,117,0,144,223,237, 215,175,223,245,33,0,156,225,0,192,137,33,0,76,52,58, 60,2,0,122,26,237,92,20,0,24,54,108,216,72,19,134, 248,172,18,23,224,119,158,3,63,191,33,183,81,168,223,25, 215,219,118,155,133,44,69,187,135,119,253,126,91,161,133,108, 146,144,2,232,18,227,252,111,172,130,168,73,61,213,0,104, 241,28,119,132,26,37,119,30,124,37,1,64,97,124,133,236, 179,153,2,24,5,0,238,178,192,254,148,192,184,116,129,117, 236,113,235,234,235,181,90,116,40,211,249,104,49,162,168,61, 6,138,9,0,186,174,238,189,82,90,160,18,171,2,86,18, 0,228,119,123,246,236,185,218,3,128,5,33,0,76,115,0, 96,120,201,0,192,156,192,206,102,45,128,109,149,88,13,208, 95,248,103,72,142,163,41,0,32,127,0,104,85,37,75,217, 86,59,0,40,231,191,49,98,166,202,134,28,11,47,235,1, 0,244,89,233,70,161,153,28,174,166,191,101,122,173,194,210, 238,84,63,119,122,155,70,149,154,78,103,29,175,191,170,160, 27,154,247,151,239,117,101,151,195,213,52,187,56,0,80,8, 61,221,198,61,214,201,41,108,239,230,241,93,201,233,9,82, 162,22,197,113,151,5,150,180,64,80,220,249,186,83,35,181, 218,94,166,169,148,58,95,77,63,244,167,83,106,173,1,57, 246,56,155,179,189,126,254,125,141,155,121,32,200,112,207,35, 155,181,5,106,5,0,228,111,229,119,219,180,105,115,190,249, 255,57,33,0,44,245,0,224,104,7,0,14,52,234,23,1, 0,29,10,2,0,125,128,73,3,252,87,135,14,29,42,190, 20,112,171,2,223,15,0,212,223,118,182,165,2,0,77,245, 83,181,127,212,52,213,15,140,243,239,90,35,246,87,50,255, 90,136,84,61,174,141,124,102,206,156,89,145,125,228,115,149, 42,241,21,193,208,249,106,94,191,70,187,149,44,126,211,119, 107,153,98,157,143,206,171,18,91,241,214,243,58,0,242,183, 198,239,254,222,252,189,34,4,128,211,67,0,152,111,52,219, 104,106,8,0,135,25,13,115,0,160,183,145,153,249,221,176, 147,81,231,124,0,160,193,7,0,51,21,240,165,238,221,187, 39,106,55,181,214,0,0,0,80,36,0,104,10,215,246,87, 62,95,43,252,189,149,102,106,233,134,42,114,254,245,12,0, 8,37,25,0,228,111,77,250,125,189,7,0,167,132,0,112, 130,209,20,163,9,70,135,134,0,112,128,81,95,163,239,20, 19,0,218,26,181,55,36,114,78,223,190,125,183,38,9,0, 90,1,0,219,217,223,37,199,188,54,211,39,115,147,114,254, 77,53,86,220,8,0,32,0,160,252,191,59,249,219,253,247, 223,255,78,243,247,114,163,179,141,78,11,1,224,100,7,0, 198,27,141,49,26,106,180,191,209,62,33,0,236,102,212,45, 4,128,246,5,3,192,193,7,31,60,194,208,200,231,73,2, 128,206,0,192,118,246,183,46,48,130,130,162,165,169,126,163, 106,116,227,36,0,0,1,0,229,255,221,201,223,238,182,219, 110,171,61,0,88,98,116,146,209,44,163,227,140,142,50,26, 109,52,196,104,63,163,189,141,122,57,0,208,169,16,0,104, 109,1,64,197,4,166,32,97,171,54,39,240,47,144,27,106, 44,246,227,153,28,120,186,207,250,14,0,80,208,245,35,2, 144,94,219,148,235,223,113,231,230,69,126,202,249,187,40,231, 227,0,0,170,119,0,40,247,239,78,126,86,254,214,252,125, 65,184,12,240,89,70,223,55,90,108,52,207,232,120,163,99, 141,142,52,58,196,232,96,163,1,33,0,152,165,114,26,76, 198,178,97,199,66,0,160,197,106,128,70,29,77,29,192,27, 229,174,3,40,36,2,48,44,98,21,193,122,7,128,125,114, 24,165,238,19,113,253,234,85,154,1,241,7,35,109,233,171, 93,253,180,177,79,175,58,169,129,0,0,16,17,128,242,231, 255,205,18,192,111,155,191,207,143,217,9,112,102,184,19,224, 56,163,81,70,131,141,250,27,237,101,100,102,112,55,236,26, 2,64,199,208,127,23,14,0,102,69,192,101,253,251,247,223, 148,20,0,96,22,0,69,128,108,7,13,0,32,148,52,0, 144,159,53,139,0,221,26,2,64,186,85,0,51,237,4,88, 20,0,208,76,128,111,153,162,132,190,6,2,182,150,115,61, 0,0,0,0,0,0,0,0,132,234,5,0,228,95,229,103, 59,118,236,120,145,51,3,32,106,13,128,108,23,1,106,151, 47,0,180,152,10,168,138,66,147,6,248,191,229,220,25,208, 95,8,168,13,0,80,54,0,104,83,35,11,1,33,138,0, 17,74,10,0,200,191,154,13,248,254,213,252,125,161,83,0, 120,122,196,12,128,168,41,128,81,107,0,20,12,0,109,45, 0,152,233,128,231,238,187,239,190,155,43,181,20,240,119,0, 128,178,1,192,119,106,100,41,96,4,0,32,148,20,0,144, 127,13,167,255,93,16,2,128,45,0,140,154,1,48,38,156, 1,16,55,5,176,67,232,191,11,6,0,85,18,118,50,27, 19,236,163,240,68,171,86,229,217,222,212,223,12,104,118,142, 219,170,178,25,16,155,1,145,2,32,5,128,80,18,0,64, 126,213,9,255,167,43,0,156,28,22,0,30,18,22,0,14, 8,11,0,163,102,0,228,5,0,145,133,128,42,44,48,105, 128,245,102,126,98,69,182,3,214,22,181,185,188,159,237,128, 217,14,24,0,0,0,16,74,2,0,200,175,154,234,255,159, 133,225,255,21,17,187,0,250,219,0,187,5,128,223,117,102, 0,184,5,128,77,197,0,128,118,97,65,65,23,179,64,193, 204,193,131,7,151,101,81,160,145,222,84,180,47,204,40,118, 239,28,166,178,45,243,70,177,245,172,101,57,68,79,116,141, 191,240,162,39,35,19,186,208,13,0,0,0,32,148,4,0, 144,95,221,107,175,189,174,137,201,255,71,237,1,144,110,19, 32,91,0,88,16,0,108,87,8,168,41,6,38,76,241,103, 19,166,40,203,69,249,165,55,138,127,54,135,40,128,150,101, 125,63,205,186,237,245,162,247,115,220,153,238,57,111,244,255, 203,132,134,255,1,0,0,0,161,36,0,128,252,169,241,171, 159,152,191,87,197,44,0,116,82,184,0,80,186,252,127,84, 1,96,155,98,0,64,115,29,128,10,12,76,149,226,42,51, 87,177,44,197,128,115,34,70,241,43,27,178,15,71,79,27, 62,162,174,33,64,182,247,200,225,122,235,218,250,159,49,167, 161,21,5,113,20,1,34,4,0,148,110,238,255,230,3,14, 56,224,238,112,244,31,53,255,95,249,255,25,17,11,0,249, 249,255,110,94,254,191,96,0,216,174,14,192,228,42,122,155, 165,10,55,183,107,87,158,185,225,111,123,14,92,91,176,102, 11,1,186,137,77,97,58,64,121,240,79,35,182,111,173,53, 201,70,217,42,155,155,114,116,254,254,246,182,111,39,120,244, 143,0,0,132,170,29,0,228,71,229,79,219,182,109,187,202, 9,255,71,109,1,236,206,255,31,145,197,2,64,77,161,255, 110,40,4,2,182,171,3,80,158,193,20,3,222,92,174,29, 2,141,117,193,39,17,142,91,233,128,76,53,1,132,128,15, 205,42,231,255,172,23,246,151,116,205,251,212,121,8,188,222, 83,0,151,92,114,9,142,0,213,173,212,254,203,177,243,223, 65,7,29,244,144,249,123,165,83,253,239,135,255,237,244,191, 241,225,252,255,161,17,243,255,163,242,255,5,3,128,191,30, 64,42,13,176,251,238,187,107,74,224,230,29,118,216,161,60, 29,153,113,82,155,35,32,64,197,106,170,88,215,180,181,239, 132,121,127,28,64,122,251,155,194,121,254,186,102,186,118,95, 68,92,87,93,235,67,107,96,135,59,238,127,97,246,159,125, 246,217,193,196,137,19,113,6,168,238,164,118,175,246,95,202, 223,167,252,103,232,71,47,202,80,253,239,134,255,221,233,127, 81,59,0,118,112,194,255,173,138,1,0,238,206,128,29,195, 121,134,187,154,90,128,107,205,194,5,91,202,214,153,25,135, 244,73,29,132,240,43,173,79,106,196,249,3,0,133,219,175, 247,47,93,186,20,135,128,234,78,182,221,151,120,225,159,45, 3,7,14,92,19,142,254,47,240,182,255,141,170,254,143,10, 255,251,27,0,217,252,127,235,98,2,128,173,3,104,78,3, 236,178,203,46,123,25,122,249,180,92,51,2,108,58,224,109, 42,251,75,38,93,219,62,56,64,236,183,75,66,183,105,19, 44,94,188,56,213,25,18,9,64,245,50,242,87,123,87,187, 87,251,47,213,111,179,83,167,78,26,253,127,106,114,255,23, 69,20,255,217,197,127,220,237,127,109,245,127,166,240,191,155, 255,47,8,0,226,166,3,118,10,195,13,221,205,194,5,167, 153,252,197,166,114,119,108,170,76,255,37,32,80,52,233,90, 82,237,143,226,32,64,29,163,194,161,202,137,170,48,10,161, 90,148,218,183,218,185,218,123,41,157,191,36,191,105,34,0, 183,58,83,255,252,226,63,187,249,143,187,251,95,166,234,127, 119,250,95,171,208,127,55,20,51,13,208,60,27,32,172,58, 220,221,20,4,126,100,246,48,254,170,18,157,147,22,168,209, 42,117,90,170,86,123,7,108,33,69,144,213,254,246,186,86, 186,102,186,118,35,107,36,220,143,16,66,73,136,0,202,95, 26,191,249,107,243,247,69,89,22,255,185,155,255,104,241,31, 187,251,95,84,245,191,27,254,47,10,0,248,179,1,236,162, 64,90,116,96,55,179,119,241,56,179,81,208,150,166,166,38, 26,0,246,99,63,246,99,63,246,99,127,220,2,117,198,79, 202,95,154,233,244,151,101,24,253,71,21,255,29,108,180,95, 67,203,205,127,252,197,127,108,248,191,177,24,0,144,110,81, 160,29,195,226,131,61,76,42,224,7,102,49,131,45,52,0, 236,199,126,236,199,126,236,199,254,216,69,127,182,152,101,127, 31,13,157,191,159,251,247,71,255,118,235,95,187,244,239,192, 134,237,215,254,223,49,98,241,159,86,197,114,254,25,139,1, 85,132,96,10,26,52,45,240,183,166,48,240,171,6,66,79, 252,0,176,31,251,177,31,251,81,11,201,63,154,209,255,239, 76,125,193,197,17,149,255,217,140,254,253,165,127,211,21,255, 21,21,0,108,26,192,22,3,186,81,0,21,33,236,97,10, 26,38,40,180,81,174,21,2,17,29,0,246,99,63,246,99, 127,18,36,191,232,133,254,221,121,255,182,242,63,221,232,223, 223,249,207,223,250,215,22,255,21,53,252,159,174,24,208,141, 2,104,39,162,222,102,183,192,43,6,13,26,180,169,177,177, 145,31,0,66,8,161,186,239,255,229,15,229,23,205,122,255, 247,70,20,254,185,243,254,221,202,255,184,209,127,239,134,237, 119,254,43,122,241,95,166,52,192,118,83,2,195,41,9,223, 53,213,141,111,155,109,13,191,160,1,32,132,16,170,247,254, 95,254,208,44,156,247,174,227,252,47,8,71,255,42,252,179, 171,254,185,243,254,237,178,191,81,185,255,184,169,127,77,165, 4,128,168,98,192,237,106,1,148,155,216,121,231,157,7,153, 122,128,223,239,186,235,174,95,209,0,176,31,251,177,31,251, 177,191,94,237,151,31,52,254,240,99,179,220,239,197,17,133, 127,118,218,223,18,163,147,27,190,217,244,199,157,247,239,87, 254,247,136,25,253,183,41,165,243,207,20,5,104,158,17,96, 212,103,159,125,246,153,96,140,222,212,185,115,103,126,0,216, 143,253,216,143,253,216,95,119,246,203,255,201,15,26,8,184, 52,116,254,54,244,31,87,248,55,179,161,229,170,127,238,188, 255,62,17,149,255,101,27,253,187,197,128,25,163,0,70,123, 155,53,142,231,153,116,192,150,114,109,24,132,232,0,176,31, 251,177,31,251,171,65,242,123,242,127,125,250,244,185,198,11, 253,47,119,66,255,238,146,191,182,240,207,174,249,63,210,232, 160,134,111,54,253,201,118,244,95,50,0,240,163,0,81,51, 2,68,39,90,157,72,171,20,245,59,248,224,131,47,48,133, 129,218,235,152,70,65,7,128,253,216,143,253,216,95,243,146, 191,147,223,51,115,254,111,143,200,251,187,115,254,163,66,255, 182,240,111,72,195,215,107,254,247,107,248,102,213,191,93,211, 84,254,183,42,181,243,247,163,0,254,186,0,205,171,3,218, 130,64,21,46,152,11,113,237,208,161,67,55,87,98,165,64, 126,0,8,33,4,0,149,75,242,115,242,119,38,2,254,227, 208,249,187,83,254,236,114,191,110,213,191,157,243,111,67,255, 135,198,20,254,185,171,254,197,205,251,47,57,0,68,69,1, 220,173,130,237,30,1,61,195,144,133,10,23,6,24,8,184, 221,68,3,202,14,1,0,0,66,8,1,0,229,114,254,242, 115,102,202,223,131,158,243,119,167,252,217,188,255,66,163,185, 78,213,255,4,39,244,111,55,252,177,133,127,61,27,90,174, 249,239,174,250,87,182,209,127,186,40,128,63,45,208,22,4, 238,25,134,48,14,48,84,116,135,200,168,156,233,0,66,128, 216,143,253,216,143,253,216,95,142,176,191,252,91,22,206,223, 78,249,211,130,63,39,24,77,53,154,24,86,253,71,133,254, 109,225,95,166,105,127,101,3,128,108,10,2,183,75,5,168, 154,209,92,160,235,148,27,41,87,97,32,63,0,236,199,126, 236,199,126,236,47,117,193,159,252,90,68,216,255,252,136,162, 191,197,94,222,95,11,254,28,25,86,253,15,13,171,254,163, 66,255,21,43,252,203,101,90,160,155,10,104,49,43,192,168, 191,209,32,179,15,242,69,170,142,172,244,20,65,126,0,216, 143,253,216,143,253,168,208,169,126,242,103,166,224,239,142,28, 156,191,93,237,111,178,151,247,31,20,250,73,191,234,223,13, 253,151,109,218,95,174,81,128,184,181,1,84,189,216,59,204, 103,104,65,131,193,251,237,183,223,226,112,126,36,155,7,209, 1,96,63,246,99,63,246,39,78,225,34,63,155,156,169,126, 233,156,191,42,254,231,135,206,223,22,253,217,213,254,70,132, 121,127,187,224,79,239,136,170,127,55,244,95,241,209,127,186, 130,64,119,86,128,93,38,56,181,64,80,152,215,208,154,198, 7,239,185,231,158,51,180,66,146,150,73,44,213,222,1,252, 0,16,66,8,0,42,166,228,175,228,183,228,191,194,69,126, 236,84,63,55,231,31,229,252,237,98,63,126,209,159,93,235, 191,95,195,55,11,254,216,229,126,59,71,132,254,43,62,250, 143,43,8,244,103,5,68,213,3,124,47,44,114,24,210,165, 75,151,177,38,124,242,142,54,74,40,197,46,130,0,0,66, 8,1,0,197,146,252,148,252,149,89,219,255,255,56,203,251, 186,243,252,109,193,95,58,231,175,197,126,142,104,248,122,169, 95,91,244,247,189,152,188,127,186,170,255,138,3,64,186,89, 1,81,245,0,189,140,246,178,69,129,161,241,135,152,169,19, 55,104,171,68,237,151,12,0,96,63,246,99,63,246,99,127, 181,217,47,255,36,63,21,238,234,151,206,249,159,238,228,252, 227,156,191,173,248,183,69,127,123,133,254,49,219,188,127,85, 56,127,63,21,224,214,3,236,224,213,3,236,226,20,5,186, 16,160,202,199,67,76,74,96,158,185,184,191,51,197,20,91, 138,181,94,0,63,0,236,199,126,236,199,126,236,47,68,242, 71,242,75,242,79,187,237,182,219,101,17,33,127,229,251,237, 230,62,118,170,159,91,240,23,229,252,135,122,206,223,22,253, 237,226,229,253,119,240,242,254,173,170,205,249,103,91,15,96, 33,160,167,51,51,64,198,15,180,16,96,194,43,227,205,84, 193,251,68,89,221,187,119,167,64,144,14,0,251,177,31,251, 177,191,98,146,31,146,63,26,60,120,240,163,109,218,180,241, 71,253,110,190,95,203,251,106,133,63,45,242,179,200,113,254, 51,98,156,191,93,233,207,86,252,247,116,156,127,85,231,253, 115,173,7,240,139,2,119,245,32,160,191,11,1,186,72,189, 123,247,158,111,106,3,126,101,166,12,110,234,216,177,35,13, 145,14,0,251,177,31,251,177,191,108,234,212,169,83,32,255, 99,252,208,175,195,81,255,42,111,212,239,231,251,181,182,191, 150,247,213,10,127,90,228,71,243,252,167,135,213,254,19,34, 156,127,127,207,249,239,154,101,209,95,213,2,64,92,61,128, 45,10,244,33,192,78,15,220,219,171,9,24,21,86,71,78, 56,240,192,3,87,155,74,203,79,247,221,119,223,188,118,21, 228,7,128,16,66,0,80,182,146,159,145,191,9,253,206,173, 13,45,167,247,217,92,191,157,226,119,150,147,239,87,177,159, 214,246,215,242,190,218,217,79,139,252,104,158,255,248,208,159, 141,242,114,254,123,55,180,156,238,231,58,127,91,244,215,148, 36,231,159,77,81,160,93,41,208,133,128,239,68,64,128,166, 70,104,126,228,81,237,219,183,159,106,138,4,239,50,55,100, 115,223,190,125,183,230,50,91,0,0,64,8,33,0,32,155, 234,126,249,23,249,25,179,162,223,26,179,180,239,69,17,225, 126,155,235,119,71,253,110,200,95,197,126,90,219,95,203,251, 106,133,191,163,66,63,54,50,194,249,127,199,115,254,93,28, 231,223,46,169,206,63,93,81,160,59,51,160,75,68,58,192, 22,6,106,74,132,230,69,106,113,132,67,27,190,94,38,113, 162,73,5,28,63,100,200,144,123,71,143,30,173,109,22,55, 103,147,26,32,4,136,253,216,143,253,216,143,253,113,146,31, 145,63,145,95,49,33,255,135,76,4,32,206,241,251,185,254, 101,206,168,127,94,24,242,183,249,254,137,161,223,58,52,244, 99,7,135,126,205,45,248,235,233,57,127,191,226,191,234,139, 254,74,5,1,154,15,169,69,17,180,50,146,150,71,28,211, 240,245,70,9,202,163,28,107,34,2,179,204,60,204,155,12, 169,125,98,10,51,62,55,249,153,160,85,171,86,252,0,176, 31,251,177,31,251,177,63,163,253,242,23,242,27,242,31,242, 35,102,90,223,221,225,136,127,165,19,234,247,29,191,27,238, 63,197,27,245,219,144,191,205,247,143,13,253,214,240,208,143, 237,31,250,181,186,113,254,113,69,129,126,58,192,214,4,216, 217,1,154,7,169,197,16,180,34,146,150,69,28,228,20,7, 30,30,134,84,142,49,154,162,169,21,253,250,245,187,192,44, 200,240,142,185,137,91,77,190,102,115,183,110,221,130,82,173, 42,72,7,128,253,216,143,253,216,159,76,201,47,200,63,200, 79,200,95,12,31,62,252,103,102,53,191,107,188,226,62,55, 199,127,174,231,248,109,184,127,177,55,234,159,25,250,163,99, 66,255,116,184,83,236,55,40,244,99,253,66,191,214,203,169, 246,247,115,254,174,243,79,108,232,63,95,8,112,215,9,208, 74,72,90,14,113,159,176,82,210,173,11,80,72,101,92,56, 165,98,114,72,93,199,119,237,218,117,129,201,219,220,96,42, 54,255,159,110,174,9,233,108,50,211,55,130,98,173,39,64, 7,128,253,216,143,253,216,159,44,169,255,151,31,144,63,144, 95,48,131,197,127,221,127,255,253,239,52,97,255,139,66,135, 239,142,246,109,85,191,205,241,159,25,227,248,85,225,127,98, 152,235,183,133,126,71,135,126,233,80,47,223,223,63,244,99, 125,66,191,230,206,243,175,121,231,159,15,4,104,5,36,45, 131,184,135,55,67,224,0,39,37,224,71,3,142,11,167,91, 40,12,51,199,44,47,124,138,9,233,92,99,8,239,31,77, 94,103,171,217,166,241,115,21,119,168,33,116,232,208,129,8, 1,66,8,213,224,8,95,253,187,250,121,245,247,234,247,195, 254,255,157,1,3,6,220,234,56,125,215,225,47,119,156,190, 29,237,159,17,230,248,79,141,112,252,54,220,63,61,244,59, 254,168,223,134,252,15,240,42,253,247,8,253,218,206,245,230, 252,163,32,192,173,9,112,215,9,208,202,71,90,254,80,107, 32,119,119,102,8,216,148,192,128,134,175,231,79,218,104,128, 91,27,48,41,12,195,204,176,32,16,222,176,5,187,239,190, 251,57,102,58,225,205,6,8,214,153,5,29,126,111,26,197, 54,83,76,248,153,105,20,159,155,93,156,190,236,209,163,71, 42,52,164,121,159,166,190,32,69,141,173,91,183,6,20,16, 66,168,10,28,187,250,99,245,203,234,159,213,79,171,191,86, 191,173,254,91,253,184,250,115,245,235,234,223,141,195,95,175, 81,190,201,241,175,118,156,189,235,240,151,123,35,253,179,188, 209,254,50,39,199,239,59,254,25,161,159,153,228,229,250,237, 168,127,96,232,167,108,200,223,86,250,119,15,253,90,215,208, 207,185,243,252,163,114,254,53,229,252,227,32,160,181,7,1, 118,217,96,183,56,208,166,4,246,12,67,41,54,26,112,144, 209,176,134,175,231,86,30,22,134,95,162,64,224,196,48,87, 163,245,152,181,64,195,98,211,144,150,237,177,199,30,231,155, 92,208,85,166,234,243,7,166,241,188,96,224,224,125,19,30, 218,104,26,208,31,77,67,218,100,244,197,152,49,99,190,84, 8,13,33,132,80,101,164,126,88,253,177,250,101,245,207,234, 167,213,95,155,126,251,69,83,12,126,143,169,3,187,190,103, 207,158,171,205,42,125,214,201,251,142,222,29,225,187,14,255, 12,207,233,219,209,254,194,208,95,204,11,253,71,148,227,31, 23,250,157,81,161,31,58,200,25,245,239,19,250,43,27,242, 119,139,253,220,229,125,253,121,254,53,237,252,211,65,128,93, 49,208,157,33,224,167,4,162,162,1,7,134,211,43,108,90, 192,7,1,155,26,56,62,44,214,176,81,129,102,24,8,73, 111,89,24,238,249,126,216,32,206,8,27,200,153,97,99,57, 59,212,57,161,206,141,209,121,8,33,132,74,174,184,62,248, 28,199,209,187,206,222,119,248,167,58,35,125,215,233,219,209, 254,236,208,111,216,80,191,239,248,109,184,255,224,208,15,69, 141,250,253,144,191,91,233,239,175,240,87,23,206,63,215,186, 0,55,37,96,167,10,218,104,192,222,225,180,138,253,194,176, 75,28,8,28,19,78,205,152,234,68,5,116,115,231,122,48, 160,112,207,146,176,65,44,245,160,224,180,80,167,59,128,112, 134,7,10,8,33,132,202,47,183,63,182,125,180,237,179,93, 103,191,52,236,223,151,132,253,189,235,244,231,134,126,193,142, 246,167,134,126,227,152,52,142,127,96,232,127,190,23,250,35, 59,234,183,83,252,252,144,127,221,228,251,11,133,0,55,37, 224,174,23,224,71,3,250,56,105,129,253,195,41,23,46,8, 28,26,230,104,142,10,171,52,109,84,96,154,7,3,115,60, 32,88,224,64,193,98,7,12,44,28,88,64,112,117,42,66, 8,161,178,201,239,131,151,58,78,222,58,250,197,142,179,95, 224,57,252,57,158,211,159,230,140,246,143,14,253,198,216,208, 143,184,142,127,80,232,111,246,117,42,252,253,81,191,59,191, 191,67,68,190,191,238,157,127,46,41,129,168,104,64,143,176, 178,210,166,5,92,16,176,17,1,91,35,48,38,172,210,20, 197,105,93,230,137,14,12,76,13,195,60,51,195,134,112,66, 152,247,177,80,48,47,108,48,39,135,141,199,2,130,133,4, 95,139,16,66,8,149,76,81,253,238,2,199,193,207,15,251, 235,147,194,254,219,58,251,19,195,254,125,86,216,223,79,15, 251,127,235,244,39,134,254,97,92,232,47,198,56,57,126,59, 226,119,29,191,13,247,239,225,228,250,227,70,253,117,31,242, 207,7,2,108,74,192,141,6,184,181,1,46,8,244,246,64, 96,191,48,55,163,41,25,90,144,97,132,19,21,176,48,96, 35,3,10,243,76,14,27,194,148,144,4,45,20,28,239,128, 193,9,33,49,158,232,104,78,132,230,34,132,16,42,186,162, 250,91,183,63,158,237,244,213,179,194,254,219,58,251,105,97, 255,126,92,216,223,31,227,140,244,173,211,183,163,253,17,161, 223,24,28,250,145,253,60,199,223,219,115,252,126,174,223,142, 250,163,66,254,56,255,60,82,2,126,52,160,139,3,2,221, 195,188,139,31,17,248,94,88,156,113,64,24,182,113,97,192, 70,6,148,215,57,194,1,130,9,33,9,30,19,82,225,177, 14,24,76,9,137,113,154,3,9,174,102,32,132,16,42,185, 252,190,215,246,201,83,157,190,250,184,176,255,158,20,246,231, 19,195,254,221,58,252,35,194,254,223,142,244,93,167,63,40, 244,27,3,66,63,226,143,248,123,134,126,199,58,254,46,49, 163,126,66,254,69,140,6,216,218,0,31,4,186,57,32,224, 166,6,84,148,161,181,151,251,134,244,230,195,192,144,48,175, 51,50,164,190,49,78,132,96,108,216,72,142,12,27,204,248, 176,241,28,29,106,162,3,10,113,154,132,16,66,168,96,165, 235,103,109,95,108,251,230,9,97,127,125,84,216,127,143,11, 251,115,59,194,31,19,246,247,35,195,254,127,72,132,211,223, 55,244,27,123,133,126,196,13,245,119,247,242,252,174,227,143, 203,245,227,252,243,128,0,55,26,16,151,22,136,138,8,236, 234,20,11,126,59,12,215,244,9,43,53,93,24,216,63,12, 239,184,64,48,204,129,130,81,14,24,88,56,56,44,108,72, 135,135,4,233,106,44,66,8,161,146,203,239,123,109,159,124, 152,227,228,173,163,31,229,56,251,97,158,195,63,48,244,3, 174,211,223,59,244,23,189,67,255,97,139,251,118,141,25,241, 199,133,251,253,81,63,206,191,8,32,208,198,43,18,140,3, 1,21,99,236,226,69,5,122,57,48,176,87,24,214,233,231, 1,193,1,97,177,135,133,130,131,195,6,51,52,108,60,22, 16,134,135,33,35,171,145,8,33,132,202,38,183,255,29,238, 56,248,97,97,127,61,36,236,191,173,179,31,24,246,239,174, 195,239,23,250,129,189,28,167,223,203,27,237,239,18,250,147, 116,142,191,173,227,155,112,252,101,78,11,180,139,72,13,116, 14,171,49,187,121,81,129,158,78,100,192,166,9,190,235,0, 65,223,48,231,163,198,209,63,108,40,251,57,112,112,96,168, 129,142,6,33,132,16,42,187,220,126,216,246,205,214,201,239, 23,246,223,253,195,254,252,123,97,255,110,29,254,119,157,240, 190,29,233,247,244,70,251,221,66,63,210,57,34,212,223,142, 112,127,229,162,1,62,8,52,121,32,96,103,13,184,81,1, 11,3,110,100,192,22,15,90,32,232,29,54,10,27,37,216, 59,148,133,131,126,161,190,231,105,95,132,16,66,101,147,223, 7,219,190,217,58,121,219,119,219,209,253,158,97,255,110,29, 190,45,230,115,71,250,214,233,187,163,125,91,213,223,222,11, 245,167,115,252,56,255,50,131,64,92,84,192,166,7,92,24, 176,145,129,157,156,232,64,247,144,252,122,56,81,2,11,6, 189,194,70,227,2,194,158,14,40,32,132,16,170,140,220,254, 184,183,211,87,247,114,28,253,238,142,179,223,45,236,239,237, 40,127,39,103,164,239,58,253,168,48,127,84,142,31,199,95, 229,32,208,206,137,10,184,48,208,217,1,130,29,61,40,216, 197,3,3,11,7,22,16,122,58,160,128,16,66,168,50,114, 251,227,30,142,147,119,29,253,46,158,179,223,209,113,248,157, 35,156,126,123,199,119,224,248,107,4,6,218,70,164,9,124, 32,240,161,192,130,129,133,3,11,8,174,118,65,8,33,84, 118,249,125,241,78,142,147,239,230,244,225,174,179,143,115,248, 238,72,31,167,95,35,32,16,5,3,126,116,192,5,2,23, 10,44,24,184,112,96,1,33,74,93,17,66,8,149,92,113, 125,112,103,207,201,119,114,250,242,111,57,125,188,235,240,221, 81,126,156,211,79,172,227,255,255,1,51,186,191,3,227,19, 106,98,0,0,0,0,73,69,78,68,174,66,96,130, }; /* style.css */ static const char * const style_css = "/* Base styles */\n" "@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Source+Code+Pro&display=swap');\n" "\n" "body {\n" " background: white;\n" " color: black;\n" " font-family: 'Open Sans', sans-serif;\n" " font-size: 16px;\n" " margin: 0;\n" "}\n" "code, kbd, pre, tt, input[type=email], input[type=number], input[type=password], input[type=tel], input[type=text], textarea {\n" " font-family: 'Source Code Pro', monospace;\n" "}\n" "pre {\n" " white-space: pre-wrap;\n" "}\n" "\n" "/* Header */\n" ".header {\n" " background: black;\n" " color: white;\n" " width: 100%;\n" "}\n" ".header2 {\n" " background: #333;\n" " color: white;\n" " width: 100%;\n" "}\n" "\n" "/* Navigation */\n" "div.nav {\n" " color: #ccc;\n" " vertical-align: middle;\n" "}\n" ".header div.nav {\n" " font-size: 24px;\n" " line-height: 40px;\n" "}\n" ".header2 div.nav {\n" " font-size: 20px;\n" " line-height: 35px;\n" "}\n" ".header a.btn, .header a.btn:visited, .header2 a.btn, .header2 a.btn:visited {\n" " color: #ccc;\n" " text-decoration: none;\n" " white-space: nowrap;\n" "}\n" ".header a.btn:hover, .header2 a.btn:hover {\n" " color: #eee;\n" "}\n" ".header a.btn:active, .header2 a.btn:active {\n" " color: #fff;\n" "}\n" ".header span.active, .header2 span.active {\n" " color: #fff;\n" " white-space: nowrap;\n" "}\n" ".header a.btn, .header span.active, .header span.spacer {\n" " padding: 0 30px 0 0;\n" "}\n" ".header2 a.btn, .header2 span.active {\n" " padding: 0 20px 0 0;\n" "}\n" "div.nav img {\n" " height: 24px;\n" " margin-right: 10px;\n" " vertical-align: middle;\n" "}\n" "span.spacer {\n" " color: #ccc;\n" "}\n" "span.spacer:after {\n" " content: \"⋮\";\n" "}\n" "\n" "/* Interior Content */\n" ".content {\n" " margin: 20px 0 44px;\n" "}\n" "\n" "form {\n" " display: inline-block;\n" "}\n" "\n" "div.btn {\n" " display: inline-block;\n" " line-height: 32px;\n" "}\n" ".content a.btn, .content a.btn:visited, button, input[type=submit], select {\n" " border: none;\n" " border-radius: 5px;\n" " cursor: pointer;\n" " font-family: inherit;\n" " font-size: 14px;\n" " font-weight: normal;\n" " padding: 4px 8px;\n" " text-decoration: none;\n" " white-space: nowrap;\n" " -moz-appearance: none;\n" " -webkit-appearance: none;\n" "}\n" ".content a.btn, .content a.btn:visited, button, input[type=submit] {\n" " background: #333;\n" " color: #ccc;\n" "}\n" ".content a.btn:hover, button:hover, input[type=submit]:hover {\n" " background: #333;\n" " color: #eee;\n" "}\n" ".content a.btn:active, button:active, input[type=submit]:active {\n" " background: #444;\n" " color: #fff;\n" "}\n" "input[type=email], input[type=password], input[type=tel], input[type=text] {\n" " padding: 4px 8px;\n" "}\n" "input[type=number] {\n" " appearance: textfield;\n" " padding: 4px 0 4px 8px;\n" "}\n" "input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button {\n" " -webkit-appearance: none;\n" " margin: 0;\n" "}\n" "select {\n" " background: #ccc url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23000000' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px;\n" " color: black;\n" " padding: 4px 32px 4px 8px;\n" "}\n" "select:hover {\n" " background: #ddd url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23000000' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px;\n" "}\n" "textarea {\n" " width: 100%;\n" "}\n" "label.image input[type=radio] {\n" " position: absolute;\n" " opacity: 0;\n" " width: 0;\n" " height: 0;\n" "}\n" "label.image input[type=radio] + img {\n" " border-radius: 5px;\n" " cursor: pointer;\n" " padding: 2px;\n" "}\n" "label.image input[type=radio]:checked + img {\n" " background: rgba(127,127,127,.5);\n" "}\n" "\n" "div.pager {\n" " padding: 10px 0;\n" " text-align: center;\n" "}\n" "\n" "div.log {\n" " background: #333;\n" " border: solid thin rgba(127,127,127,.5);\n" " color: #ccc;\n" " height: 30em;\n" " margin-top: 20px;\n" " overflow: scroll;\n" " width: 100%;\n" "}\n" "\n" "div.log pre {\n" " padding: 10px;\n" "}\n" "\n" "/* Footer */\n" ".footer {\n" " background: rgba(224,224,224,.9);\n" " bottom: 0;\n" " color: #777;\n" " font-size: 12px;\n" " height: 24px;\n" " left: 0;\n" " position: fixed;\n" " right: 0;\n" " width: 100%;\n" "}\n" ".footer a {\n" " color: #c66;\n" "}\n" "\n" "/* Grid System - Rows and Columns */\n" ".row {\n" " position: relative;\n" " width: 100%;\n" "}\n" ".row::after {\n" " clear: both;\n" " content: \"\";\n" " display: table;\n" " width: 0;\n" "}\n" ".row [class^=\"col-\"] {\n" " float: left;\n" " margin: 20px 0 0 2%;\n" " min-height: 1px;\n" " padding: 5px 0;\n" "}\n" ".row:first-child [class^=\"col-\"] {\n" " margin: 0 0 0 2%;\n" "}\n" "h1:first-child, h2:first-child, h3:first-child, .row p:first-child {\n" " margin-top: 0;\n" "}\n" ".row p:last-child, .rounded p:last-child {\n" " margin-bottom: 0;\n" "}\n" ".col-1 {\n" " width: 6.1666%;\n" "}\n" ".col-2 {\n" " width: 14.3333%;\n" "}\n" ".col-3 {\n" " width: 22.5%;\n" "}\n" ".col-4 {\n" " width: 30.6666%;\n" "}\n" ".col-5 {\n" " width: 38.8333%;\n" "}\n" ".col-6 {\n" " width: 47%;\n" "}\n" ".col-7 {\n" " width: 55.1666%;\n" "}\n" ".col-8 {\n" " width: 63.3333%;\n" "}\n" ".col-9 {\n" " width: 71.5%;\n" "}\n" ".col-10 {\n" " width: 79.6666%;\n" "}\n" ".col-11 {\n" " width: 87.8333%;\n" "}\n" ".col-12 {\n" " width: 96%;\n" "}\n" "\n" "/* Inline Styles */\n" "span.badge {\n" " background: #c00;\n" " border-radius: 8px;\n" " color: #fff;\n" " font-size: 14px;\n" " font-weight: bold;\n" " padding: 1px 6px;\n" "}\n" "\n" "span.bar {\n" " box-shadow: 1px 1px 2px rgba(51,51,51,0.25);\n" " font-size: 20px;\n" " line-height: 20px;\n" "}\n" "\n" "img.idle, img.processing, img.stopped {\n" " border-radius: 10px;\n" " float: left;\n" " height: 64px;\n" " margin-right: 10px;\n" " padding: 5px;\n" " width: 64px;\n" "}\n" "\n" "img.idle {\n" " background-color: rgba(0,192,0,0.5);\n" "}\n" "\n" "img.processing {\n" " background-color: rgba(224,224,0,0.5);\n" "}\n" "\n" "img.stopped {\n" " background-color: rgba(192,0,0,0.5);\n" "}\n" "\n" ".shaded {\n" " background-color: rgba(127,127,127,.25);\n" "}\n" "\n" "h1.title {\n" " border-bottom: solid 2px black;\n" " clear: both;\n" "}\n" "\n" "h2.title {\n" " border-bottom: solid 1px #555;\n" " clear: both;\n" " color: #555;\n" "}\n" "\n" ".title a:link, .title a:visited {\n" " color: inherit;\n" " text-decoration: none;\n" "}\n" "\n" ".title a.btn, .title a.btn:visited {\n" " color: #ccc;\n" " vertical-align: 25%;\n" "}\n" "\n" ".title a.btn:hover, .title a.btn:active {\n" " color: #fff;\n" "}\n" "\n" ".banner {\n" " animation: banner 3s 1s 1 forwards;\n" " background: rgba(160,160,224,0.5);\n" " border-radius: 5px;\n" " margin: 10px 0;\n" " padding: 10px;\n" "}\n" "\n" "@keyframes banner {\n" " 0% { background: rgba(160,160,224,0.5); }\n" " 100% { background: rgba(160,160,160,0.5); }\n" "}\n" "\n" "/* Form Tables */\n" "table.form {\n" " border-collapse: collapse;\n" " margin-top: 10px;\n" " min-width: 50%;\n" "}\n" "table.form td, table.form th {\n" " line-height: 30px;\n" " padding: 5px 2px;\n" " vertical-align: top;\n" "}\n" "table.form th {\n" " text-align: right;\n" "}\n" "\n" "iframe#map {\n" " width: 300px;\n" " height: 200px;\n" "}\n" "\n" "/* List Tables */\n" "table.list {\n" " border-bottom: solid thin black;\n" " border-collapse: collapse;\n" " clear: both;\n" " width: 100%;\n" "}\n" "table.list tr:nth-child(even) {\n" " background: #fcfcfc;\n" "}\n" "table.list tr:nth-child(odd) {\n" " background: #f0f0f0;\n" "}\n" "table.list th {\n" " background: white;\n" " border-bottom: solid thin black;\n" " text-align: left;\n" " vertical-align: bottom;\n" "}\n" "table.list td {\n" " margin: 0;\n" " padding: 5px;\n" " vertical-align: top;\n" "}\n" "\n" "/* Meter Tables for Supply Levels */\n" "table.meter {\n" " border-collapse: collapse;\n" " margin-top: 10px;\n" " min-width: 50%;\n" " max-width: 600px;\n" "}\n" "table.meter thead td {\n" " border-bottom: 1px solid black;\n" " border-left: 1px solid black;\n" " border-right: 1px solid black;\n" " height: 4px;\n" " line-height: 4px;\n" " padding: 0;\n" " width: 20%;\n" "}\n" "table.meter tfoot td {\n" " border-left: 1px solid black;\n" " border-right: 1px solid black;\n" " border-top: 1px solid black;\n" " height: 4px;\n" " line-height: 4px;\n" " padding: 0;\n" " width: 20%;\n" "}\n" "table.meter td {\n" " padding: 5px 0;\n" "}\n" "table.meter th {\n" " border-right: 1px solid black;\n" " padding: 5px;\n" " text-align: right;\n" "}\n" "\n" "/* Small Device Overrides */\n" "@media only screen and (min-device-width: 320px) and (max-device-width: 480px), only screen and (min-device-width: 320px) and (max-device-width: 568px) {\n" " .content {\n" " margin: 20px 0 20px;\n" " }\n" " .footer {\n" " height: auto;\n" " position: relative;\n" " }\n" " .row [class^=\"col-\"] {\n" " width: 96%;\n" " }\n" " iframe#map {\n" " width: 200px;\n" " height: 200px;\n" " }\n" "}\n" "\n" "/* Dark Mode Overrides */\n" "@media (prefers-color-scheme: dark) {\n" " body {\n" " background: black;\n" " color: #ccc;\n" " }\n" " span.bar {\n" " box-shadow: 1px 1px 5px rgba(255,255,255,0.5);\n" " }\n" "\n" " h1.title {\n" " border-bottom: solid 2px #ccc;\n" " }\n" " h2.title {\n" " border-bottom: solid 1px #999;\n" " color: #999;\n" " }\n" "\n" " input[type=email], input[type=text], input[type=password], input[type=number] {\n" " background: #333;\n" " color: #ccc;\n" " }\n" "\n" " table.list {\n" " border-bottom: solid thin #ccc;\n" " }\n" " table.list tr:nth-child(even) {\n" " background: #222;\n" " color: #ccc;\n" " }\n" " table.list tr:nth-child(odd) {\n" " background: #333;\n" " }\n" " table.list th {\n" " background: black;\n" " border-bottom: solid thin #ccc;\n" " }\n" " table.meter thead td {\n" " border-bottom: 1px solid #ccc;\n" " border-left: 1px solid #ccc;\n" " border-right: 1px solid #ccc;\n" " }\n" " table.meter tfoot td {\n" " border-left: 1px solid #ccc;\n" " border-right: 1px solid #ccc;\n" " border-top: 1px solid #ccc;\n" " }\n" " table.meter th {\n" " border-right: 1px solid #ccc;\n" " }\n" "}\n" ; pappl-1.0.3/pappl/resource.c000066400000000000000000000333371403603036100157430ustar00rootroot00000000000000// // System resource implementation for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" #include // // Local functions... // static void add_resource(pappl_system_t *system, _pappl_resource_t *r); static int compare_resources(_pappl_resource_t *a, _pappl_resource_t *b); static _pappl_resource_t *copy_resource(_pappl_resource_t *r); static void free_resource(_pappl_resource_t *r); // // 'papplSystemAddResourceCallback()' - Add a dynamic resource that uses a // callback function. // // This function adds a dynamic resource at the specified path. When a client // GET or POST request is received at the specified path, the "cb" function // will be called with the client pointer and "data" pointer to respond to it. // // Resource callbacks are most often used to implement custom web pages. // // > Note: Any custom web page that is added prior to calling the // > @link papplSystemRun@ function will replace the corresponding standard web // > page at the same path. // void papplSystemAddResourceCallback( pappl_system_t *system, // I - System object const char *path, // I - Resource path const char *format, // I - MIME media type for content such as "text/html" pappl_resource_cb_t cb, // I - Callback function void *data) // I - Callback data { _pappl_resource_t r; // New resource if (!system || !path || path[0] != '/' || !format || !cb) return; memset(&r, 0, sizeof(r)); r.path = (char *)path; r.format = (char *)format; r.cb = cb; r.cbdata = data; add_resource(system, &r); } // // 'papplSystemAddResourceData()' - Add a static data resource. // // This function adds a static resource at the specified path. The provided // data is not copied to the resource and must remain stable for as long as the // resource is added to the system. // // > Note: Any resource that is added prior to calling the @link papplSystemRun@ // > function will replace the corresponding standard resource at the same path. // void papplSystemAddResourceData( pappl_system_t *system, // I - System object const char *path, // I - Resource path const char *format, // I - MIME media type such as "image/png" const void *data, // I - Resource data size_t datalen) // I - Size of resource data { _pappl_resource_t r; // New resource if (!system || !path || path[0] != '/' || !format || !data || datalen == 0) return; memset(&r, 0, sizeof(r)); r.path = (char *)path; r.format = (char *)format; r.last_modified = time(NULL); r.data = data; r.length = datalen; add_resource(system, &r); } // // 'papplSystemAddResourceDirectory()' - Add external files in a directory as // resources. // // This function adds static resources from the specified directory under the // specified path. The directory is scanned and only those files present at the // time of the call are available, and those files must remain stable for as // long as the resources are added to the system.. // // > Note: Any resource that is added prior to calling the @link papplSystemRun@ // > function will replace the corresponding standard resource at the same path. // void papplSystemAddResourceDirectory( pappl_system_t *system, // I - System object const char *basepath, // I - Base resource path const char *directory) // I - Directory containing resource files { cups_dir_t *dir; // Directory pointer cups_dentry_t *dent; // Current directory entry char filename[1024], // External filename path[1024], // Resource path *ext; // Extension on filename const char *format; // MIME media type _pappl_resource_t r; // New resource if (!system || !basepath || !directory) return; // Read all files in the directory... if ((dir = cupsDirOpen(directory)) == NULL) return; while ((dent = cupsDirRead(dir)) != NULL) { // Skip dot files, directories, and files without an extension... if (dent->filename[0] == '.' || !S_ISREG(dent->fileinfo.st_mode) || (ext = strrchr(dent->filename, '.')) == NULL) continue; // See if this is an extension we recognize... if (!strcmp(ext, ".css")) format = "text/css"; else if (!strcmp(ext, ".html")) format = "text/html"; else if (!strcmp(ext, ".icc")) format = "application/vnd.iccprofile"; else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) format = "image/jpeg"; else if (!strcmp(ext, ".pdf")) format = "application/pdf"; else if (!strcmp(ext, ".png")) format = "image/png"; else if (!strcmp(ext, ".strings")) format = "text/strings"; else if (!strcmp(ext, ".txt")) format = "text/plain"; else continue; // Add the file... snprintf(filename, sizeof(filename), "%s/%s", directory, dent->filename); snprintf(path, sizeof(path), "%s/%s", basepath, dent->filename); memset(&r, 0, sizeof(r)); r.path = (char *)path; r.format = (char *)format; r.filename = (char *)filename; r.last_modified = dent->fileinfo.st_mtime; r.length = (size_t)dent->fileinfo.st_size; add_resource(system, &r); } cupsDirClose(dir); } // // 'papplSystemAddResourceFile()' - Add an external file as a resource. // // This function adds a static resource at the specified path. The provided // file is not copied to the resource and must remain stable for as long as the // resource is added to the system. // // > Note: Any resource that is added prior to calling the @link papplSystemRun@ // > function will replace the corresponding standard resource at the same path. // void papplSystemAddResourceFile( pappl_system_t *system, // I - System object const char *path, // I - Resource path const char *format, // I - MIME media type such as "image/png" const char *filename) // I - Filename { _pappl_resource_t r; // New resource struct stat fileinfo; // File information if (!system || !path || path[0] != '/' || !format || !filename || stat(filename, &fileinfo)) return; memset(&r, 0, sizeof(r)); r.path = (char *)path; r.format = (char *)format; r.filename = (char *)filename; r.last_modified = fileinfo.st_mtime; r.length = (size_t)fileinfo.st_size; add_resource(system, &r); } // // 'papplSystemAddResourceString()' - Add a static data resource as a C string. // // This function adds a static resource at the specified path. The provided // data is not copied to the resource and must remain stable for as long as the // resource is added to the system. // // > Note: Any resource that is added prior to calling the @link papplSystemRun@ // > function will replace the corresponding standard resource at the same path. // void papplSystemAddResourceString( pappl_system_t *system, // I - System object const char *path, // I - Resource path const char *format, // I - MIME media type such as "image/png" const char *data) // I - Resource data { _pappl_resource_t r; // New resource if (!system || !path || path[0] != '/' || !format || !data) return; memset(&r, 0, sizeof(r)); r.path = (char *)path; r.format = (char *)format; r.last_modified = time(NULL); r.data = data; r.length = strlen(data); add_resource(system, &r); } // // 'papplSystemAddStringsData()' - Add a static localization file resource. // // This function adds a static localization resource at the specified path. // Localization files use the NeXTStep strings ("text/strings") format defined // in PWG Candidate Standard 5100.13-2013. The provided data is not copied to // the resource and must remain stable for as long as the resource is added to // the system. // // > Note: Any resource that is added prior to calling the @link papplSystemRun@ // > function will replace the corresponding standard resource at the same path. // void papplSystemAddStringsData( pappl_system_t *system, // I - System object const char *path, // I - Resource path const char *language, // I - ISO language tag such as "en-US", "fr-CA", etc. const char *data) // I - Nul-terminated string containing strings file data { _pappl_resource_t r; // New resource if (!system || !path || path[0] != '/' || !language || !data || !*data) return; memset(&r, 0, sizeof(r)); r.path = (char *)path; r.format = (char *)"text/strings"; r.language = (char *)language; r.last_modified = time(NULL); r.data = data; r.length = strlen(data); add_resource(system, &r); } // // 'papplSystemAddStringsFile()' - Add an external localization file resource. // // This function adds a static localization resource at the specified path. // Localization files use the NeXTStep strings ("text/strings") format defined // in PWG Candidate Standard 5100.13-2013. The provided file is not copied to // the resource and must remain stable for as long as the resource is added to // the system. // // > Note: Any resource that is added prior to calling the @link papplSystemRun@ // > function will replace the corresponding standard resource at the same path. // void papplSystemAddStringsFile( pappl_system_t *system, // I - System object const char *path, // I - Resource path const char *language, // I - ISO language tag such as "en-US", "fr-CA", etc. const char *filename) // I - Filename { _pappl_resource_t r; // New resource struct stat fileinfo; // File information if (!system || !path || path[0] != '/' || !language || !filename || stat(filename, &fileinfo)) return; memset(&r, 0, sizeof(r)); r.path = (char *)path; r.format = (char *)"text/strings"; r.filename = (char *)filename; r.language = (char *)language; r.last_modified = fileinfo.st_mtime; r.length = (size_t)fileinfo.st_size; add_resource(system, &r); } // // '_papplSystemFindResource()' - Find a resource at a path. // _pappl_resource_t * // O - Resource object _papplSystemFindResource( pappl_system_t *system, // I - System object const char *path) // I - Resource path { _pappl_resource_t key, // Search key *match; // Matching resource, if any char altpath[1024]; // Alternate path if (!system || !system->resources || !path) return (NULL); key.path = (char *)path; pthread_rwlock_rdlock(&system->rwlock); if ((match = (_pappl_resource_t *)cupsArrayFind(system->resources, &key)) == NULL) { snprintf(altpath, sizeof(altpath), "%s/", path); key.path = altpath; match = (_pappl_resource_t *)cupsArrayFind(system->resources, &key); } pthread_rwlock_unlock(&system->rwlock); return (match); } // // 'papplSystemRemoveResource()' - Remove a resource at the specified path. // // This function removes a resource at the specified path. // void papplSystemRemoveResource( pappl_system_t *system, // I - System object const char *path) // I - Resource path { _pappl_resource_t key, // Search key *match; // Matching resource, if any if (!system || !system->resources || !path) return; key.path = (char *)path; pthread_rwlock_wrlock(&system->rwlock); if ((match = (_pappl_resource_t *)cupsArrayFind(system->resources, &key)) != NULL) { papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Removing resource for '%s'.", path); cupsArrayRemove(system->resources, match); } pthread_rwlock_unlock(&system->rwlock); } // // 'add_resource()' - Add a resource object to a system object. // static void add_resource(pappl_system_t *system, // I - System object _pappl_resource_t *r) // I - Resource { pthread_rwlock_wrlock(&system->rwlock); if (!cupsArrayFind(system->resources, r)) { papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Adding resource for '%s'.", r->path); if (!system->resources) system->resources = cupsArrayNew3((cups_array_func_t)compare_resources, NULL, NULL, 0, (cups_acopy_func_t)copy_resource, (cups_afree_func_t)free_resource); cupsArrayAdd(system->resources, r); } pthread_rwlock_unlock(&system->rwlock); } // // 'compare_resources()' - Compare the path of two resources. // static int // O - Result of comparison compare_resources(_pappl_resource_t *a, // I - First resource _pappl_resource_t *b) // I - Second resource { return (strcmp(a->path, b->path)); } // // 'copy_resource()' - Make a copy of some resource data. // static _pappl_resource_t * // O - New resource copy_resource(_pappl_resource_t *r) // I - Resource to copy { _pappl_resource_t *newr; // New resource if ((newr = (_pappl_resource_t *)calloc(1, sizeof(_pappl_resource_t))) != NULL) { newr->path = strdup(r->path); newr->format = strdup(r->format); newr->last_modified = r->last_modified; newr->data = r->data; newr->length = r->length; newr->cb = r->cb; newr->cbdata = r->cbdata; if (r->filename) newr->filename = strdup(r->filename); if (r->language) newr->language = strdup(r->language); if (!newr->path || !newr->format || (r->filename && !newr->filename) || (r->language && !newr->language)) { free_resource(newr); return (NULL); } } return (newr); } // // 'free_resource()' - Free the memory used for a resource. // static void free_resource(_pappl_resource_t *r) // I - Resource { free(r->path); free(r->format); free(r->filename); free(r->language); free(r); } pappl-1.0.3/pappl/snmp-private.h000066400000000000000000000100521403603036100165330ustar00rootroot00000000000000// // Private SNMP definitions for the Printer Application Framework. // // Copyright © 2020 by Michael R Sweet // Copyright © 2007-2014 by Apple Inc. // Copyright © 2006-2007 by Easy Software Products, all rights reserved. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_SNMP_PRIVATE_H_ # define _PAPPL_SNMP_PRIVATE_H_ // // Include necessary headers. // # include "base-private.h" # include // // Constants... // #define _PAPPL_SNMP_COMMUNITY "public"// SNMP default community name #define _PAPPL_SNMP_PORT 161 // SNMP well-known port #define _PAPPL_SNMP_MAX_COMMUNITY 512 // Maximum size of community name #define _PAPPL_SNMP_MAX_OID 128 // Maximum number of OID numbers #define _PAPPL_SNMP_MAX_PACKET 1472 // Maximum size of SNMP packet #define _PAPPL_SNMP_MAX_STRING 1024 // Maximum size of string #define _PAPPL_SNMP_VERSION_1 0 // SNMPv1 // // Types... // enum _pappl_asn1_e // ASN1 request/object types { _PAPPL_ASN1_END_OF_CONTENTS = 0x00, // End-of-contents _PAPPL_ASN1_BOOLEAN = 0x01, // BOOLEAN _PAPPL_ASN1_INTEGER = 0x02, // INTEGER or ENUMERATION _PAPPL_ASN1_BIT_STRING = 0x03, // BIT STRING _PAPPL_ASN1_OCTET_STRING = 0x04, // OCTET STRING _PAPPL_ASN1_NULL_VALUE = 0x05, // NULL VALUE _PAPPL_ASN1_OID = 0x06, // OBJECT IDENTIFIER _PAPPL_ASN1_SEQUENCE = 0x30, // SEQUENCE _PAPPL_ASN1_HEX_STRING = 0x40, // Binary string aka Hex-STRING _PAPPL_ASN1_COUNTER = 0x41, // 32-bit unsigned aka Counter32 _PAPPL_ASN1_GAUGE = 0x42, // 32-bit unsigned aka Gauge32 _PAPPL_ASN1_TIMETICKS = 0x43, // 32-bit unsigned aka Timeticks32 _PAPPL_ASN1_GET_REQUEST = 0xa0, // GetRequest-PDU _PAPPL_ASN1_GET_NEXT_REQUEST = 0xa1, // GetNextRequest-PDU _PAPPL_ASN1_GET_RESPONSE = 0xa2 // GetResponse-PDU }; typedef enum _pappl_asn1_e _pappl_asn1_t;// ASN1 request/object types typedef struct _pappl_snmp_string_s // String value { unsigned char bytes[_PAPPL_SNMP_MAX_STRING]; // Bytes in string unsigned num_bytes; // Number of bytes } _pappl_snmp_string_t; union _pappl_snmp_value_u // Object value { int boolean; // Boolean value int integer; // Integer value int counter; // Counter value unsigned gauge; // Gauge value unsigned timeticks; // Timeticks value int oid[_PAPPL_SNMP_MAX_OID]; // OID value _pappl_snmp_string_t string; // String value }; typedef struct _pappl_snmp_s // SNMP data packet { const char *error; // Encode/decode error http_addr_t address; // Source address int version; // Version number char community[_PAPPL_SNMP_MAX_COMMUNITY]; // Community name _pappl_asn1_t request_type; // Request type unsigned request_id; // request-id value int error_status; // error-status value int error_index; // error-index value int object_name[_PAPPL_SNMP_MAX_OID]; // object-name value _pappl_asn1_t object_type; // object-value type union _pappl_snmp_value_u object_value; // object-value value } _pappl_snmp_t; typedef void (*_pappl_snmp_cb_t)(_pappl_snmp_t *packet, void *data); // SNMP callback // // Prototypes... // extern void _papplSNMPClose(int fd) _PAPPL_PRIVATE; extern int *_papplSNMPCopyOID(int *dst, const int *src, int dstsize) _PAPPL_PRIVATE; extern int _papplSNMPIsOID(_pappl_snmp_t *packet, const int *oid) _PAPPL_PRIVATE; extern int _papplSNMPIsOIDPrefixed(_pappl_snmp_t *packet, const int *prefix) _PAPPL_PRIVATE; extern char *_papplSNMPOIDToString(const int *src, char *dst, size_t dstsize) _PAPPL_PRIVATE; extern int _papplSNMPOpen(int family) _PAPPL_PRIVATE; extern _pappl_snmp_t *_papplSNMPRead(int fd, _pappl_snmp_t *packet, double timeout) _PAPPL_PRIVATE; extern int _papplSNMPWalk(int fd, http_addr_t *address, int version, const char *community, const int *prefix, double timeout, _pappl_snmp_cb_t cb, void *data) _PAPPL_PRIVATE; extern int _papplSNMPWrite(int fd, http_addr_t *address, int version, const char *community, _pappl_asn1_t request_type, const unsigned request_id, const int *oid) _PAPPL_PRIVATE; #endif // !_PAPPL_SNMP_PRIVATE_H_ pappl-1.0.3/pappl/snmp.c000066400000000000000000000674221403603036100150730ustar00rootroot00000000000000// // SNMP functions for the Printer Application Framework. // // Copyright © 2020 by Michael R Sweet. // Copyright © 2007-2019 by Apple Inc. // Copyright © 2006-2007 by Easy Software Products, all rights reserved. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers. // #include "snmp-private.h" // // Macros... // #define _(x) x #define snmp_set_error(p,m) p->error = m // // Local functions... // static int asn1_decode_snmp(unsigned char *buffer, size_t len, _pappl_snmp_t *packet); static int asn1_encode_snmp(unsigned char *buffer, size_t len, _pappl_snmp_t *packet); static int asn1_get_integer(unsigned char **buffer, unsigned char *bufend, unsigned length); static int asn1_get_oid(unsigned char **buffer, unsigned char *bufend, unsigned length, int *oid, int oidsize); static int asn1_get_packed(unsigned char **buffer, unsigned char *bufend); static char *asn1_get_string(unsigned char **buffer, unsigned char *bufend, unsigned length, char *string, size_t strsize); static unsigned asn1_get_length(unsigned char **buffer, unsigned char *bufend); static int asn1_get_type(unsigned char **buffer, unsigned char *bufend); static void asn1_set_integer(unsigned char **buffer, int integer); static void asn1_set_length(unsigned char **buffer, unsigned length); static void asn1_set_oid(unsigned char **buffer, const int *oid); static void asn1_set_packed(unsigned char **buffer, int integer); static unsigned asn1_size_integer(int integer); static unsigned asn1_size_length(unsigned length); static unsigned asn1_size_oid(const int *oid); static unsigned asn1_size_packed(int integer); // // '_papplSNMPClose()' - Close a SNMP socket. // void _papplSNMPClose(int fd) // I - SNMP socket file descriptor { httpAddrClose(NULL, fd); } // // '_papplSNMPCopyOID()' - Copy an OID. // // The array pointed to by "src" is terminated by the value -1. // int * // O - New OID _papplSNMPCopyOID(int *dst, // I - Destination OID const int *src, // I - Source OID int dstsize) // I - Number of integers in dst { int i; // Looping var for (i = 0, dstsize --; src[i] >= 0 && i < dstsize; i ++) dst[i] = src[i]; dst[i] = -1; return (dst); } // // '_papplSNMPIsOID()' - Test whether a SNMP response contains the specified OID. // // The array pointed to by "oid" is terminated by the value -1. // int // O - 1 if equal, 0 if not equal _papplSNMPIsOID(_pappl_snmp_t *packet, // I - Response packet const int *oid) // I - OID { int i; // Looping var // Range check input... if (!packet || !oid) return (0); // Compare OIDs... for (i = 0; i < _PAPPL_SNMP_MAX_OID && oid[i] >= 0 && packet->object_name[i] >= 0; i ++) { if (oid[i] != packet->object_name[i]) return (0); } return (i < _PAPPL_SNMP_MAX_OID && oid[i] == packet->object_name[i]); } // // '_papplSNMPIsOIDPrefixed()' - Test whether a SNMP response uses the specified // OID prefix. // // The array pointed to by "prefix" is terminated by the value -1. // int // O - 1 if prefixed, 0 if not prefixed _papplSNMPIsOIDPrefixed( _pappl_snmp_t *packet, // I - Response packet const int *prefix) // I - OID prefix { int i; // Looping var // Range check input... if (!packet || !prefix) return (0); // Compare OIDs... for (i = 0; i < _PAPPL_SNMP_MAX_OID && prefix[i] >= 0 && packet->object_name[i] >= 0; i ++) { if (prefix[i] != packet->object_name[i]) return (0); } return (i < _PAPPL_SNMP_MAX_OID); } // // '_papplSNMPOIDToString()' - Convert an OID to a string. // char * // O - New string or @code NULL@ on error _papplSNMPOIDToString(const int *src, // I - OID char *dst, // I - String buffer size_t dstsize) // I - Size of string buffer { char *dstptr, // Pointer into string buffer *dstend; // End of string buffer // Range check input... if (!src || !dst || dstsize < 4) return (NULL); // Loop through the OID array and build a string... for (dstptr = dst, dstend = dstptr + dstsize - 1; *src >= 0 && dstptr < dstend; src ++, dstptr += strlen(dstptr)) snprintf(dstptr, (size_t)(dstend - dstptr + 1), ".%d", *src); if (*src >= 0) return (NULL); else return (dst); } // // '_papplSNMPOpen()' - Open a SNMP socket. // int // O - SNMP socket file descriptor _papplSNMPOpen(int family) // I - Address family - @code AF_INET@ or @code AF_INET6@ { int fd; // SNMP socket file descriptor int val; // Socket option value // Create the SNMP socket... if ((fd = socket(family, SOCK_DGRAM, 0)) < 0) return (-1); // Set the "broadcast" flag... val = 1; if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val))) { close(fd); return (-1); } return (fd); } // // '_papplSNMPRead()' - Read and parse a SNMP response. // // If "timeout" is negative, @code _papplSNMPRead@ will wait for a response // indefinitely. // _pappl_snmp_t * // O - SNMP packet or @code NULL@ if none _papplSNMPRead(int fd, // I - SNMP socket file descriptor _pappl_snmp_t *packet, // I - SNMP packet buffer double timeout) // I - Timeout in seconds { unsigned char buffer[_PAPPL_SNMP_MAX_PACKET]; // Data packet ssize_t bytes; // Number of bytes received socklen_t addrlen; // Source address length http_addr_t address; // Source address // Range check input... if (fd < 0 || !packet) return (NULL); // Optionally wait for a response... if (timeout >= 0.0) { int ready; // Data ready on socket? struct pollfd pfd; // Polled file descriptor pfd.fd = fd; pfd.events = POLLIN; while ((ready = poll(&pfd, 1, (int)(timeout * 1000.0))) < 0 && (errno == EINTR || errno == EAGAIN)) ; // Wait for poll to complete... // If we don't have any data ready, return right away... if (ready <= 0) return (NULL); } // Read the response data... addrlen = sizeof(address); if ((bytes = recvfrom(fd, buffer, sizeof(buffer), 0, (void *)&address, &addrlen)) < 0) return (NULL); // Look for the response status code in the SNMP message header... asn1_decode_snmp(buffer, (size_t)bytes, packet); memcpy(&(packet->address), &address, sizeof(packet->address)); // Return decoded data packet... return (packet); } // // '_papplSNMPWalk()' - Enumerate a group of OIDs. // // This function queries all of the OIDs with the specified OID prefix, // calling the "cb" function for every response that is received. // // The array pointed to by "prefix" is terminated by the value -1. // // If "timeout" is negative, @code _papplSNMPWalk@ will wait for a response // indefinitely. // int // O - Number of OIDs found or -1 on error _papplSNMPWalk( int fd, // I - SNMP socket http_addr_t *address, // I - Address to query int version, // I - SNMP version const char *community, // I - Community name const int *prefix, // I - OID prefix double timeout, // I - Timeout for each response in seconds _pappl_snmp_cb_t cb, // I - Function to call for each response void *data) // I - User data pointer that is passed to the callback function { int count = 0; // Number of OIDs found unsigned request_id = 0; // Current request ID _pappl_snmp_t packet; // Current response packet int lastoid[_PAPPL_SNMP_MAX_OID]; // Last OID we got // Range check input... if (fd < 0 || !address || version != _PAPPL_SNMP_VERSION_1 || !community || !prefix || !cb) return (-1); // Copy the OID prefix and then loop until we have no more OIDs... _papplSNMPCopyOID(packet.object_name, prefix, _PAPPL_SNMP_MAX_OID); lastoid[0] = -1; for (;;) { request_id ++; if (!_papplSNMPWrite(fd, address, version, community, _PAPPL_ASN1_GET_NEXT_REQUEST, request_id, packet.object_name)) return (-1); if (!_papplSNMPRead(fd, &packet, timeout)) return (-1); if (!_papplSNMPIsOIDPrefixed(&packet, prefix) || _papplSNMPIsOID(&packet, lastoid)) return (count); if (packet.error || packet.error_status) return (count > 0 ? count : -1); _papplSNMPCopyOID(lastoid, packet.object_name, _PAPPL_SNMP_MAX_OID); count ++; (*cb)(&packet, data); } } // // '_papplSNMPWrite()' - Send an SNMP query packet. // // The array pointed to by "oid" is terminated by the value -1. // int // O - 1 on success, 0 on error _papplSNMPWrite( int fd, // I - SNMP socket http_addr_t *address, // I - Address to send to int version, // I - SNMP version const char *community, // I - Community name _pappl_asn1_t request_type, // I - Request type const unsigned request_id, // I - Request ID const int *oid) // I - OID { int i; // Looping var _pappl_snmp_t packet; // SNMP message packet unsigned char buffer[_PAPPL_SNMP_MAX_PACKET]; // SNMP message buffer ssize_t bytes; // Size of message http_addr_t temp; // Copy of address // Range check input... if (fd < 0 || !address || version != _PAPPL_SNMP_VERSION_1 || !community || (request_type != _PAPPL_ASN1_GET_REQUEST && request_type != _PAPPL_ASN1_GET_NEXT_REQUEST) || request_id < 1 || !oid) return (0); // Create the SNMP message... memset(&packet, 0, sizeof(packet)); packet.version = version; packet.request_type = request_type; packet.request_id = request_id; packet.object_type = _PAPPL_ASN1_NULL_VALUE; strlcpy(packet.community, community, sizeof(packet.community)); for (i = 0; oid[i] >= 0 && i < (_PAPPL_SNMP_MAX_OID - 1); i ++) packet.object_name[i] = oid[i]; packet.object_name[i] = -1; if (oid[i] >= 0) { errno = E2BIG; return (0); } bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet); if (bytes < 0) { errno = E2BIG; return (0); } // Send the message... temp = *address; temp.ipv4.sin_port = htons(_PAPPL_SNMP_PORT); return (sendto(fd, buffer, (size_t)bytes, 0, (void *)&temp, (socklen_t)httpAddrLength(&temp)) == bytes); } // // 'asn1_decode_snmp()' - Decode a SNMP packet. // static int // O - 0 on success, -1 on error asn1_decode_snmp( unsigned char *buffer, // I - Buffer size_t len, // I - Size of buffer _pappl_snmp_t *packet) // I - SNMP packet { unsigned char *bufptr, // Pointer into the data *bufend; // End of data unsigned length; // Length of value // Initialize the decoding... memset(packet, 0, sizeof(_pappl_snmp_t)); packet->object_name[0] = -1; bufptr = buffer; bufend = buffer + len; if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_SEQUENCE) { snmp_set_error(packet, _("Packet does not start with SEQUENCE")); } else if (asn1_get_length(&bufptr, bufend) == 0) { snmp_set_error(packet, _("SEQUENCE uses indefinite length")); } else if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_INTEGER) { snmp_set_error(packet, _("No version number")); } else if ((length = asn1_get_length(&bufptr, bufend)) == 0) { snmp_set_error(packet, _("Version uses indefinite length")); } else if ((packet->version = asn1_get_integer(&bufptr, bufend, length)) != _PAPPL_SNMP_VERSION_1) { snmp_set_error(packet, _("Bad SNMP version number")); } else if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_OCTET_STRING) { snmp_set_error(packet, _("No community name")); } else if ((length = asn1_get_length(&bufptr, bufend)) == 0) { snmp_set_error(packet, _("Community name uses indefinite length")); } else { asn1_get_string(&bufptr, bufend, length, packet->community, sizeof(packet->community)); if ((packet->request_type = (_pappl_asn1_t)asn1_get_type(&bufptr, bufend)) != _PAPPL_ASN1_GET_RESPONSE) { snmp_set_error(packet, _("Packet does not contain a Get-Response-PDU")); } else if (asn1_get_length(&bufptr, bufend) == 0) { snmp_set_error(packet, _("Get-Response-PDU uses indefinite length")); } else if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_INTEGER) { snmp_set_error(packet, _("No request-id")); } else if ((length = asn1_get_length(&bufptr, bufend)) == 0) { snmp_set_error(packet, _("request-id uses indefinite length")); } else { packet->request_id = (unsigned)asn1_get_integer(&bufptr, bufend, length); if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_INTEGER) { snmp_set_error(packet, _("No error-status")); } else if ((length = asn1_get_length(&bufptr, bufend)) == 0) { snmp_set_error(packet, _("error-status uses indefinite length")); } else { packet->error_status = asn1_get_integer(&bufptr, bufend, length); if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_INTEGER) { snmp_set_error(packet, _("No error-index")); } else if ((length = asn1_get_length(&bufptr, bufend)) == 0) { snmp_set_error(packet, _("error-index uses indefinite length")); } else { packet->error_index = asn1_get_integer(&bufptr, bufend, length); if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_SEQUENCE) { snmp_set_error(packet, _("No variable-bindings SEQUENCE")); } else if (asn1_get_length(&bufptr, bufend) == 0) { snmp_set_error(packet, _("variable-bindings uses indefinite length")); } else if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_SEQUENCE) { snmp_set_error(packet, _("No VarBind SEQUENCE")); } else if (asn1_get_length(&bufptr, bufend) == 0) { snmp_set_error(packet, _("VarBind uses indefinite length")); } else if (asn1_get_type(&bufptr, bufend) != _PAPPL_ASN1_OID) { snmp_set_error(packet, _("No name OID")); } else if ((length = asn1_get_length(&bufptr, bufend)) == 0) { snmp_set_error(packet, _("Name OID uses indefinite length")); } else { asn1_get_oid(&bufptr, bufend, length, packet->object_name, _PAPPL_SNMP_MAX_OID); packet->object_type = (_pappl_asn1_t)asn1_get_type(&bufptr, bufend); if ((length = asn1_get_length(&bufptr, bufend)) == 0 && packet->object_type != _PAPPL_ASN1_NULL_VALUE && packet->object_type != _PAPPL_ASN1_OCTET_STRING) { snmp_set_error(packet, _("Value uses indefinite length")); } else { switch (packet->object_type) { case _PAPPL_ASN1_BOOLEAN : packet->object_value.boolean = asn1_get_integer(&bufptr, bufend, length); break; case _PAPPL_ASN1_INTEGER : packet->object_value.integer = asn1_get_integer(&bufptr, bufend, length); break; case _PAPPL_ASN1_NULL_VALUE : break; case _PAPPL_ASN1_OCTET_STRING : case _PAPPL_ASN1_BIT_STRING : case _PAPPL_ASN1_HEX_STRING : packet->object_value.string.num_bytes = length; asn1_get_string(&bufptr, bufend, length, (char *)packet->object_value.string.bytes, sizeof(packet->object_value.string.bytes)); break; case _PAPPL_ASN1_OID : asn1_get_oid(&bufptr, bufend, length, packet->object_value.oid, _PAPPL_SNMP_MAX_OID); break; case _PAPPL_ASN1_COUNTER : packet->object_value.counter = asn1_get_integer(&bufptr, bufend, length); break; case _PAPPL_ASN1_GAUGE : packet->object_value.gauge = (unsigned)asn1_get_integer(&bufptr, bufend, length); break; case _PAPPL_ASN1_TIMETICKS : packet->object_value.timeticks = (unsigned)asn1_get_integer(&bufptr, bufend, length); break; default : snmp_set_error(packet, _("Unsupported value type")); break; } } } } } } } return (packet->error ? -1 : 0); } // // 'asn1_encode_snmp()' - Encode a SNMP packet. // static int // O - Length on success, -1 on error asn1_encode_snmp( unsigned char *buffer, // I - Buffer size_t bufsize, // I - Size of buffer _pappl_snmp_t *packet) // I - SNMP packet { unsigned char *bufptr; // Pointer into buffer unsigned total, // Total length msglen, // Length of entire message commlen, // Length of community string reqlen, // Length of request listlen, // Length of variable list varlen, // Length of variable namelen, // Length of object name OID valuelen; // Length of object value // Get the lengths of the community string, OID, and message... namelen = asn1_size_oid(packet->object_name); switch (packet->object_type) { case _PAPPL_ASN1_NULL_VALUE : valuelen = 0; break; case _PAPPL_ASN1_BOOLEAN : valuelen = asn1_size_integer(packet->object_value.boolean); break; case _PAPPL_ASN1_INTEGER : valuelen = asn1_size_integer(packet->object_value.integer); break; case _PAPPL_ASN1_OCTET_STRING : valuelen = packet->object_value.string.num_bytes; break; case _PAPPL_ASN1_OID : valuelen = asn1_size_oid(packet->object_value.oid); break; default : packet->error = "Unknown object type"; return (-1); } varlen = 1 + asn1_size_length(namelen) + namelen + 1 + asn1_size_length(valuelen) + valuelen; listlen = 1 + asn1_size_length(varlen) + varlen; reqlen = 2 + asn1_size_integer((int)packet->request_id) + 2 + asn1_size_integer(packet->error_status) + 2 + asn1_size_integer(packet->error_index) + 1 + asn1_size_length(listlen) + listlen; commlen = (unsigned)strlen(packet->community); msglen = 2 + asn1_size_integer(packet->version) + 1 + asn1_size_length(commlen) + commlen + 1 + asn1_size_length(reqlen) + reqlen; total = 1 + asn1_size_length(msglen) + msglen; if (total > bufsize) { packet->error = "Message too large for buffer"; return (-1); } // Then format the message... bufptr = buffer; *bufptr++ = _PAPPL_ASN1_SEQUENCE; // SNMPv1 message header asn1_set_length(&bufptr, msglen); asn1_set_integer(&bufptr, packet->version); // version *bufptr++ = _PAPPL_ASN1_OCTET_STRING; // community asn1_set_length(&bufptr, commlen); memcpy(bufptr, packet->community, commlen); bufptr += commlen; *bufptr++ = (unsigned char)packet->request_type; // Get-Request-PDU/Get-Next-Request-PDU asn1_set_length(&bufptr, reqlen); asn1_set_integer(&bufptr, (int)packet->request_id); asn1_set_integer(&bufptr, packet->error_status); asn1_set_integer(&bufptr, packet->error_index); *bufptr++ = _PAPPL_ASN1_SEQUENCE; // variable-bindings asn1_set_length(&bufptr, listlen); *bufptr++ = _PAPPL_ASN1_SEQUENCE; // variable asn1_set_length(&bufptr, varlen); asn1_set_oid(&bufptr, packet->object_name); // ObjectName switch (packet->object_type) { case _PAPPL_ASN1_NULL_VALUE : *bufptr++ = _PAPPL_ASN1_NULL_VALUE; // ObjectValue *bufptr++ = 0; // Length break; case _PAPPL_ASN1_BOOLEAN : asn1_set_integer(&bufptr, packet->object_value.boolean); break; case _PAPPL_ASN1_INTEGER : asn1_set_integer(&bufptr, packet->object_value.integer); break; case _PAPPL_ASN1_OCTET_STRING : *bufptr++ = _PAPPL_ASN1_OCTET_STRING; asn1_set_length(&bufptr, valuelen); memcpy(bufptr, packet->object_value.string.bytes, valuelen); bufptr += valuelen; break; case _PAPPL_ASN1_OID : asn1_set_oid(&bufptr, packet->object_value.oid); break; default : break; } return ((int)(bufptr - buffer)); } // // 'asn1_get_integer()' - Get an integer value. // static int // O - Integer value asn1_get_integer( unsigned char **buffer, // IO - Pointer in buffer unsigned char *bufend, // I - End of buffer unsigned length) // I - Length of value { int value; // Integer value if (*buffer >= bufend) return (0); if (length > sizeof(int)) { (*buffer) += length; return (0); } for (value = (**buffer & 0x80) ? ~0 : 0; length > 0 && *buffer < bufend; length --, (*buffer) ++) value = ((value & 0xffffff) << 8) | **buffer; return (value); } // // 'asn1_get_length()' - Get a value length. // static unsigned // O - Length asn1_get_length(unsigned char **buffer, // IO - Pointer in buffer unsigned char *bufend) // I - End of buffer { unsigned length; // Length if (*buffer >= bufend) return (0); length = **buffer; (*buffer) ++; if (length & 128) { int count; // Number of bytes for length if ((count = length & 127) > sizeof(unsigned)) { (*buffer) += count; return (0); } for (length = 0; count > 0 && *buffer < bufend; count --, (*buffer) ++) length = (length << 8) | **buffer; } return (length); } // // 'asn1_get_oid()' - Get an OID value. // static int // O - Number of OIDs asn1_get_oid( unsigned char **buffer, // IO - Pointer in buffer unsigned char *bufend, // I - End of buffer unsigned length, // I - Length of value int *oid, // I - OID buffer int oidsize) // I - Size of OID buffer { unsigned char *valend; // End of value int *oidptr, // Current OID *oidend; // End of OID buffer int number; // OID number if (*buffer >= bufend) return (0); valend = *buffer + length; oidptr = oid; oidend = oid + oidsize - 1; if (valend > bufend) valend = bufend; number = asn1_get_packed(buffer, bufend); if (number < 80) { *oidptr++ = number / 40; number = number % 40; *oidptr++ = number; } else { *oidptr++ = 2; number -= 80; *oidptr++ = number; } while (*buffer < valend) { number = asn1_get_packed(buffer, bufend); if (oidptr < oidend) *oidptr++ = number; } *oidptr = -1; return ((int)(oidptr - oid)); } // // 'asn1_get_packed()' - Get a packed integer value. // static int // O - Value asn1_get_packed( unsigned char **buffer, // IO - Pointer in buffer unsigned char *bufend) // I - End of buffer { int value; // Value if (*buffer >= bufend) return (0); value = 0; while (*buffer < bufend && (**buffer & 128)) { value = (value << 7) | (**buffer & 127); (*buffer) ++; } if (*buffer < bufend) { value = (value << 7) | **buffer; (*buffer) ++; } return (value); } // // 'asn1_get_string()' - Get a string value. // static char * // O - String asn1_get_string( unsigned char **buffer, // IO - Pointer in buffer unsigned char *bufend, // I - End of buffer unsigned length, // I - Value length char *string, // I - String buffer size_t strsize) // I - String buffer size { if (*buffer >= bufend) return (NULL); if (length > (unsigned)(bufend - *buffer)) length = (unsigned)(bufend - *buffer); if (length < strsize) { // String is smaller than the buffer... if (length > 0) memcpy(string, *buffer, length); string[length] = '\0'; } else { // String is larger than the buffer... memcpy(string, *buffer, strsize - 1); string[strsize - 1] = '\0'; } if (length > 0) (*buffer) += length; return (string); } // // 'asn1_get_type()' - Get a value type. // static int // O - Type asn1_get_type(unsigned char **buffer, // IO - Pointer in buffer unsigned char *bufend) // I - End of buffer { int type; // Type if (*buffer >= bufend) return (0); type = **buffer; (*buffer) ++; if ((type & 31) == 31) type = asn1_get_packed(buffer, bufend); return (type); } // // 'asn1_set_integer()' - Set an integer value. // static void asn1_set_integer(unsigned char **buffer,// IO - Pointer in buffer int integer) // I - Integer value { **buffer = _PAPPL_ASN1_INTEGER; (*buffer) ++; if (integer > 0x7fffff || integer < -0x800000) { **buffer = 4; (*buffer) ++; **buffer = (unsigned char)(integer >> 24); (*buffer) ++; **buffer = (unsigned char)(integer >> 16); (*buffer) ++; **buffer = (unsigned char)(integer >> 8); (*buffer) ++; **buffer = (unsigned char)integer; (*buffer) ++; } else if (integer > 0x7fff || integer < -0x8000) { **buffer = 3; (*buffer) ++; **buffer = (unsigned char)(integer >> 16); (*buffer) ++; **buffer = (unsigned char)(integer >> 8); (*buffer) ++; **buffer = (unsigned char)integer; (*buffer) ++; } else if (integer > 0x7f || integer < -0x80) { **buffer = 2; (*buffer) ++; **buffer = (unsigned char)(integer >> 8); (*buffer) ++; **buffer = (unsigned char)integer; (*buffer) ++; } else { **buffer = 1; (*buffer) ++; **buffer = (unsigned char)integer; (*buffer) ++; } } // // 'asn1_set_length()' - Set a value length. // static void asn1_set_length(unsigned char **buffer, // IO - Pointer in buffer unsigned length) // I - Length value { if (length > 255) { **buffer = 0x82; // 2-byte length (*buffer) ++; **buffer = (unsigned char)(length >> 8); (*buffer) ++; **buffer = (unsigned char)length; (*buffer) ++; } else if (length > 127) { **buffer = 0x81; // 1-byte length (*buffer) ++; **buffer = (unsigned char)length; (*buffer) ++; } else { **buffer = (unsigned char)length; // Length (*buffer) ++; } } // // 'asn1_set_oid()' - Set an OID value. // static void asn1_set_oid(unsigned char **buffer, // IO - Pointer in buffer const int *oid) // I - OID value { **buffer = _PAPPL_ASN1_OID; (*buffer) ++; asn1_set_length(buffer, asn1_size_oid(oid)); if (oid[1] < 0) { asn1_set_packed(buffer, oid[0] * 40); return; } asn1_set_packed(buffer, oid[0] * 40 + oid[1]); for (oid += 2; *oid >= 0; oid ++) asn1_set_packed(buffer, *oid); } // // 'asn1_set_packed()' - Set a packed integer value. // static void asn1_set_packed(unsigned char **buffer, // IO - Pointer in buffer int integer) // I - Integer value { if (integer > 0xfffffff) { **buffer = ((integer >> 28) & 0x7f) | 0x80; (*buffer) ++; } if (integer > 0x1fffff) { **buffer = ((integer >> 21) & 0x7f) | 0x80; (*buffer) ++; } if (integer > 0x3fff) { **buffer = ((integer >> 14) & 0x7f) | 0x80; (*buffer) ++; } if (integer > 0x7f) { **buffer = ((integer >> 7) & 0x7f) | 0x80; (*buffer) ++; } **buffer = integer & 0x7f; (*buffer) ++; } // // 'asn1_size_integer()' - Figure out the number of bytes needed for an // integer value. // static unsigned // O - Size in bytes asn1_size_integer(int integer) // I - Integer value { if (integer > 0x7fffff || integer < -0x800000) return (4); else if (integer > 0x7fff || integer < -0x8000) return (3); else if (integer > 0x7f || integer < -0x80) return (2); else return (1); } // // 'asn1_size_length()' - Figure out the number of bytes needed for a // length value. // static unsigned // O - Size in bytes asn1_size_length(unsigned length) // I - Length value { if (length > 0xff) return (3); else if (length > 0x7f) return (2); else return (1); } // // 'asn1_size_oid()' - Figure out the number of bytes needed for an // OID value. // static unsigned // O - Size in bytes asn1_size_oid(const int *oid) // I - OID value { unsigned length; // Length of value if (oid[1] < 0) return (asn1_size_packed(oid[0] * 40)); for (length = asn1_size_packed(oid[0] * 40 + oid[1]), oid += 2; *oid >= 0; oid ++) length += asn1_size_packed(*oid); return (length); } // // 'asn1_size_packed()' - Figure out the number of bytes needed for a // packed integer value. // static unsigned // O - Size in bytes asn1_size_packed(int integer) // I - Integer value { if (integer > 0xfffffff) return (5); else if (integer > 0x1fffff) return (4); else if (integer > 0x3fff) return (3); else if (integer > 0x7f) return (2); else return (1); } pappl-1.0.3/pappl/style.css000066400000000000000000000207761403603036100156250ustar00rootroot00000000000000/* Base styles */ @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Source+Code+Pro&display=swap'); body { background: white; color: black; font-family: 'Open Sans', sans-serif; font-size: 16px; margin: 0; } code, kbd, pre, tt, input[type=email], input[type=number], input[type=password], input[type=tel], input[type=text], textarea { font-family: 'Source Code Pro', monospace; } pre { white-space: pre-wrap; } /* Header */ .header { background: black; color: white; width: 100%; } .header2 { background: #333; color: white; width: 100%; } /* Navigation */ div.nav { color: #ccc; vertical-align: middle; } .header div.nav { font-size: 24px; line-height: 40px; } .header2 div.nav { font-size: 20px; line-height: 35px; } .header a.btn, .header a.btn:visited, .header2 a.btn, .header2 a.btn:visited { color: #ccc; text-decoration: none; white-space: nowrap; } .header a.btn:hover, .header2 a.btn:hover { color: #eee; } .header a.btn:active, .header2 a.btn:active { color: #fff; } .header span.active, .header2 span.active { color: #fff; white-space: nowrap; } .header a.btn, .header span.active, .header span.spacer { padding: 0 30px 0 0; } .header2 a.btn, .header2 span.active { padding: 0 20px 0 0; } div.nav img { height: 24px; margin-right: 10px; vertical-align: middle; } span.spacer { color: #ccc; } span.spacer:after { content: "⋮"; } /* Interior Content */ .content { margin: 20px 0 44px; } form { display: inline-block; } div.btn { display: inline-block; line-height: 32px; } .content a.btn, .content a.btn:visited, button, input[type=submit], select { border: none; border-radius: 5px; cursor: pointer; font-family: inherit; font-size: 14px; font-weight: normal; padding: 4px 8px; text-decoration: none; white-space: nowrap; -moz-appearance: none; -webkit-appearance: none; } .content a.btn, .content a.btn:visited, button, input[type=submit] { background: #333; color: #ccc; } .content a.btn:hover, button:hover, input[type=submit]:hover { background: #333; color: #eee; } .content a.btn:active, button:active, input[type=submit]:active { background: #444; color: #fff; } input[type=email], input[type=password], input[type=tel], input[type=text] { padding: 4px 8px; } input[type=number] { appearance: textfield; padding: 4px 0 4px 8px; } input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } select { background: #ccc url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23000000' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; color: black; padding: 4px 32px 4px 8px; } select:hover { background: #ddd url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23000000' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; } textarea { width: 100%; } label.image input[type=radio] { position: absolute; opacity: 0; width: 0; height: 0; } label.image input[type=radio] + img { border-radius: 5px; cursor: pointer; padding: 2px; } label.image input[type=radio]:checked + img { background: rgba(127,127,127,.5); } div.pager { padding: 10px 0; text-align: center; } div.log { background: #333; border: solid thin rgba(127,127,127,.5); color: #ccc; height: 30em; margin-top: 20px; overflow: scroll; width: 100%; } div.log pre { padding: 10px; } /* Footer */ .footer { background: rgba(224,224,224,.9); bottom: 0; color: #777; font-size: 12px; height: 24px; left: 0; position: fixed; right: 0; width: 100%; } .footer a { color: #c66; } /* Grid System - Rows and Columns */ .row { position: relative; width: 100%; } .row::after { clear: both; content: ""; display: table; width: 0; } .row [class^="col-"] { float: left; margin: 20px 0 0 2%; min-height: 1px; padding: 5px 0; } .row:first-child [class^="col-"] { margin: 0 0 0 2%; } h1:first-child, h2:first-child, h3:first-child, .row p:first-child { margin-top: 0; } .row p:last-child, .rounded p:last-child { margin-bottom: 0; } .col-1 { width: 6.1666%; } .col-2 { width: 14.3333%; } .col-3 { width: 22.5%; } .col-4 { width: 30.6666%; } .col-5 { width: 38.8333%; } .col-6 { width: 47%; } .col-7 { width: 55.1666%; } .col-8 { width: 63.3333%; } .col-9 { width: 71.5%; } .col-10 { width: 79.6666%; } .col-11 { width: 87.8333%; } .col-12 { width: 96%; } /* Inline Styles */ span.badge { background: #c00; border-radius: 8px; color: #fff; font-size: 14px; font-weight: bold; padding: 1px 6px; } span.bar { box-shadow: 1px 1px 2px rgba(51,51,51,0.25); font-size: 20px; line-height: 20px; } img.idle, img.processing, img.stopped { border-radius: 10px; float: left; height: 64px; margin-right: 10px; padding: 5px; width: 64px; } img.idle { background-color: rgba(0,192,0,0.5); } img.processing { background-color: rgba(224,224,0,0.5); } img.stopped { background-color: rgba(192,0,0,0.5); } .shaded { background-color: rgba(127,127,127,.25); } h1.title { border-bottom: solid 2px black; clear: both; } h2.title { border-bottom: solid 1px #555; clear: both; color: #555; } .title a:link, .title a:visited { color: inherit; text-decoration: none; } .title a.btn, .title a.btn:visited { color: #ccc; vertical-align: 25%; } .title a.btn:hover, .title a.btn:active { color: #fff; } .banner { animation: banner 3s 1s 1 forwards; background: rgba(160,160,224,0.5); border-radius: 5px; margin: 10px 0; padding: 10px; } @keyframes banner { 0% { background: rgba(160,160,224,0.5); } 100% { background: rgba(160,160,160,0.5); } } /* Form Tables */ table.form { border-collapse: collapse; margin-top: 10px; min-width: 50%; } table.form td, table.form th { line-height: 30px; padding: 5px 2px; vertical-align: top; } table.form th { text-align: right; } iframe#map { width: 300px; height: 200px; } /* List Tables */ table.list { border-bottom: solid thin black; border-collapse: collapse; clear: both; width: 100%; } table.list tr:nth-child(even) { background: #fcfcfc; } table.list tr:nth-child(odd) { background: #f0f0f0; } table.list th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; } table.list td { margin: 0; padding: 5px; vertical-align: top; } /* Meter Tables for Supply Levels */ table.meter { border-collapse: collapse; margin-top: 10px; min-width: 50%; max-width: 600px; } table.meter thead td { border-bottom: 1px solid black; border-left: 1px solid black; border-right: 1px solid black; height: 4px; line-height: 4px; padding: 0; width: 20%; } table.meter tfoot td { border-left: 1px solid black; border-right: 1px solid black; border-top: 1px solid black; height: 4px; line-height: 4px; padding: 0; width: 20%; } table.meter td { padding: 5px 0; } table.meter th { border-right: 1px solid black; padding: 5px; text-align: right; } /* Small Device Overrides */ @media only screen and (min-device-width: 320px) and (max-device-width: 480px), only screen and (min-device-width: 320px) and (max-device-width: 568px) { .content { margin: 20px 0 20px; } .footer { height: auto; position: relative; } .row [class^="col-"] { width: 96%; } iframe#map { width: 200px; height: 200px; } } /* Dark Mode Overrides */ @media (prefers-color-scheme: dark) { body { background: black; color: #ccc; } span.bar { box-shadow: 1px 1px 5px rgba(255,255,255,0.5); } h1.title { border-bottom: solid 2px #ccc; } h2.title { border-bottom: solid 1px #999; color: #999; } input[type=email], input[type=text], input[type=password], input[type=number] { background: #333; color: #ccc; } table.list { border-bottom: solid thin #ccc; } table.list tr:nth-child(even) { background: #222; color: #ccc; } table.list tr:nth-child(odd) { background: #333; } table.list th { background: black; border-bottom: solid thin #ccc; } table.meter thead td { border-bottom: 1px solid #ccc; border-left: 1px solid #ccc; border-right: 1px solid #ccc; } table.meter tfoot td { border-left: 1px solid #ccc; border-right: 1px solid #ccc; border-top: 1px solid #ccc; } table.meter th { border-right: 1px solid #ccc; } } pappl-1.0.3/pappl/system-accessors.c000066400000000000000000001431611403603036100174200ustar00rootroot00000000000000// // System accessor functions for the Printer Application Framework // // Copyright © 2020-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "system-private.h" #ifdef HAVE_LIBJPEG # include # ifndef JPEG_LIB_VERSION_MAJOR // Added in JPEGLIB 9 # define JPEG_LIB_VERSION_MAJOR 8 # define JPEG_LIB_VERSION_MINOR 0 # endif // !JPEG_LIB_VERSION_MAJOR #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG # include #endif // HAVE_LIBPNG // // Local functions... // static bool add_listeners(pappl_system_t *system, const char *name, int port, int family); static int compare_filters(_pappl_mime_filter_t *a, _pappl_mime_filter_t *b); static _pappl_mime_filter_t *copy_filter(_pappl_mime_filter_t *f); // // 'papplSystemAddListeners()' - Add network or domain socket listeners. // // This function adds socket listeners. The "name" parameter specifies the // listener address. Names starting with a slash (/) specify a UNIX domain // socket path, otherwise the name is treated as a fully-qualified domain name // or numeric IPv4 or IPv6 address. If name is `NULL`, the "any" addresses are // used ("0.0.0.0" and "[::]"). // // Listeners cannot be added after @link papplSystemRun@ is called. // bool // O - `true` on success, `false` on failure papplSystemAddListeners( pappl_system_t *system, // I - System const char *name) // I - Hostname, domain socket filename, or `NULL` { bool ret; // Return value if (!system) { return (false); } else if (system->is_running) { papplLog(system, PAPPL_LOGLEVEL_FATAL, "Tried to add listeners while system is running."); return (false); } if (name && *name == '/') { // Add a domain socket listener... ret = add_listeners(system, name, 0, AF_LOCAL); } else if (name && isdigit(*name & 255)) { // Add IPv4 listener... if (system->port) { ret = add_listeners(system, name, system->port, AF_INET); } else { int port = 7999 + (getuid() % 1000); // Current port do { port ++; ret = add_listeners(system, name, port, AF_INET); } while (!ret && port < 10000); if (ret) system->port = port; } } else if (name && *name == '[') { // Add IPv6 listener... if (system->port) { ret = add_listeners(system, name, system->port, AF_INET6); } else { int port = 7999 + (getuid() % 1000); // Current port do { port ++; ret = add_listeners(system, name, port, AF_INET6); } while (!ret && port < 10000); if (ret) system->port = port; } } else { // Add named listeners on both IPv4 and IPv6... if (system->port) { ret = add_listeners(system, name, system->port, AF_INET) || add_listeners(system, name, system->port, AF_INET6); } else { int port = 7999 + (getuid() % 1000); // Current port do { port ++; ret = add_listeners(system, name, port, AF_INET); } while (!ret && port < 10000); if (ret) { system->port = port; add_listeners(system, name, port, AF_INET6); } } } return (ret); } // // 'papplSystemAddMIMEFilter()' - Add a file filter to the system. // // This function adds a file filter to the system to be used for processing // different kinds of document data in print jobs. The "srctype" and "dsttype" // arguments specify the source and destination MIME media types as constant // strings. A destination MIME media type of "image/pwg-raster" specifies a // filter that uses the driver's raster interface. Other destination types // imply direct submission to the output device using the `papplDeviceXxx` // functions. // // > Note: This function may not be called while the system is running. // void papplSystemAddMIMEFilter( pappl_system_t *system, // I - System const char *srctype, // I - Source MIME media type (constant) string const char *dsttype, // I - Destination MIME media type (constant) string pappl_mime_filter_cb_t cb, // I - Filter callback function void *data) // I - Filter callback data { _pappl_mime_filter_t key; // Search key if (!system || system->is_running || !srctype || !dsttype || !cb) return; if (!system->filters) system->filters = cupsArrayNew3((cups_array_func_t)compare_filters, NULL, NULL, 0, (cups_acopy_func_t)copy_filter, (cups_afree_func_t)free); key.src = srctype; key.dst = dsttype; key.cb = cb; key.cbdata = data; if (!cupsArrayFind(system->filters, &key)) { papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Adding '%s' to '%s' filter.", srctype, dsttype); cupsArrayAdd(system->filters, &key); } } // // '_papplSystemExportVersions()' - Export the firmware versions to IPP attributes... // void _papplSystemExportVersions( pappl_system_t *system, // I - System ipp_t *ipp, // I - IPP message ipp_tag_t group_tag, // I - Group (`IPP_TAG_PRINTER` or `IPP_TAG_SYSTEM`) cups_array_t *ra) // I - Requested attributes or `NULL` for all { int i; // Looping var ipp_attribute_t *attr; // Attribute char name[128]; // Attribute name const char *name_prefix = (group_tag == IPP_TAG_PRINTER) ? "printer" : "system"; const char *values[20]; // String values char cups_sversion[32]; // String version of libcups #ifdef HAVE_LIBJPEG char jpeg_sversion[32]; // String version of libjpeg #endif // HAVE_LIBJPEG unsigned short version[4]; // Version of software components // "xxx-firmware-name" snprintf(name, sizeof(name), "%s-firmware-name", name_prefix); if (!ra || cupsArrayFind(ra, name)) { for (i = 0; i < system->num_versions; i ++) values[i] = system->versions[i].name; values[i ++] = "PAPPL"; values[i ++] = "libcups"; #ifdef HAVE_LIBJPEG values[i ++] = "libjpeg"; #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG values[i ++] = "libpng"; #endif // HAVE_LIBPNG ippAddStrings(ipp, group_tag, IPP_TAG_NAME, name, i, NULL, values); } // "xxx-firmware-patches" snprintf(name, sizeof(name), "%s-firmware-patches", name_prefix); if (!ra || cupsArrayFind(ra, name)) { for (i = 0; i < system->num_versions; i ++) values[i] = system->versions[i].patches; values[i ++] = ""; // No patches for PAPPL values[i ++] = ""; // No patches for CUPS #ifdef HAVE_LIBJPEG values[i ++] = ""; // No patches for libjpeg #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG values[i ++] = ""; // No patches for libpng #endif // HAVE_LIBPNG ippAddStrings(ipp, group_tag, IPP_TAG_TEXT, name, i, NULL, values); } // "xxx-firmware-string-version" snprintf(name, sizeof(name), "%s-firmware-string-version", name_prefix); if (!ra || cupsArrayFind(ra, name)) { for (i = 0; i < system->num_versions; i ++) values[i] = system->versions[i].sversion; values[i ++] = PAPPL_VERSION; snprintf(cups_sversion, sizeof(cups_sversion), "%d.%d.%d", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR, CUPS_VERSION_PATCH); values[i ++] = cups_sversion; #ifdef HAVE_LIBJPEG snprintf(jpeg_sversion, sizeof(jpeg_sversion), "%d.%d", JPEG_LIB_VERSION_MAJOR, JPEG_LIB_VERSION_MINOR); values[i ++] = jpeg_sversion; #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG values[i ++] = png_libpng_ver; #endif // HAVE_LIBPNG ippAddStrings(ipp, group_tag, IPP_TAG_TEXT, name, i, NULL, values); } // "xxx-firmware-version" snprintf(name, sizeof(name), "%s-firmware-version", name_prefix); if (!ra || cupsArrayFind(ra, name)) { for (i = 0, attr = NULL; i < system->num_versions; i ++) { if (attr) ippSetOctetString(ipp, &attr, ippGetCount(attr), system->versions[i].version, (int)sizeof(system->versions[i].version)); else attr = ippAddOctetString(ipp, group_tag, name, system->versions[i].version, (int)sizeof(system->versions[i].version)); } memset(version, 0, sizeof(version)); sscanf(PAPPL_VERSION, "%hu.%hu.%hu", version + 0, version + 1, version + 2); if (attr) ippSetOctetString(ipp, &attr, ippGetCount(attr), version, (int)sizeof(version)); else attr = ippAddOctetString(ipp, group_tag, name, version, (int)sizeof(version)); version[0] = CUPS_VERSION_MAJOR; version[1] = CUPS_VERSION_MINOR; version[2] = CUPS_VERSION_PATCH; ippSetOctetString(ipp, &attr, ippGetCount(attr), version, (int)sizeof(version)); #ifdef HAVE_LIBJPEG version[0] = JPEG_LIB_VERSION_MAJOR; version[1] = JPEG_LIB_VERSION_MINOR; version[2] = 0; ippSetOctetString(ipp, &attr, ippGetCount(attr), version, (int)sizeof(version)); #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG memset(version, 0, sizeof(version)); sscanf(png_libpng_ver, "%hu.%hu.%hu", version + 0, version + 1, version + 2); ippSetOctetString(ipp, &attr, ippGetCount(attr), version, (int)sizeof(version)); #endif // HAVE_LIBPNG } } // // '_papplSystemFindMIMEFilter()' - Find a filter for the given source and destination formats. // _pappl_mime_filter_t * // O - Filter data _papplSystemFindMIMEFilter( pappl_system_t *system, // I - System const char *srctype, // I - Source MIME media type string const char *dsttype) // I - Destination MIME media type string { _pappl_mime_filter_t key, // Search key *match; // Matching filter if (!system || !srctype || !dsttype) return (NULL); pthread_rwlock_rdlock(&system->rwlock); key.src = srctype; key.dst = dsttype; match = (_pappl_mime_filter_t *)cupsArrayFind(system->filters, &key); pthread_rwlock_unlock(&system->rwlock); return (match); } // // 'papplSystemGetAdminGroup()' - Get the current administrative group, if any. // // This function copies the current administrative group, if any, to the // specified buffer. // char * // O - Admin group or `NULL` if none papplSystemGetAdminGroup( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->admin_group) { strlcpy(buffer, system->admin_group, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetAuthService()' - Get the PAM authorization service, if any. // // This function returns the PAM authorization service being used by the system // for authentication, if any. // const char * // O - PAM authorization service or `NULL` if none papplSystemGetAuthService( pappl_system_t *system) // I - System { return (system ? system->auth_service : NULL); } // // 'papplSystemGetContact()' - Get the "system-contact" value. // // This function copies the current system contact information to the specified // buffer. // pappl_contact_t * // O - Contact papplSystemGetContact( pappl_system_t *system, // I - System pappl_contact_t *contact) // O - Contact { if (!system || !contact) { if (contact) memset(contact, 0, sizeof(pappl_contact_t)); return (contact); } pthread_rwlock_rdlock(&system->rwlock); *contact = system->contact; pthread_rwlock_unlock(&system->rwlock); return (contact); } // // 'papplSystemGetDefaultPrinterID()' - Get the current "default-printer-id" value. // // This function returns the positive integer identifier for the current // default printer or `0` if there is no default printer. // int // O - "default-printer-id" value papplSystemGetDefaultPrinterID( pappl_system_t *system) // I - System { return (system ? system->default_printer_id : 0); } // // 'papplSystemGetDefaultPrintGroup()' - Get the default print group, if any. // // This function copies the current default print group, if any, to the // specified buffer. // char * // O - Default print group or `NULL` if none papplSystemGetDefaultPrintGroup( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->default_print_group) { strlcpy(buffer, system->default_print_group, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetDNSSDName()' - Get the current DNS-SD service name. // // This function copies the current DNS-SD service name of the system, if any, // to the specified buffer. // char * // O - Current DNS-SD service name or `NULL` for none papplSystemGetDNSSDName( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->dns_sd_name) { strlcpy(buffer, system->dns_sd_name, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetFooterHTML()' - Get the footer HTML for the web interface, if any. // // This function returns the HTML for the web page footer, if any. The footer // HTML can be set using the @link papplSystemSetFooterHTML@ function. // const char * // O - Footer HTML or `NULL` if none papplSystemGetFooterHTML( pappl_system_t *system) // I - System { return (system ? system->footer_html : NULL); } // // 'papplSystemGetGeoLocation()' - Get the system geo-location string, if any. // // This function copies the current system geographic location as a "geo:" URI // to the specified buffer. // char * // O - "geo:" URI or `NULL` for none papplSystemGetGeoLocation( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->geo_location) { strlcpy(buffer, system->geo_location, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetHostname()' - Get the system hostname. // // This function copies the current system hostname to the specified buffer. // char * // O - Hostname papplSystemGetHostname( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->hostname) { strlcpy(buffer, system->hostname, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetLocation()' - Get the system location string, if any. // // This function copies the current human-readable location, if any, to the // specified buffer. // char * // O - Location string or `NULL` for none papplSystemGetLocation( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->location) { strlcpy(buffer, system->location, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetLogLevel()' - Get the system log level. // // This function returns the current system log level as an enumeration. // pappl_loglevel_t papplSystemGetLogLevel( pappl_system_t *system) // I - System { return (system ? system->loglevel : PAPPL_LOGLEVEL_UNSPEC); } // // 'papplSystemGetMaxLogSize()' - Get the maximum log file size. // // This function gets the maximum log file size, which is only used when logging // directly to a file. When the limit is reached, the current log file is // renamed to "filename.O" and a new log file is created. Set the maximum size // to `0` to disable log file rotation. // // The default maximum log file size is 1MiB or `1048576` bytes. // size_t // O - Maximum log file size or `0` for none papplSystemGetMaxLogSize( pappl_system_t *system) // I - System { return (system ? system->logmaxsize : 0); } // // 'papplSystemGetName()' - Get the system name. // // This function copies the current system name to the specified buffer. // char * // O - Name string papplSystemGetName( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->name) { strlcpy(buffer, system->name, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetNextPrinterID()' - Get the next "printer-id" value. // // This function returns the positive integer identifier that will be used for // the next printer that is created. // int // O - Next "printer-id" value papplSystemGetNextPrinterID( pappl_system_t *system) // I - System { return (system ? system->next_printer_id : 0); } // // 'papplSystemGetOptions()' - Get the system options. // // This function returns the system options as a bitfield. // pappl_soptions_t // O - System options papplSystemGetOptions( pappl_system_t *system) // I - System { return (system ? system->options : PAPPL_SOPTIONS_NONE); } // // 'papplSystemGetOrganization()' - Get the system organization string, if any. // // This function copies the current organization name, if any, to the // specified buffer. // char * // O - Organization string or `NULL` for none papplSystemGetOrganization( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->organization) { strlcpy(buffer, system->organization, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetOrganizationalUnit()' - Get the system organizational unit string, if any. // // This function copies the current organizational unit name, if any, to the // specified buffer. // char * // O - Organizational unit string or `NULL` for none papplSystemGetOrganizationalUnit( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { char *ret = NULL; // Return value if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); if (system->org_unit) { strlcpy(buffer, system->org_unit, bufsize); ret = buffer; } else *buffer = '\0'; pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (ret); } // // 'papplSystemGetPassword()' - Get the current web site access password. // // This function copies the current web site password hash, if any, to the // specified buffer. // // Note: The access password is only used when the PAM authentication service // is not set. // char * // O - Password hash papplSystemGetPassword( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (system && buffer && bufsize > 0) { pthread_rwlock_rdlock(&system->rwlock); strlcpy(buffer, system->password_hash, bufsize); pthread_rwlock_unlock(&system->rwlock); } else if (buffer) *buffer = '\0'; return (buffer); } // // 'papplSystemGetPort()' - Get the port number for network connections to the // system. // // This function returns the port number that is used for network connections // to the system. // int // O - Port number papplSystemGetPort( pappl_system_t *system) // I - System { return (system ? system->port : 0); } // // 'papplSystemGetServerHeader()' - Get the Server: header for HTTP responses. // // This function returns the value of the HTTP "Server:" header that is used // by the system. // const char * // O - Server: header string or `NULL` for none papplSystemGetServerHeader( pappl_system_t *system) // I - System { return (system ? system->server_header : NULL); } // // 'papplSystemGetSessionKey()' - Get the current session key. // // This function copies the current session key to the specified buffer. The // session key is used for web interface forms to provide CSRF protection and is // refreshed periodically. // char * // O - Session key papplSystemGetSessionKey( pappl_system_t *system, // I - System char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { time_t curtime = time(NULL); // Current time if (system && buffer && bufsize > 0) { if ((curtime - system->session_time) > 86400) { // Lock for updating the session key with random data... pthread_rwlock_wrlock(&system->session_rwlock); snprintf(system->session_key, sizeof(system->session_key), "%08x%08x%08x%08x%08x%08x%08x%08x", _papplGetRand(), _papplGetRand(), _papplGetRand(), _papplGetRand(), _papplGetRand(), _papplGetRand(), _papplGetRand(), _papplGetRand()); system->session_time = curtime; } else { // Lock for reading... pthread_rwlock_rdlock(&system->session_rwlock); } strlcpy(buffer, system->session_key, bufsize); pthread_rwlock_unlock(&system->session_rwlock); } else if (buffer) *buffer = '\0'; return (buffer); } // // 'papplSystemGetTLSOnly()' - Get the TLS-only state of the system. // // This function returns whether the system will only accept encrypted // connections. // bool // O - `true` if the system is only accepting TLS encrypted connections, `false` otherwise papplSystemGetTLSOnly( pappl_system_t *system) // I - System { return (system ? system->tls_only : false); } // // 'papplSystemGetUUID()' - Get the "system-uuid" value. // // This function returns the system's UUID value. // const char * // O - "system-uuid" value papplSystemGetUUID( pappl_system_t *system) // I - System { return (system ? system->uuid : NULL); } // // 'papplSystemGetVersions()' - Get the firmware names and versions. // // This function copies the system firmware information to the specified buffer. // The return value is always the number of firmware versions that have been // set using the @link papplSystemSetVersions@ function, regardless of the // value of the "max_versions" argument. // int // O - Number of firmware versions papplSystemGetVersions( pappl_system_t *system, // I - System int max_versions, // I - Maximum number of versions to return pappl_version_t *versions) // O - Versions array or `NULL` for don't care { if (versions && max_versions > 0) memset(versions, 0, (size_t)max_versions * sizeof(pappl_version_t)); if (system && versions && system->num_versions > 0) { pthread_rwlock_rdlock(&system->rwlock); if (max_versions > system->num_versions) memcpy(versions, system->versions, (size_t)system->num_versions * sizeof(pappl_version_t)); else memcpy(versions, system->versions, (size_t)max_versions * sizeof(pappl_version_t)); pthread_rwlock_unlock(&system->rwlock); } return (system ? system->num_versions : 0); } // // 'papplSystemHashPassword()' - Generate a password hash using salt and password strings. // // This function generates a password hash using the "salt" and "password" // strings. The "salt" string should be `NULL` to generate a new password hash // or the value of an existing password hash to verify that a given plaintext // "password" string matches the password hash. // // > Note: Hashed access passwords are only used when the PAM authentication // > service is not set. // char * // O - Hashed password papplSystemHashPassword( pappl_system_t *system, // I - System const char *salt, // I - Existing password hash or `NULL` to generate a new hash const char *password, // I - Plain-text password string char *buffer, // I - String buffer size_t bufsize) // I - Size of string buffer { if (system && password && buffer && bufsize > 0) { char nonce[100], // Nonce string *ptr, // Pointer into string temp[256]; // Temporary hash unsigned char hash[32]; // SHA2-256 hash if (salt && strchr(salt, '~')) { // Copy existing nonce from the salt string... strlcpy(nonce, salt, sizeof(nonce)); if ((ptr = strchr(nonce, ':')) != NULL) *ptr = '\0'; } else { // Generate a new random nonce... snprintf(nonce, sizeof(nonce), "%08x%08x", _papplGetRand(), _papplGetRand()); } snprintf(temp, sizeof(temp), "%s:%s", nonce, password); cupsHashData("sha2-256", temp, strlen(temp), hash, sizeof(hash)); cupsHashString(hash, sizeof(hash), temp, sizeof(temp)); snprintf(buffer, bufsize, "%s~%s", nonce, temp); } else if (buffer) *buffer = '\0'; return (buffer); } // // 'papplSystemIsRunning()' - Return whether the system is running. // // This function returns whether the system is running. // bool // O - `true` if the system is running, `false` otherwise papplSystemIsRunning( pappl_system_t *system) // I - System { return (system ? system->is_running : false); } // // 'papplSystemIsShutdown()' - Return whether the system has been shutdown. // // This function returns whether the system is shutdown or scheduled to // shutdown. // bool // O - `true` if the system is shutdown, `false` otherwise papplSystemIsShutdown( pappl_system_t *system) // I - System { return (system ? (!system->is_running || system->shutdown_time != 0) : false); } // // 'papplSystemIteratePrinters()' - Iterate all of the printers. // // This function iterates each of the printers managed by the system. The // "cb" function is called once per printer with the "system" and "data" values. // void papplSystemIteratePrinters( pappl_system_t *system, // I - System pappl_printer_cb_t cb, // I - Callback function void *data) // I - Callback data { int i, // Looping var count; // Number of printers if (!system || !cb) return; // Loop through the printers. // // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the printers array. pthread_rwlock_rdlock(&system->rwlock); for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) (cb)((pappl_printer_t *)cupsArrayIndex(system->printers, i), data); pthread_rwlock_unlock(&system->rwlock); } // // 'papplSystemMatchDriver()' - Match a driver to an IEEE-1284 device ID. // const char * papplSystemMatchDriver( pappl_system_t *system, // I - System const char *device_id) // I - IEEE-1284 device ID string { int i; // Looping var pappl_pr_driver_t *driver; // Current driver const char *drvstart, // Start of key/value pair *drvend, // End of key/value pair *didptr, // Pointer into device ID *didend; // End of device ID size_t drvlen, // Length of key/value pair didlen; // Length of device ID if (!system) return (NULL); didlen = strlen(device_id); for (i = system->num_drivers, driver = system->drivers; i > 0; i --, driver ++) { if (!driver->device_id) continue; // Parse each of the driver's device ID pairs and compare against the // supplied device ID... drvstart = driver->device_id; while (*drvstart) { // Skip leading semicolons and whitespace (not valid, but sometimes // present...) while (*drvstart == ';' || isspace(*drvstart & 255)) drvstart ++; if (!*drvstart) break; // Find the end of the current key:value pair... drvend = drvstart + 1; while (*drvend && *drvend != ';') drvend ++; if (*drvend == ';') drvend ++; drvlen = (size_t)(drvend - drvstart); // See if this string exists in the target device ID... didptr = device_id; didend = didptr + didlen - drvlen; while (didptr && didptr < didend) { if (!strncmp(didptr, drvstart, drvlen)) break; if ((didptr = strchr(didptr, ';')) != NULL) didptr ++; } if (!didptr || didptr >= didend) break; drvstart = drvend; } if (!*drvstart) break; } if (i > 0) return (driver->name); else return (NULL); } // // 'papplSystemSetAdminGroup()' - Set the administrative group. // // This function sets the group name used for administrative requests such as // adding or deleting a printer. // // > Note: The administrative group is only used when the PAM authorization // > service is also set when the system is created. // void papplSystemSetAdminGroup( pappl_system_t *system, // I - System const char *value) // I - Admin group { if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->admin_group); system->admin_group = value ? strdup(value) : NULL; if (system->admin_group && strcmp(system->admin_group, "none")) { char buffer[8192]; // Buffer for strings struct group grpbuf, // Group buffer *grp = NULL; // Admin group if (getgrnam_r(system->admin_group, &grpbuf, buffer, sizeof(buffer), &grp) || !grp) papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to find admin group '%s'.", system->admin_group); else system->admin_gid = grp->gr_gid; } else system->admin_gid = (gid_t)-1; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetContact()' - Set the "system-contact" value. // // This function sets the system contact value. // void papplSystemSetContact( pappl_system_t *system, // I - System pappl_contact_t *contact) // I - Contact { if (!system || !contact) return; pthread_rwlock_wrlock(&system->rwlock); system->contact = *contact; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } // // 'papplSystemSetDefaultPrinterID()' - Set the "default-printer-id" value. // // This function sets the default printer using its unique positive integer // identifier. // void papplSystemSetDefaultPrinterID( pappl_system_t *system, // I - System int default_printer_id) // I - "default-printer-id" value { if (system) { pthread_rwlock_wrlock(&system->rwlock); system->default_printer_id = default_printer_id; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetDefaultPrintGroup()' - Set the default print group. // // This function sets the default group name used for print requests. // // > Note: The default print group is only used when the PAM authorization // > service is also set when the system is created. // void papplSystemSetDefaultPrintGroup( pappl_system_t *system, // I - System const char *value) // I - Default print group or `NULL` for none { if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->default_print_group); system->default_print_group = value ? strdup(value) : NULL; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetDNSSDName()' - Set the DNS-SD service name. // // This function sets the DNS-SD service name of the system. If `NULL`, the // DNS-SD registration is removed. // void papplSystemSetDNSSDName( pappl_system_t *system, // I - System const char *value) // I - DNS-SD service name or `NULL` for none { if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->dns_sd_name); system->dns_sd_name = value ? strdup(value) : NULL; system->dns_sd_collision = false; system->dns_sd_serial = 0; _papplSystemConfigChanged(system); if (!value) _papplSystemUnregisterDNSSDNoLock(system); else _papplSystemRegisterDNSSDNoLock(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetFooterHTML()' - Set the footer HTML for the web interface. // // This function sets the footer HTML for the web interface. // // > Note: The footer HTML can only be set prior to calling // > @link papplSystemRun@. // void papplSystemSetFooterHTML( pappl_system_t *system, // I - System const char *html) // I - Footer HTML or `NULL` for none { if (system && html && !system->is_running) { pthread_rwlock_wrlock(&system->rwlock); free(system->footer_html); system->footer_html = strdup(html); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetGeoLocation()' - Set the geographic location string. // // This function sets the geographic location of the system as a "geo:" URI. // If `NULL`, the location is cleared. // void papplSystemSetGeoLocation( pappl_system_t *system, // I - System const char *value) // I - "geo:" URI or `NULL` for none { float lat, lon; // Latitude and longitude from geo: URI // Validate geo-location - must be NULL or a "geo:" URI... if (value && sscanf(value, "geo:%f,%f", &lat, &lon) != 2) return; if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->geo_location); system->geo_location = value ? strdup(value) : NULL; _papplSystemConfigChanged(system); _papplSystemRegisterDNSSDNoLock(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetHostname()' - Set the system hostname. // // This function sets the system hostname. If `NULL`, the default hostname // is used. // void papplSystemSetHostname( pappl_system_t *system, // I - System const char *value) // I - Hostname or `NULL` for default { if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->hostname); if (value) { #if !defined(__APPLE__) && !_WIN32 cups_file_t *fp; // Hostname file if ((fp = cupsFileOpen("/etc/hostname", "w")) != NULL) { cupsFilePrintf(fp, "%s\n", value); cupsFileClose(fp); } #endif // !__APPLE__ && !_WIN32 #ifdef HAVE_AVAHI _pappl_dns_sd_t master = _papplDNSSDInit(system); // DNS-SD master reference if (master) avahi_client_set_host_name(master, value); #endif // HAVE_AVAHI sethostname(value, (int)strlen(value)); system->hostname = strdup(value); } else { char temp[1024], // Temporary hostname string *ptr; // Pointer in temporary hostname #ifdef HAVE_AVAHI _pappl_dns_sd_t master = _papplDNSSDInit(system); // DNS-SD master reference const char *avahi_name = master ? avahi_client_get_host_name_fqdn(master) : NULL; // mDNS hostname if (avahi_name) strlcpy(temp, avahi_name, sizeof(temp)); else #endif /* HAVE_AVAHI */ httpGetHostname(NULL, temp, sizeof(temp)); if ((ptr = strstr(temp, ".lan")) != NULL && !ptr[4]) { // Replace hostname.lan with hostname.local strlcpy(ptr, ".local", sizeof(temp) - (size_t)(ptr - temp)); } else if (!strrchr(temp, '.')) { // No domain information, so append .local to hostname... ptr = temp + strlen(temp); strlcpy(ptr, ".local", sizeof(temp) - (size_t)(ptr - temp)); } system->hostname = strdup(temp); } // Force an update of all DNS-SD registrations... system->dns_sd_host_changes = -1; pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetLocation()' - Set the system location string, if any. // // This function sets the human-readable location of the system. If `NULL`, // the location is cleared. // void papplSystemSetLocation( pappl_system_t *system, // I - System const char *value) // I - Location or `NULL` for none { if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->location); system->location = value ? strdup(value) : NULL; _papplSystemConfigChanged(system); _papplSystemRegisterDNSSDNoLock(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetLogLevel()' - Set the system log level // // This function sets the log level as an enumeration. // void papplSystemSetLogLevel( pappl_system_t *system, // I - System pappl_loglevel_t loglevel) // I - Log Level { if (system) { pthread_rwlock_wrlock(&system->rwlock); system->loglevel = loglevel; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetMaxLogSize()' - Set the maximum log file size in bytes. // // This function sets the maximum log file size in bytes, which is only used // when logging directly to a file. When the limit is reached, the current log // file is renamed to "filename.O" and a new log file is created. Set the // maximum size to `0` to disable log file rotation. // // The default maximum log file size is 1MiB or `1048576` bytes. // void papplSystemSetMaxLogSize( pappl_system_t *system, // I - System size_t maxsize) // I - Maximum log size in bytes or `0` for none { if (system) { pthread_rwlock_wrlock(&system->rwlock); system->logmaxsize = maxsize; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetMIMECallback()' - Set the MIME typing callback for the system. // // This function sets a custom MIME typing callback for the system. The MIME // typing callback extends the built-in MIME typing support for other media // types that are supported by the application, typically vendor print formats. // // The callback function receives a buffer containing the initial bytes of the // document data, the length of the buffer, and the callback data. It can then // return `NULL` if the content is not recognized or a constant string // containing the MIME media type, for example "application/vnd.hp-pcl" for // HP PCL print data. // void papplSystemSetMIMECallback( pappl_system_t *system, // I - System pappl_mime_cb_t cb, // I - Callback function void *data) // I - Callback data { if (system) { pthread_rwlock_wrlock(&system->rwlock); system->config_time = time(NULL); system->mime_cb = cb; system->mime_cbdata = data; pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetNextPrinterID()' - Set the next "printer-id" value. // // This function sets the unique positive integer identifier that will be used // for the next printer that is created. It is typically only called as part // of restoring the state of a system. // // > Note: The next printer ID can only be set prior to calling // > @link papplSystemRun@. // void papplSystemSetNextPrinterID( pappl_system_t *system, // I - System int next_printer_id) // I - Next "printer-id" value { if (system && !system->is_running) { pthread_rwlock_wrlock(&system->rwlock); system->next_printer_id = next_printer_id; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetOperationCallback()' - Set the IPP operation callback. // // This function sets a custom IPP operation handler for the system that is // called for any IPP operations that are not handled by the built-in IPP // services. // // > Note: The operation callback can only be set prior to calling // > @link papplSystemRun@. // void papplSystemSetOperationCallback( pappl_system_t *system, // I - System pappl_ipp_op_cb_t cb, // I - Callback function void *data) // I - Callback data { if (system && !system->is_running) { pthread_rwlock_wrlock(&system->rwlock); system->op_cb = cb; system->op_cbdata = data; pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetOrganization()' - Set the system organization string, if any. // // This function sets the organization name for the system. If `NULL`, the // name is cleared. // void papplSystemSetOrganization( pappl_system_t *system, // I - System const char *value) // I - Organization or `NULL` for none { if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->organization); system->organization = value ? strdup(value) : NULL; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetOrganizationalUnit()' - Set the system organizational unit // string, if any. // // This function sets the organizational unit name for the system. If `NULL`, // the name is cleared. // void papplSystemSetOrganizationalUnit( pappl_system_t *system, // I - System const char *value) // I - Organizational unit or `NULL` for none { if (system) { pthread_rwlock_wrlock(&system->rwlock); free(system->org_unit); system->org_unit = value ? strdup(value) : NULL; _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetPassword()' - Set the access password hash string. // // This function sets the hash for the web access password. The hash string is // generated using the @link papplSystemHashPassword@ function. // // > Note: The access password is only used when the PAM authentication service // > is not set. // void papplSystemSetPassword( pappl_system_t *system, // I - System const char *hash) // I - Hash string { if (system && hash) { pthread_rwlock_wrlock(&system->rwlock); strlcpy(system->password_hash, hash, sizeof(system->password_hash)); _papplSystemConfigChanged(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetPrinterDrivers()' - Set the list of drivers and the driver // callbacks. // // This function sets the lists of printer drivers, the optional auto-add // callback function, the optional creation callback, and the required driver // initialization callback function. // // The auto-add callback ("autoadd_cb") finds a compatible driver name for the // specified printer. It is used when the client or user specifies the "auto" // driver name, and for the "autoadd" sub-command for the `papplMainloop` API. // // The creation callback ("create_cb") is called at the end of printer creation // to make any common changes or additions to a new printer. It is typically // used to add extra web pages, add per-printer static resources, and/or // initialize the contact and location information. // // The driver initialization callback ("driver_cb") is called to initialize the // `pappl_pr_driver_data_t` structure, which provides all of the printer // capabilities and callbacks for printing. // void papplSystemSetPrinterDrivers( pappl_system_t *system, // I - System int num_drivers, // I - Number of drivers pappl_pr_driver_t *drivers, // I - Drivers pappl_pr_autoadd_cb_t autoadd_cb, // I - Auto-add callback function or `NULL` if none pappl_pr_create_cb_t create_cb, // I - Printer creation callback function or `NULL` if none pappl_pr_driver_cb_t driver_cb, // I - Driver initialization callback function void *data) // I - Callback data { if (system) { pthread_rwlock_wrlock(&system->rwlock); system->config_time = time(NULL); system->num_drivers = num_drivers; system->drivers = drivers; system->autoadd_cb = autoadd_cb; system->create_cb = create_cb; system->driver_cb = driver_cb; system->driver_cbdata = data; pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetSaveCallback()' - Set the save callback. // // This function sets a callback that is used to periodically save the current // system state. Typically the callback function ("cb") is // @link papplSystemSaveState@ and the callback data ("data") is the name of // the state file: // // ``` // |papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState, // | (void *)filename); // ``` // // > Note: The save callback can only be set prior to calling // > @link papplSystemRun@. // void papplSystemSetSaveCallback( pappl_system_t *system, // I - System pappl_save_cb_t cb, // I - Callback function void *data) // I - Callback data { if (system && !system->is_running) { pthread_rwlock_wrlock(&system->rwlock); system->save_cb = cb; system->save_cbdata = data; pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetUUID()' - Set the system UUID. // // This function sets the system UUID value, overriding the default (generated) // value. It is typically used when restoring the state of a previous // incarnation of the system. // // > Note: The UUID can only be set prior to calling @link papplSystemRun@. // void papplSystemSetUUID( pappl_system_t *system, // I - System const char *value) // I - UUID { if (system && !system->is_running) { pthread_rwlock_wrlock(&system->rwlock); free(system->uuid); if (value) { system->uuid = strdup(value); } else { char uuid[64]; // UUID value _papplSystemMakeUUID(system, NULL, 0, uuid, sizeof(uuid)); system->uuid = strdup(uuid); } _papplSystemRegisterDNSSDNoLock(system); pthread_rwlock_unlock(&system->rwlock); } } // // 'papplSystemSetVersions()' - Set the firmware names and versions. // // This function sets the names and versions of each firmware/software component // of the printer application. // void papplSystemSetVersions( pappl_system_t *system, // I - System int num_versions, // I - Number of versions pappl_version_t *versions) // I - Firmware versions { if (system && num_versions && versions) { pthread_rwlock_wrlock(&system->rwlock); if (num_versions > (int)(sizeof(system->versions) / sizeof(system->versions[0]))) system->num_versions = (int)(sizeof(system->versions) / sizeof(system->versions[0])); else system->num_versions = num_versions; memcpy(system->versions, versions, (size_t)system->num_versions * sizeof(pappl_version_t)); pthread_rwlock_unlock(&system->rwlock); } } // // 'add_listeners()' - Create and add listener sockets to a system. // static bool // O - `true` on success or `false` on failure add_listeners( pappl_system_t *system, // I - System const char *name, // I - Host name or `NULL` for any address int port, // I - Port number int family) // I - Address family { bool ret = false; // Return value int sock; // Listener socket http_addrlist_t *addrlist, // Listen addresses *addr; // Current address char service[255]; // Service port if (name && (!strcmp(name, "*") || !*name)) name = NULL; snprintf(service, sizeof(service), "%d", port); if ((addrlist = httpAddrGetList(name, family, service)) == NULL) { if (name && *name == '/') papplLog(system, PAPPL_LOGLEVEL_INFO, "Unable to lookup address(es) for '%s': %s", name, cupsLastErrorString()); else papplLog(system, PAPPL_LOGLEVEL_INFO, "Unable to lookup address(es) for '%s:%d': %s", name ? name : "*", port, cupsLastErrorString()); } else { for (addr = addrlist; addr && system->num_listeners < _PAPPL_MAX_LISTENERS; addr = addr->next) { if ((sock = httpAddrListen(&(addrlist->addr), port)) < 0) { char temp[256]; // String address if (system->port) { if (name && *name == '/') papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create listener socket for '%s': %s", name, cupsLastErrorString()); else papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create listener socket for '%s:%d': %s", httpAddrString(&addr->addr, temp, (int)sizeof(temp)), system->port, cupsLastErrorString()); } } else { ret = true; system->listeners[system->num_listeners].fd = sock; system->listeners[system->num_listeners ++].events = POLLIN; if (name && *name == '/') papplLog(system, PAPPL_LOGLEVEL_INFO, "Listening for connections on '%s'.", name); else papplLog(system, PAPPL_LOGLEVEL_INFO, "Listening for connections on '%s:%d'.", name ? name : "*", port); } } httpAddrFreeList(addrlist); } return (ret); } // // 'compare_filters()' - Compare two filters. // static int // O - Result of comparison compare_filters(_pappl_mime_filter_t *a,// I - First filter _pappl_mime_filter_t *b)// I - Second filter { int result = strcmp(a->src, b->src); if (!result) result = strcmp(a->dst, b->dst); return (result); } // // 'copy_filter()' - Copy a filter definition. // static _pappl_mime_filter_t * // O - New filter copy_filter(_pappl_mime_filter_t *f) // I - Filter definition { _pappl_mime_filter_t *newf = calloc(1, sizeof(_pappl_mime_filter_t)); // New filter if (newf) memcpy(newf, f, sizeof(_pappl_mime_filter_t)); return (newf); } pappl-1.0.3/pappl/system-ipp.c000066400000000000000000000520471403603036100162250ustar00rootroot00000000000000// // IPP processing for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local type... // typedef struct _pappl_attr_s // Input attribute structure { const char *name; // Attribute name ipp_tag_t value_tag; // Value tag int max_count; // Max number of values } _pappl_attr_t; // // Local functions... // static void ipp_create_printer(pappl_client_t *client); static void ipp_delete_printer(pappl_client_t *client); static void ipp_get_printers(pappl_client_t *client); static void ipp_get_system_attributes(pappl_client_t *client); static void ipp_set_system_attributes(pappl_client_t *client); static void ipp_shutdown_all_printers(pappl_client_t *client); // // '_papplSystemProcessIPP()' - Process an IPP System request. // void _papplSystemProcessIPP( pappl_client_t *client) // I - Client { switch (ippGetOperation(client->request)) { case IPP_OP_CREATE_PRINTER : ipp_create_printer(client); break; case IPP_OP_DELETE_PRINTER : ipp_delete_printer(client); break; case IPP_OP_GET_PRINTERS : case IPP_OP_CUPS_GET_PRINTERS : ipp_get_printers(client); break; case IPP_OP_GET_PRINTER_ATTRIBUTES : case IPP_OP_CUPS_GET_DEFAULT : client->printer = papplSystemFindPrinter(client->system, NULL, client->system->default_printer_id, NULL); _papplPrinterProcessIPP(client); break; case IPP_OP_GET_SYSTEM_ATTRIBUTES : ipp_get_system_attributes(client); break; case IPP_OP_SET_SYSTEM_ATTRIBUTES : ipp_set_system_attributes(client); break; case IPP_OP_SHUTDOWN_ALL_PRINTERS : ipp_shutdown_all_printers(client); break; default : if (client->system->op_cb && (client->system->op_cb)(client, client->system->op_cbdata)) break; papplClientRespondIPP(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, "Operation not supported."); break; } } // // 'ipp_create_printer()' - Create a printer. // static void ipp_create_printer( pappl_client_t *client) // I - Client { const char *printer_name, // Printer name *device_id, // Device URI *device_uri, // Device URI *driver_name; // Name of driver ipp_attribute_t *attr; // Current attribute pappl_printer_t *printer; // Printer cups_array_t *ra; // Requested attributes http_status_t auth_status; // Authorization status // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } // Is the system configured to support multiple printers? if (!(client->system->options & PAPPL_SOPTIONS_MULTI_QUEUE)) { papplClientRespondIPP(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, "This operation is not supported."); return; } // Get required attributes... if ((attr = ippFindAttribute(client->request, "printer-service-type", IPP_TAG_ZERO)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing 'printer-service-type' attribute in request."); return; } else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION || ippGetValueTag(attr) != IPP_TAG_KEYWORD || ippGetCount(attr) != 1 || strcmp(ippGetString(attr, 0, NULL), "print")) { papplClientRespondIPPUnsupported(client, attr); return; } if ((attr = ippFindAttribute(client->request, "printer-name", IPP_TAG_ZERO)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing 'printer-name' attribute in request."); return; } else if (ippGetGroupTag(attr) != IPP_TAG_PRINTER || (ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1 || strlen(ippGetString(attr, 0, NULL)) > 127) { papplClientRespondIPPUnsupported(client, attr); return; } else printer_name = ippGetString(attr, 0, NULL); if ((attr = ippFindAttribute(client->request, "printer-device-id", IPP_TAG_ZERO)) != NULL && (ippGetGroupTag(attr) != IPP_TAG_PRINTER || ippGetValueTag(attr) != IPP_TAG_TEXT || ippGetCount(attr) != 1)) { papplClientRespondIPPUnsupported(client, attr); return; } else device_id = ippGetString(attr, 0, NULL); if ((attr = ippFindAttribute(client->request, "smi2699-device-uri", IPP_TAG_ZERO)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing 'smi2699-device-uri' attribute in request."); return; } else if (ippGetGroupTag(attr) != IPP_TAG_PRINTER || ippGetValueTag(attr) != IPP_TAG_URI || ippGetCount(attr) != 1) { papplClientRespondIPPUnsupported(client, attr); return; } else { device_uri = ippGetString(attr, 0, NULL); if (!papplDeviceIsSupported(device_uri)) { papplClientRespondIPPUnsupported(client, attr); return; } } if ((attr = ippFindAttribute(client->request, "smi2699-device-command", IPP_TAG_ZERO)) == NULL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing 'smi2699-device-command' attribute in request."); return; } else if (ippGetGroupTag(attr) != IPP_TAG_PRINTER || ippGetValueTag(attr) != IPP_TAG_KEYWORD || ippGetCount(attr) != 1) { papplClientRespondIPPUnsupported(client, attr); return; } else if (client->system->driver_cb) { driver_name = ippGetString(attr, 0, NULL); } else { papplLog(client->system, PAPPL_LOGLEVEL_ERROR, "No driver callback set, unable to add printer."); papplClientRespondIPPUnsupported(client, attr); return; } // Create the printer... if ((printer = papplPrinterCreate(client->system, 0, printer_name, driver_name, device_id, device_uri)) == NULL) { if (errno == EEXIST) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Printer name '%s' already exists.", printer_name); ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_NAME, "printer-name", NULL, printer_name); } else if (errno == EIO) { papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Driver '%s' cannot be used with this printer.", driver_name); ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD, "smi2699-device-command", NULL, driver_name); } else if (errno == EINVAL) { papplClientRespondIPP(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Printer names must start with a letter or underscore and cannot contain special characters."); ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_NAME, "printer-name", NULL, printer_name); } else { papplClientRespondIPP(client, IPP_STATUS_ERROR_INTERNAL, "An error occurred when adding the printer: %s.", strerror(errno)); } return; } if (!_papplPrinterSetAttributes(client, printer)) return; // Return the printer papplClientRespondIPP(client, IPP_STATUS_OK, NULL); ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); cupsArrayAdd(ra, "printer-id"); cupsArrayAdd(ra, "printer-is-accepting-jobs"); cupsArrayAdd(ra, "printer-state"); cupsArrayAdd(ra, "printer-state-reasons"); cupsArrayAdd(ra, "printer-uuid"); cupsArrayAdd(ra, "printer-xri-supported"); _papplPrinterCopyAttributes(client, printer, ra, NULL); cupsArrayDelete(ra); } // // 'ipp_delete_printer()' - Delete a printer. // static void ipp_delete_printer( pappl_client_t *client) // I - Client { http_status_t auth_status; // Authorization status // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } if (!client->printer) { papplClientRespondIPP(client, IPP_STATUS_ERROR_NOT_FOUND, "Printer not found."); return; } if (!client->printer->processing_job) papplPrinterDelete(client->printer); else client->printer->is_deleted = true; papplClientRespondIPP(client, IPP_STATUS_OK, NULL); } // // 'ipp_get_printers()' - Get printers. // static void ipp_get_printers( pappl_client_t *client) // I - Client { pappl_system_t *system = client->system; // System cups_array_t *ra; // Requested attributes array int i, // Looping var count, // Number of printers limit; // Maximum number to return pappl_printer_t *printer; // Current printer const char *format; // "document-format" value, if any // Get request attributes... limit = ippGetInteger(ippFindAttribute(client->request, "limit", IPP_TAG_INTEGER), 0); ra = ippCreateRequestedArray(client->request); format = ippGetString(ippFindAttribute(client->request, "document-format", IPP_TAG_MIMETYPE), 0, NULL); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); pthread_rwlock_rdlock(&system->rwlock); // Enumerate the printers for the client... count = cupsArrayCount(system->printers); if (limit > 0 && limit < count) count = limit; for (i = 0; i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); if (limit && i >= limit) break; if (i) ippAddSeparator(client->response); pthread_rwlock_rdlock(&printer->rwlock); _papplPrinterCopyAttributes(client, printer, ra, format); pthread_rwlock_unlock(&printer->rwlock); } pthread_rwlock_unlock(&system->rwlock); cupsArrayDelete(ra); } // // 'ipp_get_system_attributes()' - Get system attributes. // static void ipp_get_system_attributes( pappl_client_t *client) // I - Client { pappl_system_t *system = client->system; // System cups_array_t *ra; // Requested attributes array int i, // Looping var count; // Count of values pappl_printer_t *printer; // Current printer ipp_attribute_t *attr; // Current attribute ipp_t *col; // configured-printers value time_t config_time = system->config_time; // system-config-change-[date-]time value time_t state_time = 0; // system-state-change-[date-]time value ra = ippCreateRequestedArray(client->request); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); pthread_rwlock_rdlock(&system->rwlock); _papplCopyAttributes(client->response, system->attrs, ra, IPP_TAG_ZERO, IPP_TAG_CUPS_CONST); if (!ra || cupsArrayFind(ra, "system-config-change-date-time") || cupsArrayFind(ra, "system-config-change-time")) { for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); if (config_time < printer->config_time) config_time = printer->config_time; } if (!ra || cupsArrayFind(ra, "system-config-change-date-time")) ippAddDate(client->response, IPP_TAG_SYSTEM, "system-config-change-date-time", ippTimeToDate(config_time)); if (!ra || cupsArrayFind(ra, "system-config-change-time")) ippAddInteger(client->response, IPP_TAG_SYSTEM, IPP_TAG_INTEGER, "system-config-change-time", (int)(config_time - system->start_time)); } if (!ra || cupsArrayFind(ra, "system-configured-printers")) { attr = ippAddCollections(client->response, IPP_TAG_SYSTEM, "system-configured-printers", cupsArrayCount(system->printers), NULL); for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); col = ippNew(); pthread_rwlock_rdlock(&printer->rwlock); ippAddInteger(col, IPP_TAG_SYSTEM, IPP_TAG_INTEGER, "printer-id", printer->printer_id); ippAddString(col, IPP_TAG_SYSTEM, IPP_TAG_TEXT, "printer-info", NULL, printer->name); ippAddBoolean(col, IPP_TAG_SYSTEM, "printer-is-accepting-jobs", 1); ippAddString(col, IPP_TAG_SYSTEM, IPP_TAG_TEXT, "printer-name", NULL, printer->name); ippAddString(col, IPP_TAG_SYSTEM, IPP_TAG_KEYWORD, "printer-service-type", NULL, "print"); _papplPrinterCopyState(col, printer, NULL); _papplPrinterCopyXRI(client, col, printer); pthread_rwlock_unlock(&printer->rwlock); ippSetCollection(client->response, &attr, i, col); ippDelete(col); } } if (!ra || cupsArrayFind(ra, "system-contact-col")) { col = _papplContactExport(&system->contact); ippAddCollection(client->response, IPP_TAG_SYSTEM, "system-contact-col", col); ippDelete(col); } if (!ra || cupsArrayFind(ra, "system-current-time")) ippAddDate(client->response, IPP_TAG_SYSTEM, "system-current-time", ippTimeToDate(time(NULL))); if (!ra || cupsArrayFind(ra, "system-default-printer-id")) ippAddInteger(client->response, IPP_TAG_SYSTEM, IPP_TAG_INTEGER, "system-default-printer-id", system->default_printer_id); _papplSystemExportVersions(system, client->response, IPP_TAG_SYSTEM, ra); if (!ra || cupsArrayFind(ra, "system-geo-location")) { if (system->geo_location) ippAddString(client->response, IPP_TAG_SYSTEM, IPP_TAG_URI, "system-geo-location", NULL, system->geo_location); else ippAddOutOfBand(client->response, IPP_TAG_SYSTEM, IPP_TAG_UNKNOWN, "system-geo-location"); } if (!ra || cupsArrayFind(ra, "system-location")) ippAddString(client->response, IPP_TAG_SYSTEM, IPP_TAG_TEXT, "system-location", NULL, system->location ? system->location : ""); if (!ra || cupsArrayFind(ra, "system-name")) ippAddString(client->response, IPP_TAG_SYSTEM, IPP_TAG_NAME, "system-name", NULL, system->name); if (!ra || cupsArrayFind(ra, "system-organization")) ippAddString(client->response, IPP_TAG_SYSTEM, IPP_TAG_TEXT, "system-organization", NULL, system->organization ? system->organization : ""); if (!ra || cupsArrayFind(ra, "system-organizational-unit")) ippAddString(client->response, IPP_TAG_SYSTEM, IPP_TAG_TEXT, "system-organizational-unit", NULL, system->org_unit ? system->org_unit : ""); if (!ra || cupsArrayFind(ra, "system-state")) { int state = IPP_PSTATE_IDLE; // System state for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); if (printer->state == IPP_PSTATE_PROCESSING) { state = IPP_PSTATE_PROCESSING; break; } } ippAddInteger(client->response, IPP_TAG_SYSTEM, IPP_TAG_ENUM, "system-state", state); } if (!ra || cupsArrayFind(ra, "system-state-change-date-time") || cupsArrayFind(ra, "system-state-change-time")) { for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); if (state_time < printer->state_time) state_time = printer->state_time; } if (!ra || cupsArrayFind(ra, "system-state-change-date-time")) ippAddDate(client->response, IPP_TAG_SYSTEM, "system-state-change-date-time", ippTimeToDate(state_time)); if (!ra || cupsArrayFind(ra, "system-state-change-time")) ippAddInteger(client->response, IPP_TAG_SYSTEM, IPP_TAG_INTEGER, "system-state-change-time", (int)(state_time - system->start_time)); } if (!ra || cupsArrayFind(ra, "system-state-reasons")) { pappl_preason_t state_reasons = PAPPL_PREASON_NONE; for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); state_reasons |= printer->state_reasons; } if (state_reasons == PAPPL_PREASON_NONE) { ippAddString(client->response, IPP_TAG_SYSTEM, IPP_CONST_TAG(IPP_TAG_KEYWORD), "system-state-reasons", NULL, "none"); } else { pappl_preason_t bit; // Reason bit for (attr = NULL, bit = PAPPL_PREASON_OTHER; bit <= PAPPL_PREASON_TONER_LOW; bit *= 2) { if (state_reasons & bit) { if (attr) ippSetString(client->response, &attr, ippGetCount(attr), _papplPrinterReasonString(bit)); else attr = ippAddString(client->response, IPP_TAG_SYSTEM, IPP_TAG_KEYWORD, "system-state-reasons", NULL, _papplPrinterReasonString(bit)); } } } } if (!ra || cupsArrayFind(ra, "system-up-time")) ippAddInteger(client->response, IPP_TAG_SYSTEM, IPP_TAG_INTEGER, "system-up-time", (int)(time(NULL) - system->start_time)); if (system->uuid && (!ra || cupsArrayFind(ra, "system-uuid"))) ippAddString(client->response, IPP_TAG_SYSTEM, IPP_TAG_URI, "system-uuid", NULL, system->uuid); if (!ra || cupsArrayFind(ra, "system-xri-supported")) { char uri[1024]; // URI value httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipps", NULL, client->host_field, client->host_port, "/ipp/system"); col = ippNew(); ippAddString(col, IPP_TAG_SYSTEM, IPP_CONST_TAG(IPP_TAG_KEYWORD), "xri-authentication", NULL, client->system->auth_service ? "basic" : "none"); ippAddString(col, IPP_TAG_SYSTEM, IPP_CONST_TAG(IPP_TAG_KEYWORD), "xri-security", NULL, "tls"); ippAddString(col, IPP_TAG_SYSTEM, IPP_TAG_URI, "xri-uri", NULL, uri); ippAddCollection(client->response, IPP_TAG_SYSTEM, "system-xri-supported", col); ippDelete(col); } pthread_rwlock_unlock(&system->rwlock); cupsArrayDelete(ra); } // // 'ipp_set_system_attributes()' - Set system attributes. // static void ipp_set_system_attributes( pappl_client_t *client) // I - Client { pappl_system_t *system = client->system; // System ipp_attribute_t *rattr; // Current request attribute ipp_tag_t value_tag; // Value tag int count; // Number of values const char *name; // Attribute name int i; // Looping var http_status_t auth_status; // Authorization status static _pappl_attr_t sattrs[] = // Settable system attributes { { "system-contact-col", IPP_TAG_BEGIN_COLLECTION, 1 }, { "system-default-printer-id", IPP_TAG_INTEGER, 1 }, { "system-geo-location", IPP_TAG_URI, 1 }, { "system-location", IPP_TAG_TEXT, 1 }, { "system-organization", IPP_TAG_TEXT, 1 }, { "system-organizational-unit", IPP_TAG_TEXT, 1 } }; // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } // Preflight request attributes... for (rattr = ippFirstAttribute(client->request); rattr; rattr = ippNextAttribute(client->request)) { papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "%s %s %s%s ...", ippTagString(ippGetGroupTag(rattr)), ippGetName(rattr), ippGetCount(rattr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(rattr))); if (ippGetGroupTag(rattr) == IPP_TAG_OPERATION) { continue; } else if (ippGetGroupTag(rattr) != IPP_TAG_SYSTEM) { papplClientRespondIPPUnsupported(client, rattr); continue; } name = ippGetName(rattr); value_tag = ippGetValueTag(rattr); count = ippGetCount(rattr); for (i = 0; i < (int)(sizeof(sattrs) / sizeof(sattrs[0])); i ++) { if (!strcmp(name, sattrs[i].name) && value_tag == sattrs[i].value_tag && count <= sattrs[i].max_count) break; } if (i >= (int)(sizeof(sattrs) / sizeof(sattrs[0]))) papplClientRespondIPPUnsupported(client, rattr); if (!strcmp(name, "system-default-printer-id")) { if (!papplSystemFindPrinter(system, NULL, ippGetInteger(rattr, 0), NULL)) { papplClientRespondIPPUnsupported(client, rattr); break; } } } if (ippGetStatusCode(client->response) != IPP_STATUS_OK) return; // Now apply changes... pthread_rwlock_wrlock(&system->rwlock); for (rattr = ippFirstAttribute(client->request); rattr; rattr = ippNextAttribute(client->request)) { if (ippGetGroupTag(rattr) == IPP_TAG_OPERATION) continue; name = ippGetName(rattr); if (!strcmp(name, "system-contact-col")) { _papplContactImport(ippGetCollection(rattr, 0), &system->contact); } else if (!strcmp(name, "system-default-printer-id")) { // Value was checked previously... system->default_printer_id = ippGetInteger(rattr, 0); } else if (!strcmp(name, "system-geo-location")) { free(system->geo_location); system->geo_location = strdup(ippGetString(rattr, 0, NULL)); } else if (!strcmp(name, "system-location")) { free(system->location); system->location = strdup(ippGetString(rattr, 0, NULL)); } else if (!strcmp(name, "system-organization")) { free(system->organization); system->organization = strdup(ippGetString(rattr, 0, NULL)); } else if (!strcmp(name, "system-organization-unit")) { free(system->org_unit); system->org_unit = strdup(ippGetString(rattr, 0, NULL)); } } system->config_changes ++; pthread_rwlock_unlock(&system->rwlock); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); } // // 'ipp_shutdown_all_printers()' - Shutdown the system. // static void ipp_shutdown_all_printers( pappl_client_t *client) // I - Client { http_status_t auth_status; // Authorization status // Verify the connection is authorized... if ((auth_status = papplClientIsAuthorized(client)) != HTTP_STATUS_CONTINUE) { papplClientRespond(client, auth_status, NULL, NULL, 0, 0); return; } client->system->shutdown_time = time(NULL); papplClientRespondIPP(client, IPP_STATUS_OK, NULL); } pappl-1.0.3/pappl/system-loadsave.c000066400000000000000000000761351403603036100172370ustar00rootroot00000000000000// // System load/save functions for the Printer Application Framework // // Copyright © 2020-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local functions... // static void parse_contact(char *value, pappl_contact_t *contact); static void parse_media_col(char *value, pappl_media_col_t *media); static char *read_line(cups_file_t *fp, char *line, size_t linesize, char **value, int *linenum); static void write_contact(cups_file_t *fp, pappl_contact_t *contact); static void write_media_col(cups_file_t *fp, const char *name, pappl_media_col_t *media); static void write_options(cups_file_t *fp, const char *name, int num_options, cups_option_t *options); // // 'papplSystemLoadState()' - Load the previous system state. // // This function loads the previous system state from a file created by the // @link papplSystemSaveState@ function. The system state contains all of the // system object values, the list of printers, and the jobs for each printer. // // When loading a printer definition, if the printer cannot be created (e.g., // because the driver name is no longer valid) then that printer and all of its // job history will be lost. In the case of a bad driver name, a printer // application's driver callback can perform any necessary mapping of the driver // name, including the use its auto-add callback to find a compatible new // driver. // // > Note: This function must be called prior to @link papplSystemRun@. // bool // O - `true` on success, `false` on failure papplSystemLoadState( pappl_system_t *system, // I - System const char *filename) // I - File to load { int i; // Looping var cups_file_t *fp; // Output file int linenum; // Line number char line[2048], // Line from file *ptr, // Pointer into line/value *value; // Value from line // Range check input... if (!system || !filename) { return (false); } else if (system->is_running) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Cannot load system state while running."); return (false); } // Open the state file... if ((fp = cupsFileOpen(filename, "r")) == NULL) { if (errno != ENOENT) papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to open system state file '%s': %s", filename, cupsLastErrorString()); return (false); } // Read lines from the state file... papplLog(system, PAPPL_LOGLEVEL_INFO, "Loading system state from '%s'.", filename); linenum = 0; while (read_line(fp, line, sizeof(line), &value, &linenum)) { if (!strcasecmp(line, "DNSSDName")) papplSystemSetDNSSDName(system, value); else if (!strcasecmp(line, "Location")) papplSystemSetLocation(system, value); else if (!strcasecmp(line, "GeoLocation")) papplSystemSetGeoLocation(system, value); else if (!strcasecmp(line, "Organization")) papplSystemSetOrganization(system, value); else if (!strcasecmp(line, "OrganizationalUnit")) papplSystemSetOrganizationalUnit(system, value); else if (!strcasecmp(line, "Contact")) { pappl_contact_t contact; // "system-contact" value parse_contact(value, &contact); papplSystemSetContact(system, &contact); } else if (!strcasecmp(line, "AdminGroup")) papplSystemSetAdminGroup(system, value); else if (!strcasecmp(line, "DefaultPrintGroup")) papplSystemSetDefaultPrintGroup(system, value); else if (!strcasecmp(line, "Password")) papplSystemSetPassword(system, value); else if (!strcasecmp(line, "DefaultPrinterID") && value) papplSystemSetDefaultPrinterID(system, (int)strtol(value, NULL, 10)); else if (!strcasecmp(line, "NextPrinterID") && value) papplSystemSetNextPrinterID(system, (int)strtol(value, NULL, 10)); else if (!strcasecmp(line, "UUID") && value) { if ((system->uuid = strdup(value)) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for system UUID."); return (false); } } else if (!strcasecmp(line, "")) break; else if (!printer) continue; else if (!strcasecmp(line, "DNSSDName")) papplPrinterSetDNSSDName(printer, value); else if (!strcasecmp(line, "Location")) papplPrinterSetLocation(printer, value); else if (!strcasecmp(line, "GeoLocation")) papplPrinterSetGeoLocation(printer, value); else if (!strcasecmp(line, "Organization")) papplPrinterSetOrganization(printer, value); else if (!strcasecmp(line, "OrganizationalUnit")) papplPrinterSetOrganizationalUnit(printer, value); else if (!strcasecmp(line, "Contact")) { pappl_contact_t contact;// "printer-contact" value parse_contact(value, &contact); papplPrinterSetContact(printer, &contact); } else if (!strcasecmp(line, "PrintGroup")) papplPrinterSetPrintGroup(printer, value); else if (!strcasecmp(line, "MaxActiveJobs") && value) papplPrinterSetMaxActiveJobs(printer, (int)strtol(value, NULL, 10)); else if (!strcasecmp(line, "MaxCompletedJobs") && value) papplPrinterSetMaxCompletedJobs(printer, (int)strtol(value, NULL, 10)); else if (!strcasecmp(line, "NextJobId") && value) papplPrinterSetNextJobID(printer, (int)strtol(value, NULL, 10)); else if (!strcasecmp(line, "ImpressionsCompleted") && value) papplPrinterSetImpressionsCompleted(printer, (int)strtol(value, NULL, 10)); else if (!strcasecmp(line, "identify-actions-default")) printer->driver_data.identify_default = _papplIdentifyActionsValue(value); else if (!strcasecmp(line, "label-mode-configured")) printer->driver_data.mode_configured = _papplLabelModeValue(value); else if (!strcasecmp(line, "label-tear-offset-configured") && value) printer->driver_data.tear_offset_configured = (int)strtol(value, NULL, 10); else if (!strcasecmp(line, "media-col-default")) parse_media_col(value, &printer->driver_data.media_default); else if (!strncasecmp(line, "media-col-ready", 15)) { if ((i = (int)strtol(line + 15, NULL, 10)) >= 0 && i < PAPPL_MAX_SOURCE) parse_media_col(value, printer->driver_data.media_ready + i); } else if (!strcasecmp(line, "orientation-requested-default")) printer->driver_data.orient_default = (ipp_orient_t)ippEnumValue("orientation-requested", value); else if (!strcasecmp(line, "output-bin-default") && value) { for (i = 0; i < printer->driver_data.num_bin; i ++) { if (!strcmp(value, printer->driver_data.bin[i])) { printer->driver_data.bin_default = i; break; } } } else if (!strcasecmp(line, "print-color-mode-default")) printer->driver_data.color_default = _papplColorModeValue(value); else if (!strcasecmp(line, "print-content-optimize-default")) printer->driver_data.content_default = _papplContentValue(value); else if (!strcasecmp(line, "print-darkness-default") && value) printer->driver_data.darkness_default = (int)strtol(value, NULL, 10); else if (!strcasecmp(line, "print-quality-default")) printer->driver_data.quality_default = (ipp_quality_t)ippEnumValue("print-quality", value); else if (!strcasecmp(line, "print-scaling-default")) printer->driver_data.scaling_default = _papplScalingValue(value); else if (!strcasecmp(line, "print-speed-default") && value) printer->driver_data.speed_default = (int)strtol(value, NULL, 10); else if (!strcasecmp(line, "printer-darkness-configured") && value) printer->driver_data.darkness_configured = (int)strtol(value, NULL, 10); else if (!strcasecmp(line, "printer-resolution-default") && value) sscanf(value, "%dx%ddpi", &printer->driver_data.x_default, &printer->driver_data.y_default); else if (!strcasecmp(line, "sides-default")) printer->driver_data.sides_default = _papplSidesValue(value); else if ((ptr = strstr(line, "-default")) != NULL) { char defname[128], // xxx-default name supname[128]; // xxx-supported name ipp_attribute_t *attr; // Attribute *ptr = '\0'; snprintf(defname, sizeof(defname), "%s-default", line); snprintf(supname, sizeof(supname), "%s-supported", line); if (!value) value = ptr; ippDeleteAttribute(printer->driver_attrs, ippFindAttribute(printer->driver_attrs, defname, IPP_TAG_ZERO)); if ((attr = ippFindAttribute(printer->driver_attrs, supname, IPP_TAG_ZERO)) != NULL) { switch (ippGetValueTag(attr)) { case IPP_TAG_BOOLEAN : ippAddBoolean(printer->driver_attrs, IPP_TAG_PRINTER, defname, !strcmp(value, "true")); break; case IPP_TAG_INTEGER : case IPP_TAG_RANGE : ippAddInteger(printer->driver_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, defname, (int)strtol(value, NULL, 10)); break; case IPP_TAG_KEYWORD : ippAddString(printer->driver_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, defname, NULL, value); break; default : break; } } else { ippAddString(printer->driver_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, defname, NULL, value); } } else if (!strcasecmp(line, "Job") && value) { // Read printer job pappl_job_t *job; // Current Job struct stat jobbuf; // Job file buffer const char *job_name, // Job name *job_id, // Job ID *job_username, // Job username *job_format, // Job format *job_value; // Job option value num_options = cupsParseOptions(value, 0, &options); if ((job_id = cupsGetOption("id", num_options, options)) == NULL || strtol(job_id, NULL, 10) <= 0 || (job_name = cupsGetOption("name", num_options, options)) == NULL || (job_username = cupsGetOption("username", num_options, options)) == NULL || (job_format = cupsGetOption("format", num_options, options)) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Bad Job definition on line %d of '%s'.", linenum, filename); break; } if ((job = _papplJobCreate(printer, (int)strtol(job_id, NULL, 10), job_username, job_format, job_name, NULL)) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Error creating job %s for printer %s", job_name, printer->name); break; } if ((job_value = cupsGetOption("filename", num_options, options)) != NULL) { if ((job->filename = strdup(job_value)) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Error creating job %s for printer %s", job_name, printer->name); break; } } if ((job_value = cupsGetOption("state", num_options, options)) != NULL) job->state = (ipp_jstate_t)strtol(job_value, NULL, 10); if ((job_value = cupsGetOption("state_reasons", num_options, options)) != NULL) job->state_reasons = (ipp_jstate_t)strtol(job_value, NULL, 10); if ((job_value = cupsGetOption("created", num_options, options)) != NULL) job->created = strtol(job_value, NULL, 10); if ((job_value = cupsGetOption("processing", num_options, options)) != NULL) job->processing = strtol(job_value, NULL, 10); if ((job_value = cupsGetOption("completed", num_options, options)) != NULL) job->completed = strtol(job_value, NULL, 10); if ((job_value = cupsGetOption("impressions", num_options, options)) != NULL) job->impressions = (int)strtol(job_value, NULL, 10); if ((job_value = cupsGetOption("imcompleted", num_options, options)) != NULL) job->impcompleted = (int)strtol(job_value, NULL, 10); // Add the job to printer completed jobs array... if (job->state < IPP_JSTATE_STOPPED) { // Load the file attributes from the spool directory... int attr_fd; // Attribute file descriptor char job_attr_filename[256]; // Attribute filename if ((attr_fd = papplJobOpenFile(job, job_attr_filename, sizeof(job_attr_filename), system->directory, "ipp", "r")) < 0) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to open file for job attributes: '%s'.", job_attr_filename); continue; } ippReadFile(attr_fd, job->attrs); close(attr_fd); if (!job->filename || stat(job->filename, &jobbuf)) { // If file removed, then set job state to aborted... job->state = IPP_JSTATE_ABORTED; } else { // Add the job to printer active jobs array... cupsArrayAdd(printer->active_jobs, job); } } else { // Add job to printer completed jobs... cupsArrayAdd(printer->completed_jobs, job); } } else papplLog(system, PAPPL_LOGLEVEL_WARN, "Unknown printer directive '%s' on line %d of '%s'.", line, linenum, filename); } // Loaded all printer attributes, call the status callback (if any) to // update the current printer state... if (printer && printer->driver_data.status_cb) (printer->driver_data.status_cb)(printer); } else { papplLog(system, PAPPL_LOGLEVEL_WARN, "Unknown directive '%s' on line %d of '%s'.", line, linenum, filename); } } cupsFileClose(fp); return (true); } // // 'papplSystemSaveState()' - Save the current system state. // // This function saves the current system state to a file. It is typically // used with the @link papplSystemSetSaveCallback@ function to periodically // save the state: // // ``` // |papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState, // | (void *)filename); // ``` // bool // O - `true` on success, `false` on failure papplSystemSaveState( pappl_system_t *system, // I - System const char *filename) // I - File to save { int i, j, // Looping vars count; // Number of printers cups_file_t *fp; // Output file pappl_printer_t *printer; // Current printer pappl_job_t *job; // Current Job if ((fp = cupsFileOpen(filename, "w")) == NULL) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create system state file '%s': %s", filename, cupsLastErrorString()); return (false); } papplLog(system, PAPPL_LOGLEVEL_INFO, "Saving system state to '%s'.", filename); pthread_rwlock_rdlock(&system->rwlock); if (system->dns_sd_name) cupsFilePutConf(fp, "DNSSDName", system->dns_sd_name); if (system->location) cupsFilePutConf(fp, "Location", system->location); if (system->geo_location) cupsFilePutConf(fp, "Geolocation", system->geo_location); if (system->organization) cupsFilePutConf(fp, "Organization", system->organization); if (system->org_unit) cupsFilePutConf(fp, "OrganizationalUnit", system->org_unit); write_contact(fp, &system->contact); if (system->admin_group) cupsFilePutConf(fp, "AdminGroup", system->admin_group); if (system->default_print_group) cupsFilePutConf(fp, "DefaultPrintGroup", system->default_print_group); if (system->password_hash[0]) cupsFilePutConf(fp, "Password", system->password_hash); cupsFilePrintf(fp, "DefaultPrinterID %d\n", system->default_printer_id); cupsFilePrintf(fp, "NextPrinterID %d\n", system->next_printer_id); cupsFilePutConf(fp, "UUID", system->uuid); // Loop through the printers. // // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the printers array. for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { int jcount; // Number of jobs int num_options = 0;// Number of options cups_option_t *options = NULL;// Options printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); if (printer->is_deleted) continue; pthread_rwlock_rdlock(&printer->rwlock); num_options = cupsAddIntegerOption("id", printer->printer_id, num_options, &options); num_options = cupsAddOption("name", printer->name, num_options, &options); num_options = cupsAddOption("did", printer->device_id ? printer->device_id : "", num_options, &options); num_options = cupsAddOption("uri", printer->device_uri, num_options, &options); num_options = cupsAddOption("driver", printer->driver_name, num_options, &options); write_options(fp, "dns_sd_name) cupsFilePutConf(fp, "DNSSDName", printer->dns_sd_name); if (printer->location) cupsFilePutConf(fp, "Location", printer->location); if (printer->geo_location) cupsFilePutConf(fp, "Geolocation", printer->geo_location); if (printer->organization) cupsFilePutConf(fp, "Organization", printer->organization); if (printer->org_unit) cupsFilePutConf(fp, "OrganizationalUnit", printer->org_unit); write_contact(fp, &printer->contact); if (printer->print_group) cupsFilePutConf(fp, "PrintGroup", printer->print_group); cupsFilePrintf(fp, "MaxActiveJobs %d\n", printer->max_active_jobs); cupsFilePrintf(fp, "MaxCompletedJobs %d\n", printer->max_completed_jobs); cupsFilePrintf(fp, "NextJobId %d\n", printer->next_job_id); cupsFilePrintf(fp, "ImpressionsCompleted %d\n", printer->impcompleted); if (printer->driver_data.identify_default) cupsFilePutConf(fp, "identify-actions-default", _papplIdentifyActionsString(printer->driver_data.identify_default)); if (printer->driver_data.mode_configured) cupsFilePutConf(fp, "label-mode-configured", _papplLabelModeString(printer->driver_data.mode_configured)); if (printer->driver_data.tear_offset_configured) cupsFilePrintf(fp, "label-tear-offset-configured %d\n", printer->driver_data.tear_offset_configured); write_media_col(fp, "media-col-default", &printer->driver_data.media_default); for (j = 0; j < printer->driver_data.num_source; j ++) { if (printer->driver_data.media_ready[j].size_name[0]) { char name[128]; // Attribute name snprintf(name, sizeof(name), "media-col-ready%d", j); write_media_col(fp, name, printer->driver_data.media_ready + j); } } if (printer->driver_data.orient_default) cupsFilePutConf(fp, "orientation-requested-default", ippEnumString("orientation-requested", (int)printer->driver_data.orient_default)); if (printer->driver_data.bin_default && printer->driver_data.num_bin > 0) cupsFilePutConf(fp, "output-bin-default", printer->driver_data.bin[printer->driver_data.bin_default]); if (printer->driver_data.color_default) cupsFilePutConf(fp, "print-color-mode-default", _papplColorModeString(printer->driver_data.color_default)); if (printer->driver_data.content_default) cupsFilePutConf(fp, "print-content-optimize-default", _papplContentString(printer->driver_data.content_default)); if (printer->driver_data.darkness_default) cupsFilePrintf(fp, "print-darkness-default %d\n", printer->driver_data.darkness_default); if (printer->driver_data.quality_default) cupsFilePutConf(fp, "print-quality-default", ippEnumString("print-quality", (int)printer->driver_data.quality_default)); if (printer->driver_data.scaling_default) cupsFilePutConf(fp, "print-scaling-default", _papplScalingString(printer->driver_data.scaling_default)); if (printer->driver_data.darkness_configured) cupsFilePrintf(fp, "printer-darkness-configured %d\n", printer->driver_data.darkness_configured); if (printer->driver_data.sides_default) cupsFilePutConf(fp, "sides-default", _papplSidesString(printer->driver_data.sides_default)); if (printer->driver_data.x_default) cupsFilePrintf(fp, "printer-resolution-default %dx%ddpi\n", printer->driver_data.x_default, printer->driver_data.y_default); for (j = 0; j < printer->driver_data.num_vendor; j ++) { char defname[128], // xxx-default name defvalue[1024]; // xxx-default value snprintf(defname, sizeof(defname), "%s-default", printer->driver_data.vendor[j]); ippAttributeString(ippFindAttribute(printer->driver_attrs, defname, IPP_TAG_ZERO), defvalue, sizeof(defvalue)); cupsFilePutConf(fp, defname, defvalue); } // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the all_jobs array. for (j = 0, jcount = cupsArrayCount(printer->all_jobs); j < jcount; j ++) { job = (pappl_job_t *)cupsArrayIndex(printer->all_jobs, j); // Add basic job attributes... num_options = 0; num_options = cupsAddIntegerOption("id", job->job_id, num_options, &options); num_options = cupsAddOption("name", job->name, num_options, &options); num_options = cupsAddOption("username", job->username, num_options, &options); num_options = cupsAddOption("format", job->format, num_options, &options); if (job->filename) num_options = cupsAddOption("filename", job->filename, num_options, &options); if (job->state) num_options = cupsAddIntegerOption("state", (int)job->state, num_options, &options); if (job->state_reasons) num_options = cupsAddIntegerOption("state_reasons", (int)job->state_reasons, num_options, &options); if (job->created) num_options = cupsAddIntegerOption("created", (int)job->created, num_options, &options); if (job->processing) num_options = cupsAddIntegerOption("processing", (int)job->processing, num_options, &options); if (job->completed) num_options = cupsAddIntegerOption("completed", (int)job->completed, num_options, &options); if (job->impressions) num_options = cupsAddIntegerOption("impressions", job->impressions, num_options, &options); if (job->impcompleted) num_options = cupsAddIntegerOption("imcompleted", job->impcompleted, num_options, &options); if (job->attrs) { int attr_fd; // Attribute file descriptor char job_attr_filename[1024];// Attribute filename // Save job attributes to file in spool directory... if (job->state < IPP_JSTATE_STOPPED) { if ((attr_fd = papplJobOpenFile(job, job_attr_filename, sizeof(job_attr_filename), system->directory, "ipp", "w")) < 0) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create file for job attributes: '%s'.", job_attr_filename); continue; } ippWriteFile(attr_fd, job->attrs); close(attr_fd); } else { // If job completed or aborted, remove job-attributes file... papplJobOpenFile(job, job_attr_filename, sizeof(job_attr_filename), system->directory, "ipp", "x"); } } write_options(fp, "Job", num_options, options); cupsFreeOptions(num_options, options); } cupsFilePuts(fp, "\n"); pthread_rwlock_unlock(&printer->rwlock); } pthread_rwlock_unlock(&system->rwlock); cupsFileClose(fp); return (true); } // // 'parse_contact()' - Parse a contact value. // static void parse_contact(char *value, // I - Value pappl_contact_t *contact) // O - Contact { int i, // Looping var num_options; // Number of options cups_option_t *options = NULL, // Options *option; // Current option memset(contact, 0, sizeof(pappl_contact_t)); num_options = cupsParseOptions(value, 0, &options); for (i = num_options, option = options; i > 0; i --, option ++) { if (!strcasecmp(option->name, "name")) strlcpy(contact->name, option->value, sizeof(contact->name)); else if (!strcasecmp(option->name, "email")) strlcpy(contact->email, option->value, sizeof(contact->email)); else if (!strcasecmp(option->name, "telephone")) strlcpy(contact->telephone, option->value, sizeof(contact->telephone)); } cupsFreeOptions(num_options, options); } // // 'parse_media_col()' - Parse a media-col value. // static void parse_media_col( char *value, // I - Value pappl_media_col_t *media) // O - Media collection { int i, // Looping var num_options; // Number of options cups_option_t *options = NULL, // Options *option; // Current option memset(media, 0, sizeof(pappl_media_col_t)); num_options = cupsParseOptions(value, 0, &options); for (i = num_options, option = options; i > 0; i --, option ++) { if (!strcasecmp(option->name, "bottom")) media->bottom_margin = (int)strtol(option->value, NULL, 10); else if (!strcasecmp(option->name, "left")) media->left_margin = (int)strtol(option->value, NULL, 10); else if (!strcasecmp(option->name, "right")) media->right_margin = (int)strtol(option->value, NULL, 10); else if (!strcasecmp(option->name, "name")) strlcpy(media->size_name, option->value, sizeof(media->size_name)); else if (!strcasecmp(option->name, "width")) media->size_width = (int)strtol(option->value, NULL, 10); else if (!strcasecmp(option->name, "length")) media->size_length = (int)strtol(option->value, NULL, 10); else if (!strcasecmp(option->name, "source")) strlcpy(media->source, option->value, sizeof(media->source)); else if (!strcasecmp(option->name, "top")) media->top_margin = (int)strtol(option->value, NULL, 10); else if (!strcasecmp(option->name, "offset")) media->top_offset = (int)strtol(option->value, NULL, 10); else if (!strcasecmp(option->name, "tracking")) media->tracking = _papplMediaTrackingValue(option->value); else if (!strcasecmp(option->name, "type")) strlcpy(media->type, option->value, sizeof(media->type)); } cupsFreeOptions(num_options, options); } // // 'read_line()' - Read a line from the state file. // // This function is like `cupsFileGetConf`, except that it doesn't support // comments since the state files are not meant to be edited or maintained by // humans. // static char * // O - Line or `NULL` on EOF read_line(cups_file_t *fp, // I - File char *line, // I - Line buffer size_t linesize, // I - Size of line buffer char **value, // O - Value portion of line int *linenum) // IO - Current line number { char *ptr; // Pointer into line // Try reading a line from the file... *value = NULL; if (!cupsFileGets(fp, line, linesize)) return (NULL); // Got it, bump the line number... (*linenum) ++; // If we have "something value" then split at the whitespace... if ((ptr = strchr(line, ' ')) != NULL) { *ptr++ = '\0'; *value = ptr; } // Strip the trailing ">" for "" if (line[0] == '<' && *value && (ptr = *value + strlen(*value) - 1) >= *value && *ptr == '>') *ptr = '\0'; return (line); } // // 'write_contact()' - Write an "xxx-contact" value. // static void write_contact(cups_file_t *fp, // I - File pappl_contact_t *contact) // I - Contact { int num_options = 0; // Number of options cups_option_t *options = NULL; // Options if (contact->name[0]) num_options = cupsAddOption("name", contact->name, num_options, &options); if (contact->email[0]) num_options = cupsAddOption("email", contact->email, num_options, &options); if (contact->telephone[0]) num_options = cupsAddOption("telephone", contact->telephone, num_options, &options); write_options(fp, "Contact", num_options, options); cupsFreeOptions(num_options, options); } // // 'write_media_col()' - Write a media-col value... // static void write_media_col( cups_file_t *fp, // I - File const char *name, // I - Attribute name pappl_media_col_t *media) // I - Media value { int num_options = 0; // Number of options cups_option_t *options = NULL; // Options if (media->bottom_margin) num_options = cupsAddIntegerOption("bottom", media->bottom_margin, num_options, &options); if (media->left_margin) num_options = cupsAddIntegerOption("left", media->left_margin, num_options, &options); if (media->right_margin) num_options = cupsAddIntegerOption("right", media->right_margin, num_options, &options); if (media->size_name[0]) num_options = cupsAddOption("name", media->size_name, num_options, &options); if (media->size_width) num_options = cupsAddIntegerOption("width", media->size_width, num_options, &options); if (media->size_length) num_options = cupsAddIntegerOption("length", media->size_length, num_options, &options); if (media->source[0]) num_options = cupsAddOption("source", media->source, num_options, &options); if (media->top_margin) num_options = cupsAddIntegerOption("top", media->top_margin, num_options, &options); if (media->top_offset) num_options = cupsAddIntegerOption("offset", media->top_offset, num_options, &options); if (media->tracking) num_options = cupsAddOption("tracking", _papplMediaTrackingString(media->tracking), num_options, &options); if (media->type[0]) num_options = cupsAddOption("type", media->type, num_options, &options); write_options(fp, name, num_options, options); cupsFreeOptions(num_options, options); } // // 'write_options()' - Write a CUPS options array value... // static void write_options(cups_file_t *fp, // I - File const char *name, // I - Attribute name int num_options,// I - Number of options cups_option_t *options) // I - Options { const char *start, // Start of current subset *ptr; // Pointer into value cupsFilePuts(fp, name); while (num_options > 0) { cupsFilePrintf(fp, " %s=\"", options->name); for (start = options->value, ptr = start; *ptr; ptr ++) { if (*ptr == '\\' || *ptr == '\"') { if (ptr > start) cupsFileWrite(fp, start, (size_t)(ptr - start)); cupsFilePutChar(fp, '\\'); start = ptr; } } if (ptr > start) cupsFileWrite(fp, start, (size_t)(ptr - start)); cupsFilePutChar(fp, '\"'); num_options --; options ++; } if (*name == '<') cupsFilePuts(fp, ">\n"); else cupsFilePutChar(fp, '\n'); } pappl-1.0.3/pappl/system-printer.c000066400000000000000000000076241403603036100171210ustar00rootroot00000000000000// // Printer object for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" // // Local functions... // static int compare_printers(pappl_printer_t *a, pappl_printer_t *b); // // '_papplSystemAddPrinter()' - Add a printer to the system object, creating the printers array as needed. // void _papplSystemAddPrinter( pappl_system_t *system, // I - System pappl_printer_t *printer, // I - Printer int printer_id) // I - Printer ID or `0` for new { // Add the printer to the system... pthread_rwlock_wrlock(&system->rwlock); if (printer_id) printer->printer_id = printer_id; else printer->printer_id = system->next_printer_id ++; if (!system->printers) system->printers = cupsArrayNew3((cups_array_func_t)compare_printers, NULL, NULL, 0, NULL, (cups_afree_func_t)_papplPrinterDelete); cupsArrayAdd(system->printers, printer); if (!system->default_printer_id) system->default_printer_id = printer->printer_id; pthread_rwlock_unlock(&system->rwlock); _papplSystemConfigChanged(system); } // // 'papplSystemFindPrinter()' - Find a printer by resource, ID, or device URI. // // This function finds a printer contained in the system using its resource // path, unique integer identifier, or device URI. If none of these is // specified, the current default printer is returned. // pappl_printer_t * // O - Printer or `NULL` if none papplSystemFindPrinter( pappl_system_t *system, // I - System const char *resource, // I - Resource path or `NULL` int printer_id, // I - Printer ID or `0` const char *device_uri) // I - Device URI or `NULL` { int i, // Current printer index count; // Printer count pappl_printer_t *printer = NULL;// Matching printer // Range check input... if (!system) return (NULL); papplLog(system, PAPPL_LOGLEVEL_DEBUG, "papplSystemFindPrinter(system=%p, resource=\"%s\", printer_id=%d, device_uri=\"%s\")", (void *)system, resource, printer_id, device_uri); pthread_rwlock_rdlock(&system->rwlock); if (resource && (!strcmp(resource, "/") || !strcmp(resource, "/ipp/print") || (!strncmp(resource, "/ipp/print/", 11) && isdigit(resource[11] & 255)))) { printer_id = system->default_printer_id; resource = NULL; papplLog(system, PAPPL_LOGLEVEL_DEBUG, "papplSystemFindPrinter: Looking for default printer_id=%d", printer_id); } // Loop through the printers to find the one we want... // // Note: Cannot use cupsArrayFirst/Last since other threads might be // enumerating the printers array. for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); papplLog(system, PAPPL_LOGLEVEL_DEBUG, "papplSystemFindPrinter: printer '%s' - resource=\"%s\", printer_id=%d, device_uri=\"%s\"", printer->name, printer->resource, printer->printer_id, printer->device_uri); if (resource && !strncasecmp(printer->resource, resource, printer->resourcelen) && (!resource[printer->resourcelen] || resource[printer->resourcelen] == '/')) break; else if (printer->printer_id == printer_id) break; else if (device_uri && !strcmp(printer->device_uri, device_uri)) break; } if (i >= count) printer = NULL; pthread_rwlock_unlock(&system->rwlock); papplLog(system, PAPPL_LOGLEVEL_DEBUG, "papplSystemFindPrinter: Returning %p(%s)", printer, printer ? printer->name : "none"); return (printer); } // // 'compare_printers()' - Compare two printers. // static int // O - Result of comparison compare_printers(pappl_printer_t *a, // I - First printer pappl_printer_t *b) // I - Second printer { return (strcmp(a->name, b->name)); } pappl-1.0.3/pappl/system-private.h000066400000000000000000000163561403603036100171170ustar00rootroot00000000000000// // Private system header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_SYSTEM_PRIVATE_H_ # define _PAPPL_SYSTEM_PRIVATE_H_ // // Include necessary headers... // # include "dnssd-private.h" # include "system.h" # include // // Constants... // # define _PAPPL_MAX_LISTENERS 32 // Maximum number of listener sockets // // Types and structures... // typedef struct _pappl_mime_filter_s // MIME filter { const char *src, // Source MIME media type *dst; // Destination MIME media type pappl_mime_filter_cb_t cb; // Filter callback function void *cbdata; // Filter callback data } _pappl_mime_filter_t; typedef struct _pappl_resource_s // Resource { char *path, // Path *format, // Content type (MIME media type) *filename, // Filename *language; // Language (for strings) time_t last_modified; // Last-Modified date/time const void *data; // Static data size_t length; // Length of file/data pappl_resource_cb_t cb; // Dynamic callback void *cbdata; // Callback data } _pappl_resource_t; struct _pappl_system_s // System data { pthread_rwlock_t rwlock; // Reader/writer lock pappl_soptions_t options; // Server options bool is_running; // Is the system running? time_t start_time, // Startup time config_time, // Time of last config change clean_time, // Next clean time shutdown_time; // Shutdown requested? pthread_mutex_t config_mutex; // Mutex for configuration changes size_t config_changes, // Number of configuration changes save_changes; // Number of saved changes char *uuid, // "system-uuid" value *name, // "system-name" value *dns_sd_name, // "system-dns-sd-name" value *location, // "system-location" value *geo_location, // "system-geo-location" value *organization, // "system-organization" value *org_unit; // "system-organizational-unit" value pappl_contact_t contact; // "system-contact-col" value char *hostname; // Published hostname int port; // Port number, if any int num_versions; // Number of "xxx-firmware-yyy" values pappl_version_t versions[10]; // "xxx-firmware-yyy" values char *footer_html; // Footer HTML for web interface char *server_header; // Server: header value char *directory; // Spool directory char *logfile; // Log filename, if any int logfd; // Log file descriptor, if any pappl_loglevel_t loglevel; // Log level size_t logmaxsize; // Maximum log file size or `0` for none char *subtypes; // DNS-SD sub-types, if any bool tls_only; // Only support TLS? char *auth_service; // PAM authorization service, if any char *admin_group; // PAM administrative group, if any gid_t admin_gid; // PAM administrative group ID char *default_print_group; // Default PAM printing group, if any char session_key[65]; // Session key pthread_rwlock_t session_rwlock; // Reader/writer lock for the session key time_t session_time; // Session key time int num_listeners; // Number of listener sockets struct pollfd listeners[_PAPPL_MAX_LISTENERS]; // Listener sockets cups_array_t *links; // Web navigation links cups_array_t *resources; // Array of resources cups_array_t *filters; // Array of filters int next_client; // Next client number cups_array_t *printers; // Array of printers int default_printer_id, // Default printer-id next_printer_id; // Next printer-id char password_hash[100]; // Access password hash int num_drivers; // Number of printer drivers pappl_pr_driver_t *drivers; // Printer drivers pappl_pr_autoadd_cb_t autoadd_cb; // Printer driver auto-add callback pappl_pr_create_cb_t create_cb; // Printer driver creation callback pappl_pr_driver_cb_t driver_cb; // Printer driver initialization callback void *driver_cbdata; // Printer driver callback data ipp_t *attrs; // Static attributes for system pappl_mime_cb_t mime_cb; // MIME typing callback void *mime_cbdata; // MIME typing callback data pappl_ipp_op_cb_t op_cb; // IPP operation callback void *op_cbdata; // IPP operation callback data pappl_save_cb_t save_cb; // Save callback void *save_cbdata; // Save callback data # ifdef HAVE_DNSSD _pappl_srv_t dns_sd_ipps_ref, // DNS-SD IPPS service dns_sd_http_ref; // DNS-SD HTTP service DNSRecordRef dns_sd_loc_ref; // DNS-SD LOC record # else _pappl_srv_t dns_sd_ref; // DNS-SD services # endif // HAVE_DNSSD unsigned char dns_sd_loc[16]; // DNS-SD LOC record data bool dns_sd_any_collision; // Was there a name collision for any printer? bool dns_sd_collision; // Was there a name collision for this system? int dns_sd_serial; // DNS-SD serial number (for collisions) int dns_sd_host_changes; // Last count of DNS-SD host name changes }; // // Functions... // extern void _papplSystemAddPrinter(pappl_system_t *system, pappl_printer_t *printer, int printer_id) _PAPPL_PRIVATE; extern void _papplSystemAddPrinterIcons(pappl_system_t *system, pappl_printer_t *printer) _PAPPL_PRIVATE; extern void _papplSystemCleanJobs(pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemConfigChanged(pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemExportVersions(pappl_system_t *system, ipp_t *ipp, ipp_tag_t group_tag, cups_array_t *ra); extern _pappl_mime_filter_t *_papplSystemFindMIMEFilter(pappl_system_t *system, const char *srctype, const char *dsttype) _PAPPL_PRIVATE; extern _pappl_resource_t *_papplSystemFindResource(pappl_system_t *system, const char *path) _PAPPL_PRIVATE; extern char *_papplSystemMakeUUID(pappl_system_t *system, const char *printer_name, int job_id, char *buffer, size_t bufsize) _PAPPL_PRIVATE; extern void _papplSystemProcessIPP(pappl_client_t *client) _PAPPL_PRIVATE; extern bool _papplSystemRegisterDNSSDNoLock(pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemUnregisterDNSSDNoLock(pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebAddPrinter(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebConfig(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebConfigFinalize(pappl_system_t *system, int num_form, cups_option_t *form) _PAPPL_PRIVATE; extern void _papplSystemWebHome(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebLogFile(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebLogs(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebNetwork(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebSecurity(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebSettings(pappl_client_t *client) _PAPPL_PRIVATE; # ifdef HAVE_GNUTLS extern void _papplSystemWebTLSInstall(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; extern void _papplSystemWebTLSNew(pappl_client_t *client, pappl_system_t *system) _PAPPL_PRIVATE; # endif // HAVE_GNUTLS #endif // !_PAPPL_SYSTEM_PRIVATE_H_ pappl-1.0.3/pappl/system-webif.c000066400000000000000000002214671403603036100165350ustar00rootroot00000000000000// // System web interface functions for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" #include #include #ifdef HAVE_GNUTLS # include # include #endif // HAVE_GNUTLS // // Local types... // typedef struct _pappl_system_dev_s // System device callback data { pappl_client_t *client; // Client connection const char *device_uri; // Current device URI } _pappl_system_dev_t; // // Local functions... // static bool system_device_cb(const char *device_info, const char *device_uri, const char *device_id, void *data); static void system_footer(pappl_client_t *client); static void system_header(pappl_client_t *client, const char *title); #ifdef HAVE_GNUTLS static bool tls_install_certificate(pappl_client_t *client, const char *crtfile, const char *keyfile); static bool tls_install_file(pappl_client_t *client, const char *dst, const char *src); static bool tls_make_certificate(pappl_client_t *client, int num_form, cups_option_t *form); static bool tls_make_certsignreq(pappl_client_t *client, int num_form, cups_option_t *form, char *crqpath, size_t crqsize); #endif // HAVE_GNUTLS // // Local globals... // #ifdef HAVE_GNUTLS static const char * const countries[][2] = { // List of countries and their ISO 3166 2-letter codes { "AF", "Afghanistan" }, { "AX", "Åland Islands" }, { "AL", "Albania" }, { "DZ", "Algeria" }, { "AS", "American Samoa" }, { "AD", "Andorra" }, { "AO", "Angola" }, { "AI", "Anguilla" }, { "AQ", "Antarctica" }, { "AG", "Antigua and Barbuda" }, { "AR", "Argentina" }, { "AM", "Armenia" }, { "AW", "Aruba" }, { "AU", "Australia" }, { "AT", "Austria" }, { "AZ", "Azerbaijan" }, { "BS", "Bahamas" }, { "BH", "Bahrain" }, { "BD", "Bangladesh" }, { "BB", "Barbados" }, { "BY", "Belarus" }, { "BE", "Belgium" }, { "BZ", "Belize" }, { "BJ", "Benin" }, { "BM", "Bermuda" }, { "BT", "Bhutan" }, { "BO", "Bolivia (Plurinational State of)" }, { "BQ", "Bonaire, Sint Eustatius and Saba" }, { "BA", "Bosnia and Herzegovina" }, { "BW", "Botswana" }, { "BV", "Bouvet Island" }, { "BR", "Brazil" }, { "IO", "British Indian Ocean Territory" }, { "BN", "Brunei Darussalam" }, { "BG", "Bulgaria" }, { "BF", "Burkina Faso" }, { "BI", "Burundi" }, { "CV", "Cabo Verde" }, { "KH", "Cambodia" }, { "CM", "Cameroon" }, { "CA", "Canada" }, { "KY", "Cayman Islands" }, { "CF", "Central African Republic" }, { "TD", "Chad" }, { "CL", "Chile" }, { "CN", "China" }, { "CX", "Christmas Island" }, { "CC", "Cocos (Keeling) Islands" }, { "CO", "Colombia" }, { "KM", "Comoros" }, { "CD", "Congo, Democratic Republic of the" }, { "CG", "Congo" }, { "CK", "Cook Islands" }, { "CR", "Costa Rica" }, { "CI", "Côte d'Ivoire" }, { "HR", "Croatia" }, { "CU", "Cuba" }, { "CW", "Curaçao" }, { "CY", "Cyprus" }, { "CZ", "Czechia" }, { "DK", "Denmark" }, { "DJ", "Djibouti" }, { "DM", "Dominica" }, { "DO", "Dominican Republic" }, { "EC", "Ecuador" }, { "EG", "Egypt" }, { "SV", "El Salvador" }, { "GQ", "Equatorial Guinea" }, { "ER", "Eritrea" }, { "EE", "Estonia" }, { "SZ", "Eswatini" }, { "ET", "Ethiopia" }, { "FK", "Falkland Islands (Malvinas)" }, { "FO", "Faroe Islands" }, { "FJ", "Fiji" }, { "FI", "Finland" }, { "FR", "France" }, { "GF", "French Guiana" }, { "PF", "French Polynesia" }, { "TF", "French Southern Territories" }, { "GA", "Gabon" }, { "GM", "Gambia" }, { "GE", "Georgia" }, { "DE", "Germany" }, { "GH", "Ghana" }, { "GI", "Gibraltar" }, { "GR", "Greece" }, { "GL", "Greenland" }, { "GD", "Grenada" }, { "GP", "Guadeloupe" }, { "GU", "Guam" }, { "GT", "Guatemala" }, { "GG", "Guernsey" }, { "GW", "Guinea-Bissau" }, { "GN", "Guinea" }, { "GY", "Guyana" }, { "HT", "Haiti" }, { "HM", "Heard Island and McDonald Islands" }, { "VA", "Holy See" }, { "HN", "Honduras" }, { "HK", "Hong Kong" }, { "HU", "Hungary" }, { "IS", "Iceland" }, { "IN", "India" }, { "ID", "Indonesia" }, { "IR", "Iran (Islamic Republic of)" }, { "IQ", "Iraq" }, { "IE", "Ireland" }, { "IM", "Isle of Man" }, { "IL", "Israel" }, { "IT", "Italy" }, { "JM", "Jamaica" }, { "JP", "Japan" }, { "JE", "Jersey" }, { "JO", "Jordan" }, { "KZ", "Kazakhstan" }, { "KE", "Kenya" }, { "KI", "Kiribati" }, { "KP", "Korea (Democratic People's Republic of)" }, { "KR", "Korea, Republic of" }, { "KW", "Kuwait" }, { "KG", "Kyrgyzstan" }, { "LA", "Lao People's Democratic Republic" }, { "LV", "Latvia" }, { "LB", "Lebanon" }, { "LS", "Lesotho" }, { "LR", "Liberia" }, { "LY", "Libya" }, { "LI", "Liechtenstein" }, { "LT", "Lithuania" }, { "LU", "Luxembourg" }, { "MO", "Macao" }, { "MG", "Madagascar" }, { "MW", "Malawi" }, { "MY", "Malaysia" }, { "MV", "Maldives" }, { "ML", "Mali" }, { "MT", "Malta" }, { "MH", "Marshall Islands" }, { "MQ", "Martinique" }, { "MR", "Mauritania" }, { "MU", "Mauritius" }, { "YT", "Mayotte" }, { "MX", "Mexico" }, { "FM", "Micronesia (Federated States of)" }, { "MD", "Moldova, Republic of" }, { "MC", "Monaco" }, { "MN", "Mongolia" }, { "ME", "Montenegro" }, { "MS", "Montserrat" }, { "MA", "Morocco" }, { "MZ", "Mozambique" }, { "MM", "Myanmar" }, { "NA", "Namibia" }, { "NR", "Nauru" }, { "NP", "Nepal" }, { "NL", "Netherlands" }, { "NC", "New Caledonia" }, { "NZ", "New Zealand" }, { "NI", "Nicaragua" }, { "NE", "Niger" }, { "NG", "Nigeria" }, { "NU", "Niue" }, { "NF", "Norfolk Island" }, { "MK", "North Macedonia" }, { "MP", "Northern Mariana Islands" }, { "NO", "Norway" }, { "OM", "Oman" }, { "PK", "Pakistan" }, { "PW", "Palau" }, { "PS", "Palestine, State of" }, { "PA", "Panama" }, { "PG", "Papua New Guinea" }, { "PY", "Paraguay" }, { "PE", "Peru" }, { "PH", "Philippines" }, { "PN", "Pitcairn" }, { "PL", "Poland" }, { "PT", "Portugal" }, { "PR", "Puerto Rico" }, { "QA", "Qatar" }, { "RE", "Réunion" }, { "RO", "Romania" }, { "RU", "Russian Federation" }, { "RW", "Rwanda" }, { "BL", "Saint Barthélemy" }, { "SH", "Saint Helena, Ascension and Tristan da Cunha" }, { "KN", "Saint Kitts and Nevis" }, { "LC", "Saint Lucia" }, { "MF", "Saint Martin (French part)" }, { "PM", "Saint Pierre and Miquelon" }, { "VC", "Saint Vincent and the Grenadines" }, { "WS", "Samoa" }, { "SM", "San Marino" }, { "ST", "Sao Tome and Principe" }, { "SA", "Saudi Arabia" }, { "SN", "Senegal" }, { "RS", "Serbia" }, { "SC", "Seychelles" }, { "SL", "Sierra Leone" }, { "SG", "Singapore" }, { "SX", "Sint Maarten (Dutch part)" }, { "SK", "Slovakia" }, { "SI", "Slovenia" }, { "SB", "Solomon Islands" }, { "SO", "Somalia" }, { "ZA", "South Africa" }, { "GS", "South Georgia and the South Sandwich Islands" }, { "SS", "South Sudan" }, { "ES", "Spain" }, { "LK", "Sri Lanka" }, { "SD", "Sudan" }, { "SR", "Suriname" }, { "SJ", "Svalbard and Jan Mayen" }, { "SE", "Sweden" }, { "CH", "Switzerland" }, { "SY", "Syrian Arab Republic" }, { "TW", "Taiwan, Province of China" }, { "TJ", "Tajikistan" }, { "TZ", "Tanzania, United Republic of" }, { "TH", "Thailand" }, { "TL", "Timor-Leste" }, { "TG", "Togo" }, { "TK", "Tokelau" }, { "TO", "Tonga" }, { "TT", "Trinidad and Tobago" }, { "TN", "Tunisia" }, { "TR", "Turkey" }, { "TM", "Turkmenistan" }, { "TC", "Turks and Caicos Islands" }, { "TV", "Tuvalu" }, { "UG", "Uganda" }, { "UA", "Ukraine" }, { "AE", "United Arab Emirates" }, { "GB", "United Kingdom of Great Britain and Northern Ireland" }, { "UK", "United Kingdom" }, { "UM", "United States Minor Outlying Islands" }, { "US", "United States of America" }, { "UY", "Uruguay" }, { "UZ", "Uzbekistan" }, { "VU", "Vanuatu" }, { "VE", "Venezuela (Bolivarian Republic of)" }, { "VN", "Viet Nam" }, { "VG", "Virgin Islands (British)" }, { "VI", "Virgin Islands (U.S.)" }, { "WF", "Wallis and Futuna" }, { "EH", "Western Sahara" }, { "YE", "Yemen" }, { "ZM", "Zambia" }, { "ZW", "Zimbabwe" } }; #endif // HAVE_GNUTLS // // '_papplSystemAddPrinter()' - Add a printer // void _papplSystemWebAddPrinter( pappl_client_t *client, pappl_system_t *system) { int i; // Looping var const char *status = NULL; // Status message, if any char printer_name[128] = "", // Printer Name driver_name[128] = "", // Driver Name device_uri[1024] = "", // Device URI *device_id = NULL, // Device ID hostname[256] = "", // Hostname *ptr; // Pointer into string int port = 0; // Default port for Socket printing _pappl_system_dev_t devdata; // Device callback data static const char *hostname_pattern = // IP address or hostname pattern // Hostname per RFC 1123 "(^\\s*((?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\\b-){0,61}[0-9A-Za-z])?(?:\\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\\b-){0,61}[0-9A-Za-z])?)*\\.?)\\s*$)" "|" // IPv4 address "(^\\s*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\\s*$)" "|" // IPv6 address "(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$)"; if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { const char *value; // Form value int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { http_addrlist_t *list; // Address list if ((value = cupsGetOption("printer_name", num_form, form)) != NULL) strlcpy(printer_name, value, sizeof(printer_name)); if ((value = cupsGetOption("driver_name", num_form, form)) != NULL) strlcpy(driver_name, value, sizeof(driver_name)); if ((value = cupsGetOption("device_uri", num_form, form)) != NULL) { strlcpy(device_uri, value, sizeof(device_uri)); if ((device_id = strchr(device_uri, '|')) != NULL) *device_id++ = '\0'; } if (!strcmp(device_uri, "socket")) { // Make URI using hostname if ((value = cupsGetOption("hostname", num_form, form)) == NULL) { status = "Please enter a hostname or IP address for the printer."; device_uri[0] = '\0'; } else { // Break out the port number, if present... strlcpy(hostname, value, sizeof(hostname)); if ((ptr = strrchr(hostname, ':')) != NULL && !strchr(ptr, ']')) { char *end; // End of value *ptr++ = '\0'; port = (int)strtol(ptr, &end, 10); if (errno == ERANGE || *end || port <= 0 || port > 65535) { status = "Bad port number."; device_uri[0] = '\0'; } } if (!status) { // Then see if we can lookup the hostname or IP address (port number // isn't used here...) if ((list = httpAddrGetList(hostname, AF_UNSPEC, "9100")) == NULL) { status = "Unable to lookup address."; } else { httpAddrFreeList(list); httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "socket", NULL, hostname, port, "/"); } } } } if (!printer_name[0]) { status = "Please enter a printer name."; } else if (!device_uri[0]) { status = "Please select a device."; } else if (!driver_name[0]) { status = "Please select a driver."; } else if (!status) { pappl_printer_t *printer = papplPrinterCreate(system, 0, printer_name, driver_name, device_id, device_uri); // New printer if (printer) { papplClientRespondRedirect(client, HTTP_STATUS_FOUND, printer->uriname); cupsFreeOptions(num_form, form); return; } switch (errno) { case EEXIST : status = "A printer with that name already exists."; break; case EIO : status = "Unable to use that driver."; break; case EINVAL : status = "Printer names must start with a letter or underscore and cannot contain special characters."; break; default : status = strerror(errno); break; } } } cupsFreeOptions(num_form, form); } system_header(client, "Add Printer"); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPrintf(client, " \n" " \n" " \n" " \n" " \n" " \n" " \n" "
\n" " \n" " \n" "
\n" "
\n"); system_footer(client); } // // '_papplSystemWebConfig()' - Show the system configuration page. // void _papplSystemWebConfig( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { char dns_sd_name[64], // DNS-SD name location[128], // Location geo_location[128], // Geo-location latitude organization[128], // Organization org_unit[128]; // Organizational unit pappl_contact_t contact; // Contact info const char *status = NULL; // Status message, if any if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) status = "Invalid form data."; else if (!papplClientIsValidForm(client, num_form, form)) status = "Invalid form submission."; else { _papplSystemWebConfigFinalize(system, num_form, form); status = "Changes saved."; } cupsFreeOptions(num_form, form); } system_header(client, "Configuration"); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); _papplClientHTMLInfo(client, true, papplSystemGetDNSSDName(system, dns_sd_name, sizeof(dns_sd_name)), papplSystemGetLocation(system, location, sizeof(location)), papplSystemGetGeoLocation(system, geo_location, sizeof(geo_location)), papplSystemGetOrganization(system, organization, sizeof(organization)), papplSystemGetOrganizationalUnit(system, org_unit, sizeof(org_unit)), papplSystemGetContact(system, &contact)); papplClientHTMLPuts(client, "
\n" "
\n"); system_footer(client); } // // '_papplSystemWebConfigFinalize()' - Save the changes to the system configuration. // void _papplSystemWebConfigFinalize( pappl_system_t *system, // I - System int num_form, // I - Number of form variables cups_option_t *form) // I - Form variables { const char *value, // Form value *geo_lat, // Geo-location latitude *geo_lon, // Geo-location longitude *contact_name, // Contact name *contact_email, // Contact email *contact_tel; // Contact telephone number if ((value = cupsGetOption("dns_sd_name", num_form, form)) != NULL) papplSystemSetDNSSDName(system, *value ? value : NULL); if ((value = cupsGetOption("location", num_form, form)) != NULL) papplSystemSetLocation(system, *value ? value : NULL); geo_lat = cupsGetOption("geo_location_lat", num_form, form); geo_lon = cupsGetOption("geo_location_lon", num_form, form); if (geo_lat && geo_lon) { char uri[1024]; // "geo:" URI if (*geo_lat && *geo_lon) { snprintf(uri, sizeof(uri), "geo:%g,%g", strtod(geo_lat, NULL), strtod(geo_lon, NULL)); papplSystemSetGeoLocation(system, uri); } else papplSystemSetGeoLocation(system, NULL); } if ((value = cupsGetOption("organization", num_form, form)) != NULL) papplSystemSetOrganization(system, *value ? value : NULL); if ((value = cupsGetOption("organizational_unit", num_form, form)) != NULL) papplSystemSetOrganizationalUnit(system, *value ? value : NULL); contact_name = cupsGetOption("contact_name", num_form, form); contact_email = cupsGetOption("contact_email", num_form, form); contact_tel = cupsGetOption("contact_telephone", num_form, form); if (contact_name || contact_email || contact_tel) { pappl_contact_t contact; // Contact info memset(&contact, 0, sizeof(contact)); if (contact_name) strlcpy(contact.name, contact_name, sizeof(contact.name)); if (contact_email) strlcpy(contact.email, contact_email, sizeof(contact.email)); if (contact_tel) strlcpy(contact.telephone, contact_tel, sizeof(contact.telephone)); papplSystemSetContact(system, &contact); } } // // '_papplSystemWebHome()' - Show the system home page. // void _papplSystemWebHome( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { system_header(client, NULL); papplClientHTMLPrintf(client, "
\n" "
\n" "

Configuration Change

\n", client->host_field, client->host_port); _papplClientHTMLPutLinks(client, system->links, PAPPL_LOPTIONS_CONFIGURATION); _papplClientHTMLInfo(client, false, system->dns_sd_name, system->location, system->geo_location, system->organization, system->org_unit, &system->contact); _papplSystemWebSettings(client); papplClientHTMLPuts(client, "
\n" "
\n" "

Printers

\n"); _papplClientHTMLPutLinks(client, system->links, PAPPL_LOPTIONS_PRINTER); papplSystemIteratePrinters(system, (pappl_printer_cb_t)_papplPrinterWebIteratorCallback, client); papplClientHTMLPuts(client, "
\n" "
\n"); system_footer(client); } // // '_papplSystemWebLogFile()' - Return the log file as requested // void _papplSystemWebLogFile( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_GET) { http_status_t code; // HTTP status of response struct stat loginfo; // Log information ssize_t bytes; // Bytes read/written size_t length = 0; // Log length const char *value; // Range Field value char *rangeptr; // Pointer into range... char buffer[8192]; // Copy buffer int fd; // Resource file descriptor long low = 0, // Log lower range high = -1; // Log upper range value = httpGetField(client->http, HTTP_FIELD_RANGE); // If range exists, send log content from low to high if (value && *value && (rangeptr = strstr(value, "bytes=")) != NULL) { if ((low = strtol(rangeptr + 6, &rangeptr, 10)) < 0) low = 0; else if (rangeptr && *rangeptr == '-' && isdigit(rangeptr[1] & 255)) high = strtol(rangeptr + 1, NULL, 10); } if ((fd = open(system->logfile, O_RDONLY)) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to open log file '%s': %s", system->logfile, strerror(errno)); papplClientRespond(client, HTTP_STATUS_SERVER_ERROR, NULL, NULL, 0, 0); return; } // Get log file info if (fstat(fd, &loginfo)) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to access log file '%s': %s", system->logfile, strerror(errno)); papplClientRespond(client, HTTP_STATUS_SERVER_ERROR, NULL, NULL, 0, 0); close(fd); return; } // Write the log in the range if (low > loginfo.st_size) { length = (size_t)loginfo.st_size; code = HTTP_STATUS_OK; low = 0; } else if (high < 0) { length = (size_t)loginfo.st_size - (size_t)low; code = HTTP_STATUS_PARTIAL_CONTENT; } else { length = (size_t)(high - low); code = HTTP_STATUS_PARTIAL_CONTENT; } httpSetLength(client->http, length); httpSetField(client->http, HTTP_FIELD_SERVER, papplSystemGetServerHeader(system)); httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, "text/plain"); // Seek to position low in log if (lseek(fd, (off_t)low, SEEK_CUR) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to seek to offset %ld in log file '%s': %s", low, system->logfile, strerror(errno)); papplClientRespond(client, HTTP_STATUS_SERVER_ERROR, NULL, NULL, 0, 0); close(fd); return; } if (httpWriteResponse(client->http, code) < 0) { close(fd); return; } papplLogClient(client, PAPPL_LOGLEVEL_INFO, "%s %s %d", code == HTTP_STATUS_OK ? "OK" : "Partial Content", "text/plain", (int)length); // Read buffer and write to client while (length > 0 && (bytes = read(fd, buffer, sizeof(buffer))) > 0) { if ((size_t)bytes > length) bytes = (ssize_t)length; length -= (size_t)bytes; httpWrite2(client->http, buffer, (size_t)bytes); } httpWrite2(client->http, "", 0); close(fd); } else papplClientRespond(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0, 0); } // // '_papplSystemWebLogs()' - Show the system logs // void _papplSystemWebLogs( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { pappl_loglevel_t i, // Looping var loglevel; // Current log level const char *status = NULL; // Status message, if any static const char * const levels[] = // Log level strings { "Debugging", "Informational", "Warning", "Errors", "Fatal Errors/Conditions", }; if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { const char *value; // Form value int num_form = 0; // Number of form variables cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { if ((value = cupsGetOption("log_level", num_form, form)) != NULL) { // Get log level and save it... for (loglevel = PAPPL_LOGLEVEL_DEBUG; loglevel <= PAPPL_LOGLEVEL_FATAL; loglevel ++) { if (!strcmp(value, levels[loglevel])) break; } if (loglevel <= PAPPL_LOGLEVEL_FATAL) { papplSystemSetLogLevel(system, loglevel); status = "Changes Saved."; } else status = "Please select a valid log level."; } else { status = "Please select a valid log level."; } } cupsFreeOptions(num_form, form); } system_header(client, "Logs"); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" "
\n" " \n" "
\n" " \n"); system_footer(client); } // // '_papplSystemWebNetwork()' - Show the system network configuration page. // void _papplSystemWebNetwork( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { const char *status = NULL; // Status message, if any struct ifaddrs *addrs, // List of network addresses *addr; // Current network address if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { const char *value; // Form variable value if ((value = cupsGetOption("hostname", num_form, form)) != NULL) { // Set hostname and save it... papplSystemSetHostname(client->system, value); status = "Changes saved."; } } cupsFreeOptions(num_form, form); } system_header(client, "Networking"); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPrintf(client, " \n" " \n" " \n", system->hostname); if (!getifaddrs(&addrs)) { char temp[256], // Address string *tempptr; // Pointer into address papplClientHTMLPuts(client, " \n" " \n"); freeifaddrs(addrs); } papplClientHTMLPuts(client, " \n" "
IPv4 Addresses:"); for (addr = addrs; addr; addr = addr->ifa_next) { if (addr->ifa_name == NULL || addr->ifa_addr == NULL || addr->ifa_addr->sa_family != AF_INET || !(addr->ifa_flags & IFF_UP) || (addr->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || !strncmp(addr->ifa_name, "awdl", 4)) continue; httpAddrString((http_addr_t *)addr->ifa_addr, temp, sizeof(temp)); tempptr = temp; if (!strcmp(addr->ifa_name, "wlan0") || !strcmp(addr->ifa_name, "wlp2s0")) papplClientHTMLPrintf(client, "Wi-Fi: %s
", tempptr); else if (!strncmp(addr->ifa_name, "wlan", 4) && isdigit(addr->ifa_name[4])) papplClientHTMLPrintf(client, "Wi-Fi %d: %s
", (int)strtol(addr->ifa_name + 4, NULL, 10) + 1, tempptr); else if (!strcmp(addr->ifa_name, "en0") || !strcmp(addr->ifa_name, "eth0") || !strncmp(addr->ifa_name, "enx", 3)) papplClientHTMLPrintf(client, "Ethernet: %s
", tempptr); else if (!strncmp(addr->ifa_name, "en", 2) && isdigit(addr->ifa_name[2])) papplClientHTMLPrintf(client, "Ethernet %d: %s
", (int)strtol(addr->ifa_name + 2, NULL, 10) + 1, tempptr); else if (!strncmp(addr->ifa_name, "eth", 3) && isdigit(addr->ifa_name[3])) papplClientHTMLPrintf(client, "Ethernet %d: %s
", (int)strtol(addr->ifa_name + 3, NULL, 10) + 1, tempptr); } papplClientHTMLPuts(client, "
IPv6 Addresses:"); for (addr = addrs; addr; addr = addr->ifa_next) { if (addr->ifa_name == NULL || addr->ifa_addr == NULL || addr->ifa_addr->sa_family != AF_INET6 || !(addr->ifa_flags & IFF_UP) || (addr->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || !strncmp(addr->ifa_name, "awdl", 4)) continue; httpAddrString((http_addr_t *)addr->ifa_addr, temp, sizeof(temp)); if ((tempptr = strchr(temp, '+')) != NULL) *tempptr = '\0'; else if ((tempptr = strchr(temp, ']')) != NULL) *tempptr = '\0'; if (!strncmp(temp, "[v1.", 4)) tempptr = temp + 4; else if (*temp == '[') tempptr = temp + 1; else tempptr = temp; if (!strcmp(addr->ifa_name, "wlan0") || !strcmp(addr->ifa_name, "wlp2s0")) papplClientHTMLPrintf(client, "Wi-Fi: %s
", tempptr); else if (!strncmp(addr->ifa_name, "wlan", 4) && isdigit(addr->ifa_name[4])) papplClientHTMLPrintf(client, "Wi-Fi %d: %s
", (int)strtol(addr->ifa_name + 4, NULL, 10) + 1, tempptr); else if (!strcmp(addr->ifa_name, "en0") || !strcmp(addr->ifa_name, "eth0") || !strncmp(addr->ifa_name, "enx", 3)) papplClientHTMLPrintf(client, "Ethernet: %s
", tempptr); else if (!strncmp(addr->ifa_name, "en", 2) && isdigit(addr->ifa_name[2])) papplClientHTMLPrintf(client, "Ethernet %d: %s
", (int)strtol(addr->ifa_name + 2, NULL, 10) + 1, tempptr); else if (!strncmp(addr->ifa_name, "eth", 3) && isdigit(addr->ifa_name[3])) papplClientHTMLPrintf(client, "Ethernet %d: %s
", (int)strtol(addr->ifa_name + 3, NULL, 10) + 1, tempptr); } papplClientHTMLPuts(client, "
\n" " \n"); _papplClientHTMLPutLinks(client, client->system->links, PAPPL_LOPTIONS_NETWORK); system_footer(client); } // // '_papplSystemWebSecurity()' - Show the system security (users/password) page. // void _papplSystemWebSecurity( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { const char *status = NULL; // Status message, if any struct group *grp; // Current group if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else if (!client->system->auth_service) { const char *old_password, // Old password (if any) *new_password, // New password *new_password2; // New password again char hash[1024]; // Hash of password old_password = cupsGetOption("old_password", num_form, form); new_password = cupsGetOption("new_password", num_form, form); new_password2 = cupsGetOption("new_password2", num_form, form); if (system->password_hash[0] && (!old_password || strcmp(system->password_hash, papplSystemHashPassword(system, system->password_hash, old_password, hash, sizeof(hash))))) { status = "Wrong old password."; } else if (!new_password || !new_password2 || strcmp(new_password, new_password2)) { status = "Passwords do not match."; } else { const char *passptr; // Pointer into password bool have_lower, // Do we have a lowercase letter? have_upper, // Do we have an uppercase letter? have_digit; // Do we have a number? for (passptr = new_password, have_lower = false, have_upper = false, have_digit = false; *passptr; passptr ++) { if (isdigit(*passptr & 255)) have_digit = true; else if (islower(*passptr & 255)) have_lower = true; else if (isupper(*passptr & 255)) have_upper = true; } if (!have_digit || !have_lower || !have_upper || strlen(new_password) < 8) { status = "Password must be at least eight characters long and contain at least one uppercase letter, one lowercase letter, and one digit."; } else { papplSystemHashPassword(system, NULL, new_password, hash, sizeof(hash)); papplSystemSetPassword(system, hash); status = "Password changed."; } } } else { const char *group; // Current group char buffer[8192]; // Buffer for strings struct group grpbuf; // Group buffer grp = NULL; if ((group = cupsGetOption("admin_group", num_form, form)) != NULL) { if (getgrnam_r(group, &grpbuf, buffer, sizeof(buffer), &grp) || !grp) status = "Bad administration group."; else papplSystemSetAdminGroup(system, group); } if ((group = cupsGetOption("print_group", num_form, form)) != NULL) { if (getgrnam_r(group, &grpbuf, buffer, sizeof(buffer), &grp) || !grp) { status = "Bad print group."; } else { papplSystemSetDefaultPrintGroup(system, group); papplSystemIteratePrinters(system, (pappl_printer_cb_t)papplPrinterSetPrintGroup, (void *)group); } } if (!status) status = "Group changes saved."; } cupsFreeOptions(num_form, form); } system_header(client, "Security"); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLPuts(client, "
\n" " \n" "
\n"); if (system->auth_service) { // Show Users pane for group controls papplClientHTMLPuts(client, "
\n" "

Users

\n"); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n" " \n" "
\n" "
\n" " \n"); } else if (system->password_hash[0]) { // Show simple access password update form... papplClientHTMLPuts(client, "
\n" "

Change Access Password

\n"); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n" " \n" " \n" "
\n" "
\n" " \n"); } else { // Show simple access password initial setting form... papplClientHTMLPuts(client, "
\n" "

Set Access Password

\n"); papplClientHTMLStartForm(client, client->uri, false); papplClientHTMLPuts(client, " \n" " \n" " \n" " \n" " \n" " \n" "
\n" "
\n" " \n"); } _papplClientHTMLPutLinks(client, client->system->links, PAPPL_LOPTIONS_SECURITY); // Finish up... papplClientHTMLPuts(client, "
\n"); system_footer(client); } // // '_papplSystemWebSettings()' - Show the system settings panel, as needed. // void _papplSystemWebSettings( pappl_client_t *client) // I - Client { int i, // Looping var count; // Number of links _pappl_link_t *l; // Current link for (i = 0, count = cupsArrayCount(client->system->links); i < count; i ++) { l = (_pappl_link_t *)cupsArrayIndex(client->system->links, i); if (!l) continue; if (l->options & PAPPL_LOPTIONS_OTHER) break; } if (i < count) { papplClientHTMLPuts(client, "

Other Settings

\n" "
"); _papplClientHTMLPutLinks(client, client->system->links, PAPPL_LOPTIONS_OTHER); papplClientHTMLPuts(client, "
\n"); } if ((client->system->options & PAPPL_SOPTIONS_WEB_LOG) && client->system->logfile && strcmp(client->system->logfile, "-") && strcmp(client->system->logfile, "syslog")) { papplClientHTMLPuts(client, "

Logging

\n" "
"); _papplClientHTMLPutLinks(client, client->system->links, PAPPL_LOPTIONS_LOGGING); papplClientHTMLPuts(client, "
\n"); } } #ifdef HAVE_GNUTLS // // '_papplSystemWebTLSInstall()' - Show the system TLS certificate installation page. // void _papplSystemWebTLSInstall( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { const char *status = NULL; // Status message, if any (void)system; if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else { const char *crtfile, // Certificate file *keyfile; // Private key file char filename[1024]; // Filename crtfile = cupsGetOption("certificate", num_form, form); keyfile = cupsGetOption("privatekey", num_form, form); if (!keyfile) { char hostname[256], // Hostname *hostptr; // Pointer into hostname strlcpy(hostname, client->system->hostname, sizeof(hostname)); if ((hostptr = strchr(hostname, '.')) != NULL) *hostptr = '\0'; snprintf(filename, sizeof(filename), "%s/%s.key", client->system->directory, hostname); if (!access(filename, R_OK)) keyfile = filename; else status = "Missing private key."; } if (!status) { if (tls_install_certificate(client, crtfile, keyfile)) status = "Certificate installed."; else status = "Invalid certificate or private key."; } } cupsFreeOptions(num_form, form); } system_header(client, "Install TLS Certificate"); if (status) papplClientHTMLPrintf(client, "
%s
\n", status); papplClientHTMLPuts(client, " \n" " \n" "
\n"); papplClientHTMLStartForm(client, client->uri, true); papplClientHTMLPuts(client, "
\n" "

This form will install a trusted TLS certificate you have obtained from a Certificate Authority ('CA'). Once installed, it will be used immediately.

\n" " \n" " \n" " \n" " \n" " \n" " \n" "
(PEM-encoded)
(PEM-encoded, leave unselected to use the key from the last signing request)
\n" "
\n" " \n"); _papplClientHTMLPutLinks(client, client->system->links, PAPPL_LOPTIONS_TLS); papplClientHTMLPuts(client, "
\n"); system_footer(client); } // // '_papplSystemWebTLSNew()' - Show the system TLS certificate/request creation page. // void _papplSystemWebTLSNew( pappl_client_t *client, // I - Client pappl_system_t *system) // I - System { int i; // Looping var const char *status = NULL; // Status message, if any char crqpath[256] = ""; // Certificate request file, if any bool success = false; // Were we successful? if (!papplClientHTMLAuthorize(client)) return; if (client->operation == HTTP_STATE_POST) { int num_form = 0; // Number of form variable cups_option_t *form = NULL; // Form variables if ((num_form = papplClientGetForm(client, &form)) == 0) { status = "Invalid form data."; } else if (!papplClientIsValidForm(client, num_form, form)) { status = "Invalid form submission."; } else if (!strcmp(client->uri, "/tls-new-crt")) { if (tls_make_certificate(client, num_form, form)) { status = "Certificate created."; success = true; } else status = "Unable to create certificate."; } else { if (tls_make_certsignreq(client, num_form, form, crqpath, sizeof(crqpath))) { status = "Certificate request created."; success = true; } else status = "Unable to create certificate request."; } cupsFreeOptions(num_form, form); } if (!strcmp(client->uri, "/tls-new-crt")) system_header(client, "Create New TLS Certificate"); else system_header(client, "Create TLS Certificate Request"); if (status) { papplClientHTMLPrintf(client, "
%s
\n", status); if (crqpath[0]) papplClientHTMLPrintf(client, "

Download Certificate Request File

\n", crqpath); if (success) { papplClientHTMLPuts(client, " \n" " \n"); system_footer(client); return; } } papplClientHTMLPuts(client, " \n" " \n" "
\n"); papplClientHTMLStartForm(client, client->uri, false); if (!strcmp(client->uri, "/tls-new-crt")) papplClientHTMLPuts(client, "
\n" "

This form creates a new 'self-signed' TLS certificate for secure printing. Self-signed certificates are not automatically trusted by web browsers.

\n" " \n" " \n" " \n"); else papplClientHTMLPuts(client, "
\n" "

This form creates a certificate signing request ('CSR') that you can send to a Certificate Authority ('CA') to obtain a trusted TLS certificate. The private key is saved separately for use with the certificate you get from the CA.

\n" "
 years
\n" " \n"); papplClientHTMLPrintf(client, " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"); else papplClientHTMLPuts(client, "\n" " \n"); papplClientHTMLPuts(client, " \n" "
\n" "
\n" " \n" " \n"); _papplClientHTMLPutLinks(client, client->system->links, PAPPL_LOPTIONS_TLS); papplClientHTMLPuts(client, "
\n"); system_footer(client); } #endif // HAVE_GNUTLS // // 'system_device_cb()' - Device callback for the "add printer" chooser. // static bool // O - `true` to stop, `false` to continue system_device_cb( const char *device_info, // I - Device description const char *device_uri, // I - Device URI const char *device_id, // I - IEEE-1284 device ID void *data) // I - Callback data (client + device URI) { _pappl_system_dev_t *devdata = (_pappl_system_dev_t *)data; // Callback data papplClientHTMLPrintf(devdata->client, "", device_uri, device_id, !strcmp(devdata->device_uri, device_uri) ? " selected" : "", device_info); return (false); } // // 'system_footer()' - Show the system footer. // static void system_footer(pappl_client_t *client) // I - Client { papplClientHTMLPuts(client, " \n"); papplClientHTMLFooter(client); } // // 'system_header()' - Show the system header. // static void system_header(pappl_client_t *client, // I - Client const char *title) // I - Title { if (!papplClientRespond(client, HTTP_STATUS_OK, NULL, "text/html", 0, 0)) return; papplClientHTMLHeader(client, title, 0); if (client->system->versions[0].sversion[0]) papplClientHTMLPrintf(client, "
\n" "
\n" "
\n" " Version %s\n" "
\n" "
\n" "
\n", client->system->versions[0].sversion); papplClientHTMLPuts(client, "
\n"); if (title) papplClientHTMLPrintf(client, "
\n" "
\n" "

%s

\n", title); } #ifdef HAVE_GNUTLS // // 'tls_install_certificate()' - Install a certificate and private key. // static bool // O - `true` on success, `false` otherwise tls_install_certificate( pappl_client_t *client, // I - Client const char *crtfile, // I - PEM-encoded certificate filename const char *keyfile) // I - PEM-encoded private key filename { pappl_system_t *system = papplClientGetSystem(client); // System const char *home; // Home directory char hostname[256], // Hostname basedir[256], // CUPS directory ssldir[256], // CUPS "ssl" directory dstcrt[1024], // Destination certificate dstkey[1024]; // Destination private key gnutls_certificate_credentials_t *credentials; // TLS credentials int status; // Status for loading of credentials // Try loading the credentials... if ((credentials = (gnutls_certificate_credentials_t *)malloc(sizeof(gnutls_certificate_credentials_t))) == NULL) return (false); gnutls_certificate_allocate_credentials(credentials); status = gnutls_certificate_set_x509_key_file(*credentials, crtfile, keyfile, GNUTLS_X509_FMT_PEM); gnutls_certificate_free_credentials(*credentials); free(credentials); if (status != 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to load TLS credentials: %s", gnutls_strerror(status)); return (false); } // If everything checks out, copy the certificate and private key to the // CUPS "ssl" directory... home = getuid() ? getenv("HOME") : NULL; if (home) snprintf(basedir, sizeof(basedir), "%s/.cups", home); else strlcpy(basedir, CUPS_SERVERROOT, sizeof(basedir)); if (access(basedir, X_OK)) { // Make "~/.cups" or "CUPS_SERVERROOT" directory... if (mkdir(basedir, 0755)) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create directory '%s': %s", basedir, strerror(errno)); return (false); } } snprintf(ssldir, sizeof(ssldir), "%s/ssl", basedir); if (access(ssldir, X_OK)) { // Make "~/.cups/ssl" or "CUPS_SERVERROOT/ssl" directory... if (mkdir(ssldir, 0755)) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create directory '%s': %s", ssldir, strerror(errno)); return (false); } } snprintf(dstkey, sizeof(dstkey), "%s/%s.key", ssldir, papplSystemGetHostname(system, hostname, sizeof(hostname))); snprintf(dstcrt, sizeof(dstcrt), "%s/%s.crt", ssldir, hostname); if (!tls_install_file(client, dstkey, keyfile)) { unlink(dstcrt); return (false); } if (!tls_install_file(client, dstcrt, crtfile)) { unlink(dstkey); return (false); } // If we get this far we are done! return (true); } // // 'tls_install_file()' - Copy a TLS file. // static bool // O - `true` on success, `false` otherwise tls_install_file( pappl_client_t *client, // I - Client const char *dst, // I - Destination filename const char *src) // I - Source filename { cups_file_t *dstfile, // Destination file *srcfile; // Source file char buffer[32768]; // Copy buffer ssize_t bytes; // Bytes to copy if ((dstfile = cupsFileOpen(dst, "wb")) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create file '%s': %s", dst, strerror(errno)); return (false); } if ((srcfile = cupsFileOpen(src, "rb")) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to open file '%s': %s", src, strerror(errno)); cupsFileClose(dstfile); unlink(dst); return (false); } while ((bytes = cupsFileRead(srcfile, buffer, sizeof(buffer))) > 0) { if (cupsFileWrite(dstfile, buffer, (size_t)bytes) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to write file '%s': %s", dst, strerror(errno)); cupsFileClose(dstfile); unlink(dst); cupsFileClose(srcfile); return (false); } } cupsFileClose(dstfile); cupsFileClose(srcfile); return (true); } // // 'tls_make_certificate()' - Make a self-signed certificate and private key. // static bool // O - `true` on success, `false` otherwise tls_make_certificate( pappl_client_t *client, // I - Client int num_form, // I - Number of form variables cups_option_t *form) // I - Form variables { int i; // Looping var pappl_system_t *system = papplClientGetSystem(client); // System const char *home, // Home directory *value, // Value from form variables *level, // Level/algorithm+bits *email, // Email address *organization, // Organization name *org_unit, // Organizational unit, if any *city, // City/locality *state, // State/province *country; // Country int duration; // Duration in years int num_alt_names = 1; // Alternate names char alt_names[4][256]; // Subject alternate names char hostname[256], // Hostname *domain, // Domain name basedir[256], // CUPS directory ssldir[256], // CUPS "ssl" directory crtfile[1024], // Certificate file keyfile[1024]; // Private key file gnutls_x509_crt_t crt; // Self-signed certificate gnutls_x509_privkey_t key; // Private/public key pair cups_file_t *fp; // Key/cert file unsigned char buffer[8192]; // Buffer for key/cert data size_t bytes; // Number of bytes of data unsigned char serial[4]; // Serial number buffer int status; // GNU TLS status // Verify that we have all of the required form variables... if ((value = cupsGetOption("duration", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'duration' form field."); return (false); } else if ((duration = (int)strtol(value, NULL, 10)) < 1 || duration > 10) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Bad 'duration'='%s' form field.", value); return (false); } if ((level = cupsGetOption("level", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'level' form field."); return (false); } if ((email = cupsGetOption("email", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'email' form field."); return (false); } if ((organization = cupsGetOption("organization", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'organization' form field."); return (false); } if ((org_unit = cupsGetOption("organizational_unit", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'organizational_unit' form field."); return (false); } if ((city = cupsGetOption("city", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'city' form field."); return (false); } if ((state = cupsGetOption("state", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'state' form field."); return (false); } if ((country = cupsGetOption("country", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'country' form field."); return (false); } // Get all of the names this system is known by... papplSystemGetHostname(system, hostname, sizeof(hostname)); if ((domain = strchr(hostname, '.')) != NULL) { // If the domain name is not hostname.local or hostname.lan, make that the // second Subject Alternate Name... if (strcmp(domain, ".local") && strcmp(domain, ".lan")) strlcpy(alt_names[num_alt_names ++], hostname, sizeof(alt_names[0])); *domain = '\0'; } // then add hostname as the first alternate name... strlcpy(alt_names[0], hostname, sizeof(alt_names[0])); // and finish up with hostname.lan and hostname.local as the final alternates... snprintf(alt_names[num_alt_names ++], sizeof(alt_names[0]), "%s.lan", hostname); snprintf(alt_names[num_alt_names ++], sizeof(alt_names[0]), "%s.local", hostname); // Store the certificate and private key in the CUPS "ssl" directory... home = getuid() ? getenv("HOME") : NULL; if (home) snprintf(basedir, sizeof(basedir), "%s/.cups", home); else strlcpy(basedir, CUPS_SERVERROOT, sizeof(basedir)); if (access(basedir, X_OK)) { // Make "~/.cups" or "CUPS_SERVERROOT" directory... if (mkdir(basedir, 0755)) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create directory '%s': %s", basedir, strerror(errno)); return (false); } } snprintf(ssldir, sizeof(ssldir), "%s/ssl", basedir); if (access(ssldir, X_OK)) { // Make "~/.cups/ssl" or "CUPS_SERVERROOT/ssl" directory... if (mkdir(ssldir, 0755)) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create directory '%s': %s", ssldir, strerror(errno)); return (false); } } snprintf(keyfile, sizeof(keyfile), "%s/%s.key", ssldir, hostname); snprintf(crtfile, sizeof(crtfile), "%s/%s.crt", ssldir, hostname); papplLogClient(client, PAPPL_LOGLEVEL_DEBUG, "Creating crtfile='%s', keyfile='%s'.", crtfile, keyfile); // Create the paired encryption keys... gnutls_x509_privkey_init(&key); if (!strcmp(level, "rsa-2048")) gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0); else if (!strcmp(level, "rsa-4096")) gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 4096, 0); else gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 384, 0); // Save the private key... bytes = sizeof(buffer); if ((status = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to export private key: %s", gnutls_strerror(status)); gnutls_x509_privkey_deinit(key); return (false); } else if ((fp = cupsFileOpen(keyfile, "w")) != NULL) { cupsFileWrite(fp, (char *)buffer, bytes); cupsFileClose(fp); } else { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create private key file '%s': %s", keyfile, strerror(errno)); gnutls_x509_privkey_deinit(key); return (false); } // Create the self-signed certificate... i = (int)(time(NULL) / 60); serial[0] = (unsigned char)(i >> 24); serial[1] = (unsigned char)(i >> 16); serial[2] = (unsigned char)(i >> 8); serial[3] = (unsigned char)i; gnutls_x509_crt_init(&crt); gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, country, (unsigned)strlen(country)); gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, hostname, (unsigned)strlen(hostname)); gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, organization, (unsigned)strlen(organization)); gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, org_unit, (unsigned)strlen(org_unit)); gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, state, (unsigned)strlen(state)); gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0, city, (unsigned)strlen(city)); gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0, email, (unsigned)strlen(email)); gnutls_x509_crt_set_key(crt, key); gnutls_x509_crt_set_serial(crt, serial, sizeof(serial)); gnutls_x509_crt_set_activation_time(crt, time(NULL)); gnutls_x509_crt_set_expiration_time(crt, time(NULL) + duration * 365 * 86400); gnutls_x509_crt_set_ca_status(crt, 0); gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, alt_names[0], (unsigned)strlen(alt_names[0]), GNUTLS_FSAN_SET); for (i = 1; i < num_alt_names; i ++) gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, alt_names[i], (unsigned)strlen(alt_names[i]), GNUTLS_FSAN_APPEND); gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0); gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT); gnutls_x509_crt_set_version(crt, 3); bytes = sizeof(buffer); if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0) gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes); gnutls_x509_crt_sign(crt, crt, key); // Save the certificate and public key... bytes = sizeof(buffer); if ((status = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to export public key and X.509 certificate: %s", gnutls_strerror(status)); gnutls_x509_crt_deinit(crt); gnutls_x509_privkey_deinit(key); return (false); } else if ((fp = cupsFileOpen(crtfile, "w")) != NULL) { cupsFileWrite(fp, (char *)buffer, bytes); cupsFileClose(fp); } else { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create public key and X.509 certificate file '%s': %s", crtfile, strerror(errno)); gnutls_x509_crt_deinit(crt); gnutls_x509_privkey_deinit(key); return (false); } // Now create symlinks for each of the alternate names... for (i = 1; i < num_alt_names; i ++) { char altfile[1024]; // Alternate cert/key filename snprintf(altfile, sizeof(altfile), "%s/%s.key", ssldir, alt_names[i]); unlink(altfile); symlink(keyfile, altfile); snprintf(altfile, sizeof(altfile), "%s/%s.crt", ssldir, alt_names[i]); unlink(altfile); symlink(crtfile, altfile); } // If we get this far we are done! gnutls_x509_crt_deinit(crt); gnutls_x509_privkey_deinit(key); return (true); } // // 'tls_make_certsignreq()' - Make a certificate signing request and private key. // static bool // O - `true` on success, `false` otherwise tls_make_certsignreq( pappl_client_t *client, // I - Client int num_form, // I - Number of form variables cups_option_t *form, // I - Form variables char *crqpath, // I - Certificate request filename buffer size_t crqsize) // I - Size of certificate request buffer { pappl_system_t *system = papplClientGetSystem(client); // System const char *level, // Level/algorithm+bits *email, // Email address *organization, // Organization name *org_unit, // Organizational unit, if any *city, // City/locality *state, // State/province *country; // Country char hostname[256], // Hostname crqfile[1024], // Certificate request file keyfile[1024]; // Private key file gnutls_x509_crq_t crq; // Certificate request gnutls_x509_privkey_t key; // Private/public key pair cups_file_t *fp; // Key/cert file unsigned char buffer[8192]; // Buffer for key/cert data size_t bytes; // Number of bytes of data int status; // GNU TLS status *crqpath = '\0'; // Verify that we have all of the required form variables... if ((level = cupsGetOption("level", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'level' form field."); return (false); } if ((email = cupsGetOption("email", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'email' form field."); return (false); } if ((organization = cupsGetOption("organization", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'organization' form field."); return (false); } if ((org_unit = cupsGetOption("organizational_unit", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'organizational_unit' form field."); return (false); } if ((city = cupsGetOption("city", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'city' form field."); return (false); } if ((state = cupsGetOption("state", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'state' form field."); return (false); } if ((country = cupsGetOption("country", num_form, form)) == NULL) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Missing 'country' form field."); return (false); } // Store the certificate request and private key in the spool directory... snprintf(keyfile, sizeof(keyfile), "%s/%s.key", system->directory, papplSystemGetHostname(system, hostname, sizeof(hostname))); snprintf(crqfile, sizeof(crqfile), "%s/%s.csr", system->directory, hostname); snprintf(crqpath, crqsize, "/%s.csr", hostname); // Create the paired encryption keys... gnutls_x509_privkey_init(&key); if (!strcmp(level, "rsa-2048")) gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0); else if (!strcmp(level, "rsa-4096")) gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 4096, 0); else gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, 384, 0); // Save the private key... bytes = sizeof(buffer); if ((status = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to export private key: %s", gnutls_strerror(status)); gnutls_x509_privkey_deinit(key); return (false); } else if ((fp = cupsFileOpen(keyfile, "w")) != NULL) { cupsFileWrite(fp, (char *)buffer, bytes); cupsFileClose(fp); } else { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create private key file '%s': %s", keyfile, strerror(errno)); gnutls_x509_privkey_deinit(key); return (false); } // Create the certificate request... gnutls_x509_crq_init(&crq); gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_COUNTRY_NAME, 0, country, (unsigned)strlen(country)); gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_COMMON_NAME, 0, hostname, (unsigned)strlen(hostname)); gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, organization, (unsigned)strlen(organization)); gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, org_unit, (unsigned)strlen(org_unit)); gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, state, (unsigned)strlen(state)); gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_LOCALITY_NAME, 0, city, (unsigned)strlen(city)); gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_PKCS9_EMAIL, 0, email, (unsigned)strlen(email)); gnutls_x509_crq_set_key(crq, key); gnutls_x509_crq_set_subject_alt_name(crq, GNUTLS_SAN_DNSNAME, hostname, (unsigned)strlen(hostname), GNUTLS_FSAN_SET); gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_TLS_WWW_SERVER, 0); gnutls_x509_crq_set_key_usage(crq, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT); gnutls_x509_crq_set_version(crq, 3); gnutls_x509_crq_sign2(crq, key, GNUTLS_DIG_SHA256, 0); // Save the certificate request and public key... bytes = sizeof(buffer); if ((status = gnutls_x509_crq_export(crq, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0) { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to export public key and X.509 certificate request: %s", gnutls_strerror(status)); gnutls_x509_crq_deinit(crq); gnutls_x509_privkey_deinit(key); return (false); } else if ((fp = cupsFileOpen(crqfile, "w")) != NULL) { cupsFileWrite(fp, (char *)buffer, bytes); cupsFileClose(fp); } else { papplLogClient(client, PAPPL_LOGLEVEL_ERROR, "Unable to create public key and X.509 certificate request file '%s': %s", crqfile, strerror(errno)); gnutls_x509_crq_deinit(crq); gnutls_x509_privkey_deinit(key); return (false); } // If we get this far we are done! papplSystemAddResourceFile(system, crqpath, "application/pkcs10", crqfile); gnutls_x509_crq_deinit(crq); gnutls_x509_privkey_deinit(key); return (true); } #endif // HAVE_GNUTLS pappl-1.0.3/pappl/system.c000066400000000000000000000641441403603036100154400ustar00rootroot00000000000000// // System object for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // Copyright © 2010-2019 by Apple Inc. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "pappl-private.h" #include "resource-private.h" #include "device-private.h" // // Local globals... // static time_t sigterm_time = 0; // SIGTERM time? static bool restart_logging = false;// Restart logging? // // Local functions... // static void make_attributes(pappl_system_t *system); static void sighup_handler(int sig); static void sigterm_handler(int sig); // // '_papplSystemAddPrinterIcons()' - (Re)add printer icon resources. // void _papplSystemAddPrinterIcons( pappl_system_t *system, // I - System pappl_printer_t *printer) // I - Printer { char path[256]; // Resource path pappl_icon_t *icons = printer->driver_data.icons; // Printer icons snprintf(path, sizeof(path), "%s/icon-sm.png", printer->uriname); papplSystemRemoveResource(system, path); if (icons[0].filename[0]) papplSystemAddResourceFile(system, path, "image/png", icons[0].filename); else if (icons[0].data && icons[0].datalen) papplSystemAddResourceData(system, path, "image/png", icons[0].data, icons[0].datalen); else papplSystemAddResourceData(system, path, "image/png", icon_sm_png, sizeof(icon_sm_png)); snprintf(path, sizeof(path), "%s/icon-md.png", printer->uriname); papplSystemRemoveResource(system, path); if (icons[1].filename[0]) papplSystemAddResourceFile(system, path, "image/png", icons[1].filename); else if (icons[1].data && icons[1].datalen) papplSystemAddResourceData(system, path, "image/png", icons[1].data, icons[1].datalen); else papplSystemAddResourceData(system, path, "image/png", icon_md_png, sizeof(icon_md_png)); snprintf(path, sizeof(path), "%s/icon-lg.png", printer->uriname); papplSystemRemoveResource(system, path); if (icons[2].filename[0]) papplSystemAddResourceFile(system, path, "image/png", icons[2].filename); else if (icons[2].data && icons[2].datalen) papplSystemAddResourceData(system, path, "image/png", icons[2].data, icons[2].datalen); else papplSystemAddResourceData(system, path, "image/png", icon_lg_png, sizeof(icon_lg_png)); } // // '_papplSystemConfigChanged()' - Mark the system configuration as changed. // void _papplSystemConfigChanged( pappl_system_t *system) // I - System { pthread_mutex_lock(&system->config_mutex); if (system->is_running) { system->config_time = time(NULL); system->config_changes ++; } pthread_mutex_unlock(&system->config_mutex); } // // 'papplSystemCreate()' - Create a system object. // // This function creates a new system object, which is responsible for managing // all the printers, jobs, and resources used by the printer application. // // The "options" argument specifies which options are enabled for the server: // // - `PAPPL_SOPTIONS_NONE`: No options. // - `PAPPL_SOPTIONS_DNSSD_HOST`: When resolving DNS-SD service name collisions, // use the DNS-SD hostname instead of a serial number or UUID. // - `PAPPL_SOPTIONS_WEB_LOG`: Include the log file web page. // - `PAPPL_SOPTIONS_MULTI_QUEUE`: Support multiple printers. // - `PAPPL_SOPTIONS_WEB_NETWORK`: Include the network settings web page. // - `PAPPL_SOPTIONS_RAW_SOCKET`: Accept jobs via raw sockets starting on port // 9100. // - `PAPPL_SOPTIONS_WEB_REMOTE`: Allow remote queue management. // - `PAPPL_SOPTIONS_WEB_SECURITY`: Include the security settings web page. // - `PAPPL_SOPTIONS_WEB_INTERFACE`: Include the standard printer and job monitoring // web pages. // - `PAPPL_SOPTIONS_WEB_TLS`: Include the TLS settings page. // - `PAPPL_SOPTIONS_USB_PRINTER`: Accept jobs via USB for the default printer // (embedded Linux only). // // The "name" argument specifies a human-readable name for the system. // // The "port" argument specifies the port number to bind to. A value of `0` // will cause an available port number to be assigned when the first listener // is added with the @link papplSystemAddListeners@ function. // // The "subtypes" argument specifies one or more comma-delimited DNS-SD service // sub-types such as "_print" and "_universal". // // The "spooldir" argument specifies the location of job files. If `NULL`, a // temporary directory is created. // // The "logfile" argument specifies where to send log messages. If `NULL`, the // log messages are written to a temporary file. // // The "loglevel" argument specifies the initial logging level. // // The "auth_service" argument specifies a PAM authentication service name. If // `NULL`, no user authentication will be provided. // // The "tls_only" argument controls whether the printer application will accept // unencrypted connections. In general, this argument should always be `false` // (allow unencrypted connections) since not all clients support encrypted // printing. // pappl_system_t * // O - System object papplSystemCreate( pappl_soptions_t options, // I - Server options const char *name, // I - System name int port, // I - Port number or `0` for auto const char *subtypes, // I - DNS-SD sub-types or `NULL` for none const char *spooldir, // I - Spool directory or `NULL` for default const char *logfile, // I - Log file or `NULL` for default pappl_loglevel_t loglevel, // I - Log level const char *auth_service, // I - PAM authentication service or `NULL` for none bool tls_only) // I - Only support TLS connections? { pappl_system_t *system; // System object const char *tmpdir; // Temporary directory if (!name) return (NULL); // Allocate memory... if ((system = (pappl_system_t *)calloc(1, sizeof(pappl_system_t))) == NULL) return (NULL); // Initialize values... pthread_rwlock_init(&system->rwlock, NULL); pthread_rwlock_init(&system->session_rwlock, NULL); pthread_mutex_init(&system->config_mutex, NULL); system->options = options; system->start_time = time(NULL); system->name = strdup(name); system->dns_sd_name = strdup(name); system->port = port; system->directory = spooldir ? strdup(spooldir) : NULL; system->logfd = -1; system->logfile = logfile ? strdup(logfile) : NULL; system->loglevel = loglevel; system->logmaxsize = 1024 * 1024; system->next_client = 1; system->next_printer_id = 1; system->subtypes = subtypes ? strdup(subtypes) : NULL; system->tls_only = tls_only; system->admin_gid = (gid_t)-1; system->auth_service = auth_service ? strdup(auth_service) : NULL; if (!system->name || !system->dns_sd_name || (spooldir && !system->directory) || (logfile && !system->logfile) || (subtypes && !system->subtypes) || (auth_service && !system->auth_service)) goto fatal; // Make sure the system name and UUID are initialized... papplSystemSetHostname(system, NULL); papplSystemSetUUID(system, NULL); // Set the system TLS credentials... cupsSetServerCredentials(NULL, system->hostname, 1); // See if the spool directory can be created... if ((tmpdir = getenv("TMPDIR")) == NULL) #ifdef __APPLE__ tmpdir = "/private/tmp"; #else tmpdir = "/tmp"; #endif // __APPLE__ if (!system->directory) { char newspooldir[256]; // Spool directory snprintf(newspooldir, sizeof(newspooldir), "%s/pappl%d.d", tmpdir, (int)getpid()); if ((system->directory = strdup(newspooldir)) == NULL) goto fatal; } if (mkdir(system->directory, 0700) && errno != EEXIST) { perror(system->directory); goto fatal; } // Initialize logging... if (system->loglevel == PAPPL_LOGLEVEL_UNSPEC) system->loglevel = PAPPL_LOGLEVEL_ERROR; if (!system->logfile) { // Default log file is $TMPDIR/papplUID.log... char newlogfile[256]; // Log filename snprintf(newlogfile, sizeof(newlogfile), "%s/pappl%d.log", tmpdir, (int)getpid()); if ((system->logfile = strdup(newlogfile)) == NULL) goto fatal; } _papplLogOpen(system); // Initialize authentication... if (system->auth_service && !strcmp(system->auth_service, "none")) { free(system->auth_service); system->auth_service = NULL; } // Initialize base filters... #ifdef HAVE_LIBJPEG papplSystemAddMIMEFilter(system, "image/jpeg", "image/pwg-raster", _papplJobFilterJPEG, NULL); #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG papplSystemAddMIMEFilter(system, "image/png", "image/pwg-raster", _papplJobFilterPNG, NULL); #endif // HAVE_LIBPNG return (system); // If we get here, something went wrong... fatal: papplSystemDelete(system); return (NULL); } // // 'papplSystemDelete()' - Delete a system object. // // > Note: A system object cannot be deleted while the system is running. // void papplSystemDelete( pappl_system_t *system) // I - System object { int i; // Looping var if (!system || system->is_running) return; _papplSystemUnregisterDNSSDNoLock(system); cupsArrayDelete(system->printers); free(system->uuid); free(system->name); free(system->dns_sd_name); free(system->hostname); free(system->server_header); free(system->directory); free(system->logfile); free(system->subtypes); free(system->auth_service); free(system->admin_group); free(system->default_print_group); if (system->logfd >= 0 && system->logfd != 2) close(system->logfd); for (i = 0; i < system->num_listeners; i ++) close(system->listeners[i].fd); cupsArrayDelete(system->filters); cupsArrayDelete(system->links); cupsArrayDelete(system->resources); pthread_rwlock_destroy(&system->rwlock); pthread_rwlock_destroy(&system->session_rwlock); pthread_mutex_destroy(&system->config_mutex); free(system); } // // '_papplSystemMakeUUID()' - Make a UUID for a system, printer, or job. // // Unlike httpAssembleUUID, this function does not introduce random data for // printers so the UUIDs are stable. // char * // I - UUID string _papplSystemMakeUUID( pappl_system_t *system, // I - System const char *printer_name, // I - Printer name or `NULL` for none int job_id, // I - Job ID or `0` for none char *buffer, // I - String buffer size_t bufsize) // I - Size of buffer { char data[1024]; // Source string for MD5 unsigned char sha256[32]; // SHA-256 digest/sum // Build a version 3 UUID conforming to RFC 4122. // // Start with the SHA2-256 sum of the hostname, port, object name and // number, and some random data on the end for jobs (to avoid duplicates). if (printer_name && job_id) snprintf(data, sizeof(data), "_PAPPL_JOB_:%s:%d:%s:%d:%08x", system->hostname, system->port, printer_name, job_id, _papplGetRand()); else if (printer_name) snprintf(data, sizeof(data), "_PAPPL_PRINTER_:%s:%d:%s", system->hostname, system->port, printer_name); else snprintf(data, sizeof(data), "_PAPPL_SYSTEM_:%s:%d", system->hostname, system->port); cupsHashData("sha2-256", (unsigned char *)data, strlen(data), sha256, sizeof(sha256)); // Generate the UUID from the SHA-256... snprintf(buffer, bufsize, "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", sha256[0], sha256[1], sha256[3], sha256[4], sha256[5], sha256[6], (sha256[10] & 15) | 0x30, sha256[11], (sha256[15] & 0x3f) | 0x40, sha256[16], sha256[20], sha256[21], sha256[25], sha256[26], sha256[30], sha256[31]); return (buffer); } // // 'papplSystemRun()' - Run the printer application. // // This function runs the printer application, accepting new connections, // handling requests, and processing jobs as needed. It returns once the // system is shutdown, either through an IPP request or `SIGTERM`. // void papplSystemRun(pappl_system_t *system) // I - System { int i, // Looping var count; // Number of listeners that fired pappl_client_t *client; // New client char header[HTTP_MAX_VALUE]; // Server: header value int dns_sd_host_changes; // Current number of host name changes pappl_printer_t *printer; // Current printer pappl_printer_t *usb_printer = NULL; // USB printer // Range check... if (!system) return; if (system->is_running) { papplLog(system, PAPPL_LOGLEVEL_FATAL, "Tried to run system when already running."); return; } if (system->num_listeners == 0) { papplLog(system, PAPPL_LOGLEVEL_FATAL, "Tried to run system without listeners."); return; } system->is_running = true; // Add fallback resources... papplSystemAddResourceData(system, "/favicon.png", "image/png", icon_md_png, sizeof(icon_md_png)); papplSystemAddResourceData(system, "/navicon.png", "image/png", icon_sm_png, sizeof(icon_sm_png)); papplSystemAddResourceString(system, "/style.css", "text/css", style_css); if ((system->options & PAPPL_SOPTIONS_WEB_LOG) && system->logfile && strcmp(system->logfile, "-") && strcmp(system->logfile, "syslog")) { papplSystemAddResourceCallback(system, "/logfile.txt", "text/plain", (pappl_resource_cb_t)_papplSystemWebLogFile, system); papplSystemAddResourceCallback(system, "/logs", "text/html", (pappl_resource_cb_t)_papplSystemWebLogs, system); papplSystemAddLink(system, "View Logs", "/logs", PAPPL_LOPTIONS_LOGGING | PAPPL_LOPTIONS_HTTPS_REQUIRED); } if (system->options & PAPPL_SOPTIONS_WEB_INTERFACE) { if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE) { papplSystemAddResourceCallback(system, "/", "text/html", (pappl_resource_cb_t)_papplSystemWebHome, system); papplSystemAddResourceCallback(system, "/addprinter", "text/html", (pappl_resource_cb_t)_papplSystemWebAddPrinter, system); papplSystemAddLink(system, "Add Printer", "/addprinter", PAPPL_LOPTIONS_PRINTER | PAPPL_LOPTIONS_HTTPS_REQUIRED); } if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE) papplSystemAddResourceCallback(system, "/config", "text/html", (pappl_resource_cb_t)_papplSystemWebConfig, system); if (system->options & PAPPL_SOPTIONS_WEB_NETWORK) { papplSystemAddResourceCallback(system, "/network", "text/html", (pappl_resource_cb_t)_papplSystemWebNetwork, system); papplSystemAddLink(system, "Network", "/network", PAPPL_LOPTIONS_OTHER | PAPPL_LOPTIONS_HTTPS_REQUIRED); } if (system->options & PAPPL_SOPTIONS_WEB_SECURITY) { papplSystemAddResourceCallback(system, "/security", "text/html", (pappl_resource_cb_t)_papplSystemWebSecurity, system); papplSystemAddLink(system, "Security", "/security", PAPPL_LOPTIONS_OTHER | PAPPL_LOPTIONS_HTTPS_REQUIRED); } #ifdef HAVE_GNUTLS if (system->options & PAPPL_SOPTIONS_WEB_TLS) { papplSystemAddResourceCallback(system, "/tls-install-crt", "text/html", (pappl_resource_cb_t)_papplSystemWebTLSInstall, system); papplSystemAddLink(system, "Install TLS Certificate", "/tls-install-crt", PAPPL_LOPTIONS_OTHER | PAPPL_LOPTIONS_HTTPS_REQUIRED); papplSystemAddResourceCallback(system, "/tls-new-crt", "text/html", (pappl_resource_cb_t)_papplSystemWebTLSNew, system); papplSystemAddLink(system, "Create New TLS Certificate", "/tls-new-crt", PAPPL_LOPTIONS_OTHER | PAPPL_LOPTIONS_HTTPS_REQUIRED); papplSystemAddResourceCallback(system, "/tls-new-csr", "text/html", (pappl_resource_cb_t)_papplSystemWebTLSNew, system); papplSystemAddLink(system, "Create TLS Certificate Request", "/tls-new-csr", PAPPL_LOPTIONS_OTHER | PAPPL_LOPTIONS_HTTPS_REQUIRED); } #endif // HAVE_GNUTLS } // Catch important signals... papplLog(system, PAPPL_LOGLEVEL_INFO, "Starting system."); signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGHUP, sighup_handler); // Set the server header... free(system->server_header); if (system->versions[0].name[0]) { char safe_name[64], // "Safe" name *safe_ptr; // Pointer into "safe" name // Replace spaces and other not-allowed characters in the firmware name // with an underscore... strlcpy(safe_name, system->versions[0].name, sizeof(safe_name)); for (safe_ptr = safe_name; *safe_ptr; safe_ptr ++) { if (*safe_ptr <= ' ' || *safe_ptr == '/' || *safe_ptr == 0x7f || (*safe_ptr & 0x80)) *safe_ptr = '_'; } // Format the server header using the sanitized firmware name and version... snprintf(header, sizeof(header), "%s/%s PAPPL/" PAPPL_VERSION " CUPS IPP/2.0", safe_name, system->versions[0].sversion); } else { // If no version information is registered, just say "unknown" for the // main name... strlcpy(header, "Unknown PAPPL/" PAPPL_VERSION " CUPS IPP/2.0", sizeof(header)); } if ((system->server_header = strdup(header)) == NULL) { papplLog(system, PAPPL_LOGLEVEL_FATAL, "Unable to allocate Server header value."); system->is_running = false; return; } // Make the static attributes... make_attributes(system); // Advertise the system via DNS-SD as needed... if (system->dns_sd_name) _papplSystemRegisterDNSSDNoLock(system); // Start up printers... for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); // Advertise via DNS-SD as needed... if (printer->dns_sd_name) _papplPrinterRegisterDNSSDNoLock(printer); // Start the raw socket listeners as needed... if ((system->options & PAPPL_SOPTIONS_RAW_SOCKET) && printer->num_raw_listeners > 0) { pthread_t tid; // Thread ID if (pthread_create(&tid, NULL, (void *(*)(void *))_papplPrinterRunRaw, printer)) { // Unable to create listener thread... papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create raw listener thread: %s", strerror(errno)); } else { // Detach the main thread from the raw thread to prevent hangs... pthread_detach(tid); } } } // Start the USB listener as needed... if (system->options & PAPPL_SOPTIONS_USB_PRINTER) { // USB support is limited to a single (default) printer... if ((usb_printer = papplSystemFindPrinter(system, NULL, system->default_printer_id, NULL)) != NULL) { pthread_t tid; // Thread ID if (pthread_create(&tid, NULL, (void *(*)(void *))_papplPrinterRunUSB, usb_printer)) { // Unable to create USB thread... papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to create USB printer thread: %s", strerror(errno)); } else { // Detach the main thread from the raw thread to prevent hangs... pthread_detach(tid); } } } // Loop until we are shutdown or have a hard error... for (;;) { if (restart_logging) { restart_logging = false; _papplLogOpen(system); } if ((count = poll(system->listeners, (nfds_t)system->num_listeners, 1000)) < 0 && errno != EINTR && errno != EAGAIN) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to accept new connections: %s", strerror(errno)); break; } if (count > 0) { // Accept client connections as needed... for (i = 0; i < system->num_listeners; i ++) { if (system->listeners[i].revents & POLLIN) { if ((client = _papplClientCreate(system, system->listeners[i].fd)) != NULL) { if (pthread_create(&client->thread_id, NULL, (void *(*)(void *))_papplClientRun, client)) { // Unable to create client thread... papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to create client thread: %s", strerror(errno)); _papplClientDelete(client); } else { // Detach the main thread from the client thread to prevent hangs... pthread_detach(client->thread_id); } } } } } dns_sd_host_changes = _papplDNSSDGetHostChanges(); if (system->dns_sd_any_collision || system->dns_sd_host_changes != dns_sd_host_changes) { // Handle name collisions... bool force_dns_sd = system->dns_sd_host_changes != dns_sd_host_changes; // Force re-registration? if (force_dns_sd) papplSystemSetHostname(system, NULL); pthread_rwlock_rdlock(&system->rwlock); if (system->dns_sd_collision || force_dns_sd) _papplSystemRegisterDNSSDNoLock(system); for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); if (printer->dns_sd_collision || force_dns_sd) _papplPrinterRegisterDNSSDNoLock(printer); } system->dns_sd_any_collision = false; system->dns_sd_host_changes = dns_sd_host_changes; pthread_rwlock_unlock(&system->rwlock); } if (system->config_changes > system->save_changes) { pthread_mutex_lock(&system->config_mutex); system->save_changes = system->config_changes; pthread_mutex_unlock(&system->config_mutex); if (system->save_cb) { // Save the configuration... (system->save_cb)(system, system->save_cbdata); } } if (system->shutdown_time || sigterm_time) { // Shutdown requested, see if we can do so safely... int jcount = 0; // Number of active jobs // Force shutdown after 60 seconds if (system->shutdown_time && (time(NULL) - system->shutdown_time) > 60) break; // Shutdown-System request if (sigterm_time && (time(NULL) - sigterm_time) > 60) break; // SIGTERM received // Otherwise shutdown immediately if there are no more active jobs... pthread_rwlock_rdlock(&system->rwlock); for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); pthread_rwlock_rdlock(&printer->rwlock); jcount += cupsArrayCount(printer->active_jobs); pthread_rwlock_unlock(&printer->rwlock); } pthread_rwlock_unlock(&system->rwlock); if (jcount == 0) break; } // Clean out old jobs... if (system->clean_time && time(NULL) >= system->clean_time) papplSystemCleanJobs(system); } papplLog(system, PAPPL_LOGLEVEL_INFO, "Shutting down system."); ippDelete(system->attrs); system->attrs = NULL; if (system->dns_sd_name) _papplSystemUnregisterDNSSDNoLock(system); for (i = 0, count = cupsArrayCount(system->printers); i < count; i ++) { printer = (pappl_printer_t *)cupsArrayIndex(system->printers, i); // Remove advertising via DNS-SD as needed... if (printer->dns_sd_name) _papplPrinterUnregisterDNSSDNoLock(printer); } if (system->save_changes < system->config_changes && system->save_cb) { // Save the configuration... (system->save_cb)(system, system->save_cbdata); } system->is_running = false; if (usb_printer) { while (usb_printer->usb_active) usleep(100000); } } // // 'papplSystemShutdown()' - Shutdown the system. // // This function tells the system to perform an orderly shutdown of all printers // and to terminate the main loop. // void papplSystemShutdown( pappl_system_t *system) // I - System { if (system && !system->shutdown_time) system->shutdown_time = time(NULL); } // // 'make_attributes()' - Make the static attributes for the system. // static void make_attributes(pappl_system_t *system) // I - System { int i; // Looping var ipp_attribute_t *attr; // Attribute static const char * const printer_creation_attributes_supported[] = { // "printer-creation-attributes-supported" Values "copies-default", "finishings-col-default", "finishings-default", "media-col-default", "media-default", "orientation-requested-default", "print-color-mode-default", "print-content-optimize-default", "print-quality-default", "printer-contact-col", "printer-device-id", "printer-dns-sd-name", "printer-geo-location", "printer-location", "printer-name", "printer-resolution-default", "smi2699-device-command", "smi2699-device-uri" }; static const char * const system_mandatory_printer_attributes[] = { // "system-mandatory-printer-attributes" values "printer-name", "smi2699-device-command", "smi2699-device-uri" }; static const char * const system_settable_attributes_supported[] = { // "system-settable-attributes-supported" values "system-contact-col", "system-default-printer-id", "system-dns-sd-name", "system-geo-location", "system-location", "system-name", "system-organization", "system-organizational-unit" }; system->attrs = ippNew(); // printer-creation-attributes-supported ippAddStrings(system->attrs, IPP_TAG_SYSTEM, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-creation-attributes-supported", (int)(sizeof(printer_creation_attributes_supported) / sizeof(printer_creation_attributes_supported[0])), NULL, printer_creation_attributes_supported); // smi2699-device-command-supported if (system->num_drivers > 0) { int num_drivers = system->num_drivers; // Number of drivers to report if (system->autoadd_cb) num_drivers ++; attr = ippAddStrings(system->attrs, IPP_TAG_SYSTEM, IPP_CONST_TAG(IPP_TAG_NAME), "smi2699-device-command-supported", num_drivers, NULL, NULL); for (i = 0; i < system->num_drivers; i ++) ippSetString(system->attrs, &attr, i, system->drivers[i].name); if (system->autoadd_cb) ippSetString(system->attrs, &attr, system->num_drivers, "auto"); } // smi2699-device-uri-schemes-supported _papplDeviceAddSupportedSchemes(system->attrs); // system-mandatory-printer-attributes ippAddStrings(system->attrs, IPP_TAG_SYSTEM, IPP_CONST_TAG(IPP_TAG_KEYWORD), "system-mandatory-printer-attributes", (int)(sizeof(system_mandatory_printer_attributes) / sizeof(system_mandatory_printer_attributes[0])), NULL, system_mandatory_printer_attributes); // system-settable-attributes-supported ippAddStrings(system->attrs, IPP_TAG_SYSTEM, IPP_CONST_TAG(IPP_TAG_KEYWORD), "system-settable-attributes-supported", (int)(sizeof(system_settable_attributes_supported) / sizeof(system_settable_attributes_supported[0])), NULL, system_settable_attributes_supported); } // // 'sighup_handler()' - SIGHUP handler // static void sighup_handler(int sig) // I - Signal (ignored) { (void)sig; restart_logging = true; } // // 'sigterm_handler()' - SIGTERM handler. // static void sigterm_handler(int sig) // I - Signal (ignored) { (void)sig; sigterm_time = time(NULL); } pappl-1.0.3/pappl/system.h000066400000000000000000000252671403603036100154500ustar00rootroot00000000000000// // Public system header file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _PAPPL_SYSTEM_H_ # define _PAPPL_SYSTEM_H_ // // Include necessary headers... // # include "base.h" # include "log.h" // // C++ magic... // # ifdef __cplusplus extern "C" { # endif // __cplusplus // // Types... // typedef struct pappl_pr_driver_s // Printer driver information { const char *name; // Driver name const char *description; // Driver description (usually the make and model) const char *device_id; // IEEE-1284 device ID void *extension; // Extension data pointer } pappl_pr_driver_t; enum pappl_soptions_e // System option bits { PAPPL_SOPTIONS_NONE = 0x0000, // No options PAPPL_SOPTIONS_DNSSD_HOST = 0x0001, // Use hostname in DNS-SD service names instead of serial number/UUID PAPPL_SOPTIONS_MULTI_QUEUE = 0x0002, // Support multiple printers PAPPL_SOPTIONS_RAW_SOCKET = 0x0004, // Accept jobs via raw sockets PAPPL_SOPTIONS_USB_PRINTER = 0x0008, // Accept jobs via USB for default printer (embedded Linux only) PAPPL_SOPTIONS_WEB_INTERFACE = 0x0010, // Enable the standard web pages PAPPL_SOPTIONS_WEB_LOG = 0x0020, // Enable the log file page PAPPL_SOPTIONS_WEB_NETWORK = 0x0040, // Enable the network settings page PAPPL_SOPTIONS_WEB_REMOTE = 0x0080, // Allow remote queue management (vs. localhost only) PAPPL_SOPTIONS_WEB_SECURITY = 0x0100, // Enable the user/password settings page PAPPL_SOPTIONS_WEB_TLS = 0x0200 // Enable the TLS settings page }; typedef unsigned pappl_soptions_t; // Bitfield for system options typedef struct pappl_version_s // Firmware version information { char name[64]; // "xxx-firmware-name" value char patches[64]; // "xxx-firmware-patches" value char sversion[64]; // "xxx-firmware-string-version" value unsigned short version[4]; // "xxx-firmware-version" value } pappl_version_t; // // Callback function types... // typedef const char *(*pappl_pr_autoadd_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data); // Auto-add callback typedef void (*pappl_pr_create_cb_t)(pappl_printer_t *printer, void *data); // Printer creation callback typedef bool (*pappl_pr_driver_cb_t)(pappl_system_t *system, const char *driver_name, const char *device_uri, const char *device_id, pappl_pr_driver_data_t *driver_data, ipp_t **driver_attrs, void *data); // Driver callback function typedef bool (*pappl_mime_filter_cb_t)(pappl_job_t *job, pappl_device_t *device, void *data); // Filter callback function typedef bool (*pappl_ipp_op_cb_t)(pappl_client_t *client, void *data); // IPP operation callback function typedef const char *(*pappl_mime_cb_t)(const unsigned char *header, size_t headersize, void *data); // MIME typing callback function typedef void (*pappl_printer_cb_t)(pappl_printer_t *printer, void *data); // Printer iterator callback function typedef bool (*pappl_resource_cb_t)(pappl_client_t *client, void *data); // Dynamic resource callback function typedef bool (*pappl_save_cb_t)(pappl_system_t *system, void *data); // Save callback function // // Functions... // extern void papplSystemAddLink(pappl_system_t *system, const char *label, const char *path_or_url, pappl_loptions_t options) _PAPPL_PUBLIC; extern bool papplSystemAddListeners(pappl_system_t *system, const char *name) _PAPPL_PUBLIC; extern void papplSystemAddMIMEFilter(pappl_system_t *system, const char *srctype, const char *dsttype, pappl_mime_filter_cb_t cb, void *data) _PAPPL_PUBLIC; extern void papplSystemAddResourceCallback(pappl_system_t *system, const char *path, const char *format, pappl_resource_cb_t cb, void *data) _PAPPL_PUBLIC; extern void papplSystemAddResourceData(pappl_system_t *system, const char *path, const char *format, const void *data, size_t datalen) _PAPPL_PUBLIC; extern void papplSystemAddResourceDirectory(pappl_system_t *system, const char *basepath, const char *directory) _PAPPL_PUBLIC; extern void papplSystemAddResourceFile(pappl_system_t *system, const char *path, const char *format, const char *filename) _PAPPL_PUBLIC; extern void papplSystemAddResourceString(pappl_system_t *system, const char *path, const char *format, const char *data) _PAPPL_PUBLIC; extern void papplSystemAddStringsData(pappl_system_t *system, const char *path, const char *language, const char *data) _PAPPL_PUBLIC; extern void papplSystemAddStringsFile(pappl_system_t *system, const char *path, const char *language, const char *filename) _PAPPL_PUBLIC; extern void papplSystemCleanJobs(pappl_system_t *system) _PAPPL_PUBLIC; extern pappl_system_t *papplSystemCreate(pappl_soptions_t options, const char *name, int port, const char *subtypes, const char *spooldir, const char *logfile, pappl_loglevel_t loglevel, const char *auth_service, bool tls_only) _PAPPL_PUBLIC; extern void papplSystemDelete(pappl_system_t *system) _PAPPL_PUBLIC; extern pappl_printer_t *papplSystemFindPrinter(pappl_system_t *system, const char *resource, int printer_id, const char *device_uri) _PAPPL_PUBLIC; extern char *papplSystemGetAdminGroup(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern const char *papplSystemGetAuthService(pappl_system_t *system) _PAPPL_PUBLIC; extern pappl_contact_t *papplSystemGetContact(pappl_system_t *system, pappl_contact_t *contact) _PAPPL_PUBLIC; extern int papplSystemGetDefaultPrinterID(pappl_system_t *system) _PAPPL_PUBLIC; extern char *papplSystemGetDefaultPrintGroup(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplSystemGetDNSSDName(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern const char *papplSystemGetFooterHTML(pappl_system_t *system) _PAPPL_PUBLIC; extern char *papplSystemGetGeoLocation(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplSystemGetHostname(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplSystemGetLocation(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern pappl_loglevel_t papplSystemGetLogLevel(pappl_system_t *system) _PAPPL_PUBLIC; extern size_t papplSystemGetMaxLogSize(pappl_system_t *system) _PAPPL_PUBLIC; extern char *papplSystemGetName(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern int papplSystemGetNextPrinterID(pappl_system_t *system) _PAPPL_PUBLIC; extern pappl_soptions_t papplSystemGetOptions(pappl_system_t *system) _PAPPL_PUBLIC; extern char *papplSystemGetOrganization(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplSystemGetOrganizationalUnit(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern char *papplSystemGetPassword(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern int papplSystemGetPort(pappl_system_t *system) _PAPPL_PUBLIC; extern const char *papplSystemGetServerHeader(pappl_system_t *system) _PAPPL_PUBLIC; extern char *papplSystemGetSessionKey(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern bool papplSystemGetTLSOnly(pappl_system_t *system) _PAPPL_PUBLIC; extern const char *papplSystemGetUUID(pappl_system_t *system) _PAPPL_PUBLIC; extern int papplSystemGetVersions(pappl_system_t *system, int max_versions, pappl_version_t *versions) _PAPPL_PUBLIC; extern char *papplSystemHashPassword(pappl_system_t *system, const char *salt, const char *password, char *buffer, size_t bufsize) _PAPPL_PUBLIC; extern bool papplSystemIsRunning(pappl_system_t *system) _PAPPL_PUBLIC; extern bool papplSystemIsShutdown(pappl_system_t *system) _PAPPL_PUBLIC; extern void papplSystemIteratePrinters(pappl_system_t *system, pappl_printer_cb_t cb, void *data) _PAPPL_PUBLIC; extern bool papplSystemLoadState(pappl_system_t *system, const char *filename) _PAPPL_PUBLIC; extern const char *papplSystemMatchDriver(pappl_system_t *system, const char *device_id) _PAPPL_PUBLIC; extern void papplSystemRemoveLink(pappl_system_t *system, const char *label) _PAPPL_PUBLIC; extern void papplSystemRemoveResource(pappl_system_t *system, const char *path) _PAPPL_PUBLIC; extern void papplSystemRun(pappl_system_t *system) _PAPPL_PUBLIC; extern bool papplSystemSaveState(pappl_system_t *system, const char *filename) _PAPPL_PUBLIC; extern void papplSystemSetAdminGroup(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetContact(pappl_system_t *system, pappl_contact_t *contact) _PAPPL_PUBLIC; extern void papplSystemSetDefaultPrinterID(pappl_system_t *system, int default_printer_id) _PAPPL_PUBLIC; extern void papplSystemSetDefaultPrintGroup(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetDNSSDName(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetFooterHTML(pappl_system_t *system, const char *html) _PAPPL_PUBLIC; extern void papplSystemSetGeoLocation(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetHostname(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetLocation(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetLogLevel(pappl_system_t *system, pappl_loglevel_t loglevel) _PAPPL_PUBLIC; extern void papplSystemSetMaxLogSize(pappl_system_t *system, size_t maxSize) _PAPPL_PUBLIC; extern void papplSystemSetMIMECallback(pappl_system_t *system, pappl_mime_cb_t cb, void *data) _PAPPL_PUBLIC; extern void papplSystemSetNextPrinterID(pappl_system_t *system, int next_printer_id) _PAPPL_PUBLIC; extern void papplSystemSetOperationCallback(pappl_system_t *system, pappl_ipp_op_cb_t cb, void *data) _PAPPL_PUBLIC; extern void papplSystemSetOrganization(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetOrganizationalUnit(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetPassword(pappl_system_t *system, const char *hash) _PAPPL_PUBLIC; extern void papplSystemSetPrinterDrivers(pappl_system_t *system, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_create_cb_t create_cb, pappl_pr_driver_cb_t driver_cb, void *data) _PAPPL_PUBLIC; extern void papplSystemSetSaveCallback(pappl_system_t *system, pappl_save_cb_t cb, void *data) _PAPPL_PUBLIC; extern void papplSystemSetUUID(pappl_system_t *system, const char *value) _PAPPL_PUBLIC; extern void papplSystemSetVersions(pappl_system_t *system, int num_versions, pappl_version_t *versions) _PAPPL_PUBLIC; extern void papplSystemShutdown(pappl_system_t *system) _PAPPL_PUBLIC; // // C++ magic... // # ifdef __cplusplus } # endif // __cplusplus #endif // !_PAPPL_SYSTEM_H_ pappl-1.0.3/pappl/test.html000066400000000000000000000401521403603036100156060ustar00rootroot00000000000000 Test Page for PAPPL Stylesheet

Example Printer

Make and Model, Location
Organization
Idle, No jobs.


Make and Model, Location
Organization
Printing, 1 job, Media Low.


Make and Model, Location
Organization
Stopped, 2 jobs, Media Jam.


List

Job IDNameStatus
5Green Eggs and HamPendingCancel
4Blue FishPrinting at 12:37pmCancel
3Red FishCompleted at 12:36pm
2Two FishCompleted at 12:35pm
1One FishCompleted at 12:34pm

Form Change

Geo-Location: x
Main Tray Media:
IPv4 Address: Automatic
Manual

Meters

Cyan
Magenta
Yellow
Black
Waste Ink

Grid-Based Style Sheet

The following should collapse to a single column on small screened devices.

C-12
C-11
C-1
C-10
C-2
C-9
C-3
C-8
C-4
C-8
C-2
C-2
C-7
C-5
C-6
C-6
C-6
C-3
C-3
C-6
C-2
C-2
C-2
C-5
C-5
C-2
C-4
C-4
C-4
C-4
C-4
C-2
C-2
C-3
C-3
C-3
C-3
C-2
C-2
C-2
C-2
C-2
C-2
C-1
C-1
C-1
C-1
C-1
C-1
C-1
C-1
C-1
C-1
C-1
C-1
pappl-1.0.3/pappl/util.c000066400000000000000000000064471403603036100150730ustar00rootroot00000000000000// // Utility functions for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include "base-private.h" #ifdef HAVE_SYS_RANDOM_H # include #endif // HAVE_SYS_RANDOM_H // // Local functions... // static int filter_cb(_pappl_ipp_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr); // // '_pappl_strlcpy()' - Safely copy a C string. // #ifndef HAVE_STRLCPY size_t _pappl_strlcpy(char *dst, // I - Destination buffer const char *src, // I - Source string size_t dstsize) // I - Destination size { size_t srclen = strlen(src); // Length of source string // Copy up to dstsize - 1 bytes dstsize --; if (srclen > dstsize) srclen = dstsize; memmove(dst, src, srclen); dst[srclen] = '\0'; return (srclen); } #endif // !HAVE_STRLCPY // // '_papplCopyAttributes()' - Copy attributes from one message to another. // void _papplCopyAttributes( ipp_t *to, // I - Destination request ipp_t *from, // I - Source request cups_array_t *ra, // I - Requested attributes ipp_tag_t group_tag, // I - Group to copy int quickcopy) // I - Do a quick copy? { _pappl_ipp_filter_t filter; // Filter data filter.ra = ra; filter.group_tag = group_tag; ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter); } // // '_papplGetRand()' - Return the best 32-bit random number we can. // unsigned // O - Random number _papplGetRand(void) { #ifdef HAVE_ARC4RANDOM // arc4random uses real entropy automatically... return (arc4random()); #else # ifdef HAVE_GETRANDOM // Linux has the getrandom function to get real entropy, but can fail... unsigned buffer; // Random number buffer if (getrandom(&buffer, sizeof(buffer), 0) == sizeof(buffer)) return (buffer); # elif defined(HAVE_GNUTLS_RND) // GNU TLS has the gnutls_rnd function we can use as well, but can fail... unsigned buffer; // Random number buffer if (!gnutls_rnd(GNUTLS_RND_NONCE, &buffer, sizeof(buffer))) return (buffer); # endif // HAVE_GETRANDOM // Fall back to random() seeded with the current time - not ideal, but for // our non-cryptographic purposes this is OK... static int first_time = 1; // First time we ran? if (first_time) { srandom(time(NULL)); first_time = 0; } return ((unsigned)random()); #endif // __APPLE__ } // // 'filter_cb()' - Filter printer attributes based on the requested array. // static int // O - 1 to copy, 0 to ignore filter_cb(_pappl_ipp_filter_t *filter, // I - Filter parameters ipp_t *dst, // I - Destination (unused) ipp_attribute_t *attr) // I - Source attribute { // Filter attributes as needed... #ifndef _WIN32 /* Avoid MS compiler bug */ (void)dst; #endif /* !_WIN32 */ ipp_tag_t group = ippGetGroupTag(attr); const char *name = ippGetName(attr); if ((filter->group_tag != IPP_TAG_ZERO && group != filter->group_tag && group != IPP_TAG_ZERO) || !name || (!strcmp(name, "media-col-database") && !cupsArrayFind(filter->ra, (void *)name))) return (0); return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL); } pappl-1.0.3/testsuite/000077500000000000000000000000001403603036100146545ustar00rootroot00000000000000pappl-1.0.3/testsuite/Dependencies000066400000000000000000000012531403603036100171660ustar00rootroot00000000000000pwg-driver.o: pwg-driver.c testpappl.h ../pappl/pappl.h ../pappl/device.h \ ../pappl/base.h ../pappl/system.h ../pappl/log.h ../pappl/client.h \ ../pappl/printer.h ../pappl/job.h ../pappl/mainloop.h \ ../pappl/base-private.h ../config.h label-png.h testmainloop.o: testmainloop.c testpappl.h ../pappl/pappl.h \ ../pappl/device.h ../pappl/base.h ../pappl/system.h ../pappl/log.h \ ../pappl/client.h ../pappl/printer.h ../pappl/job.h \ ../pappl/mainloop.h testpappl.o: testpappl.c ../config.h testpappl.h ../pappl/pappl.h \ ../pappl/device.h ../pappl/base.h ../pappl/system.h ../pappl/log.h \ ../pappl/client.h ../pappl/printer.h ../pappl/job.h \ ../pappl/mainloop.h pappl-1.0.3/testsuite/Makefile000066400000000000000000000026731403603036100163240ustar00rootroot00000000000000# # Test suite makefile for the Printer Application Framework # # Copyright © 2020 by Michael R Sweet # # Licensed under Apache License v2.0. See the file "LICENSE" for more # information. # include ../Makedefs OBJS = \ pwg-driver.o \ testmainloop.o \ testpappl.o TARGETS = \ testmainloop \ testpappl # Make everything all: $(TARGETS) # Clean everything clean: $(RM) -r $(OBJS) $(TARGETS) # Clean all non-distribution files distclean: clean # Update dependencies depend: $(CC) -MM $(CFLAGS) $(OBJS:.o=.c) | sed -e '1,$$s/ \/usr\/include\/[^ ]*//g' -e '1,$$s/ \/usr\/local\/include\/[^ ]*//g' >Dependencies # Install everything install: # Test everything test: testpappl $(RM) testpappl.log $(RM) -r testpappl.output $(MKDIR) testpappl.output ./testpappl -c -l testpappl.log -L debug -o testpappl.output -t all # Test suite program testpappl: testpappl.o pwg-driver.o ../pappl/libpappl.a echo Linking $@... $(CC) $(LDFLAGS) -o $@ testpappl.o pwg-driver.o ../pappl/libpappl.a $(LIBS) $(CODE_SIGN) $(CSFLAGS) -i org.msweet.pappl.$@ $@ # Mainloop test program testmainloop: testmainloop.o pwg-driver.o ../pappl/libpappl.a echo Linking $@... $(CC) $(LDFLAGS) -o $@ testmainloop.o pwg-driver.o ../pappl/libpappl.a $(LIBS) $(CODE_SIGN) $(CSFLAGS) -i org.msweet.pappl.$@ $@ # Static resource header... resheader: echo Generating $@... ../pappl/makeresheader.sh label-*.png >label-png.h # Dependencies include Dependencies pappl-1.0.3/testsuite/label-lg.png000066400000000000000000001053071403603036100170470ustar00rootroot00000000000000PNG  IHDR^qqgAMA abKGD̿IDATxwy&=.2$&$H-VD\|Ͽw?LIVItl>٦eY"w"A0`# F9cٙgggw#ڙ7= nnnnnnnnnnnnnnnnnnnnm䖠pbkWRil';ewbhfTn_ ܎-֒xnSNtK\$޿c汓6]ISnp۟BU׃_m)fᒆNtG_L )E>#)7L?"~WV3/ `UWR$~ȡg޿ci#T6$ ]t).9䑟G:+{']#)/mgmw|wW]R 4t `6)2 xΙVl s%NtU|c3JObfo2?/~Ӹ'pTR,\kSfYNL7޿c<*w,N? :13ӿ>ԕ;0K')g:!;!t=R,9,yjώw|WSٖEIxcbc)v `f9}CK+)v `l*'?C72Oqc)v `&ljgĿ\XN2wUt,NtO߃Ygpe+)v #w'b@Y}#dJ`>g W7c|횧qqwpwc)v `:ֹAY '9~^7q36ړ8;6))v `r7 9|­P|mub?0Ꚓbk_݀ܯ,,wa#8/ ôYR<ՀSӲ] %:~-؂#|NRbl,Nuc+dpEgk̿q[&H< >bڳs{N~A_7їᅌ`6.R߇;{KOGϠSpj_7rA،،㊿8 <6Kh?;?,N GW⇸V؈pO ],%8ЎVnVc, xVx͛W|\Xw :6i@u~Xƫ_c4_l-xO yhpgpoy{y؀_@jtwV-?fL8йۿK1o0nh赕zLL4*6i+ `2 U\܌exE܁!l±k+pg o6lO qubghFN"t)p//]Ml:\+qn] z|xh(n&]G:>`spoRy/VRaqzx?`(v *\|܋a܌}rcN0^C}]'tc{>|T+j@v__$9(*5oOxqa7- >h}j(Ã/ =T їGwJ )=Cժz*3e>!܈B1OaAal& Hm~B|f TP֔@j6S)m)ԭ-a,~~g 7 Fz?t $x /:TGekJD p Y!:-|#/M~_ cSx?r ?Y c"qEF`#6q8E{QF* )ַ!2_10!A\9U؋؀m_@Kc7J5KJSӶsS\a&7q0!|c6)J(X*R$)iy枓SeS;ƽ(<X_Hp0j=S]U8'  qh*PDnCqWOSkj)@1܌!ܥ% >A|~܈a܇J起c6e,lm8yO4_xEIT8Sӳ矋!q_cU`#p& sXOaab#f .B]ř,g 1(R):N4Cjs;R7[1ƴ:zQXk a5>1}؈ x;>M<u0e80 \{֫ To<>A|+0b<ڙz'el&< m(`d)̷Ux|cӪ+a G >A\8)9b*S1A| 1w|u?$ !7@vhL5VȀVUpq3{WaU¯_ĝ jWbj  KsBH4NiWTt8I{{iUb7a7. C+ øY/Xf5ưp|7͚eR~;)m 5&@&Ť]'0p#0&c7aoM ĵ?R]ص?4RNs\^,k@gaUa7g1j, p#v^ |21!l]u|/0Ywaj1So~baKJv`Cx8cL_n02ΟA{0Bj @_N/Vd-&/$XUnÙW! cy9WaL 5~CƎ++S_P@wȯ6d(9܏4:m20r< WbN 5~CJNH6 }|9Tu|P5Sm<^ ~oP:^Xb4I= OTL~֟J2y䐟.w1x,v lb_ 4Ox{p8E ` _2JL\㏇€Pw b0fޏZ H6`wu錭x0yLqJNdkd_G&CZ _4Aݴ_v(6jcJtQ[n$ߓn ?P)~3^Db8R b=kӯ G>A\2!?v_G4)+5oඦq~XWHop1aW nwg(;>/'hYslxx[ b aDq.>A\g]W7xF: *8@&h lxA ǰAWa=]Mߘo~ 1)uq||WC@NS)c9@ڿo~9U{DWa?4FUbx(lۗl.GK#@J x/X1iOsbx~|c)>pӀ0jHF#k@?_(og|yUf c=~sdiW?b9;^)9_p klNz1~n[SǛ&He F<6xǑٍoyp ?S)gۆXbZa#x-\|TptNM-1;4N9<k_oċWzk۴Yga靭x'~q `S}ߏ)*-  ⊦7 <Nd]I#xxbn01+cE'k<|Nd6*D"f qe6/)h@Rxx&D/=iPpNK}ߗ:@VmԶ4^|<%p#q*ގA&7v/?SoWo c˗>x]Rg +Ng6<| JSMddM</%`⷏6`-zc%iU+~װQ'4,?' ~p1MO1zL b=MoV|px|ǝHsA|ث:k;dr3$~al b@Pf6|Ů:?q?@۟7Lw5i3Gwb$r\A|iq >c]ou~Rv _ %kچKN?<* øY/X8zŇChO6?q_p@)mj/=;S ٨i317\sѣ˱ SIؿ%j bY_!};>!܈]תO6 56ſZ7j}~j8'ob Çp5j2p'? i3Q c^i-޿p-ߚا⅘$wøii3) c/^HڷQvxV5N?'JPsx?ėbߏ!\/UcWB6<#J5pePc_'6sx&z{pR_R<(e L/~W9|#my!47[G?@ g;ӿi3M A ||e u$S_G~zN\?7Ӗ.kOu>Ti3cn4e<_4\':ͷpjqӏOj|" -6~o9<_r ? m|?7&I2_Op=2Oj|iHn>ƼSMlz /r後zs.5TN7b1@_?.6/+6?~?O\O/AIn@y @olTm {-~%E G cǚӀSpJ8oy䐟aǾ Җ/4 )p*77c5+)JT߫y}~vc/$~`Kq5W"Imb??9&Pݬ۟߇_M՗t~܎k᫸OR/%y^QELZX[;u\X=JGwz܌j\ zw3)Zޓ[x^/5QTU$ ͙/B*c(˸̈grnE\u6]>O#˂" ʵ3_H$ZM4}͝3l T7qο3iWM|=s< b|Ed3i˯* oZ9=+~vojhUjsb>|?! z13KU?kWvYЦO}< R|BZSDPsp~뭾spai0+Hoy~ME ~W0R?9Y{NgqqenqwƻM!XS_nԂukcK|O1mQ/*I˜|~m*}?9܈!.cދ@;S_{vECrC$RBX fa6%܂э $[N&(4;.i,n{qKTKomO̭$濤t(,fek[.P/4׿ˋW(>Jw~{zM6U}-9|)8hj^,xp@ a- It7*6M }1M8-<;plOՁ3PtMIѺ\v31uSsXp nr3Rh >2[ٱp_\ fSr-:?}A1TR^{.9sx{[vTl\:jU@g]'C_Rjh}ݑkz}<IY4YBL@b)onIsM^wWR} Q'? ߩ @R~}2Rjȋ}ߑe~( ;Q]a=BHRINDԲ~oljeJ66hZJџ?i'<1h)nG[P[ KWHA>ֵiTyj/?ߑK%@$iSY*+78<]}  :b+s~@ IC#qU[6OrT^vuVor~ s F@M;f%6kHAlKeZ@ 0B.XLu܊Zǻyn~ɁKmȥA|Yм必uC=ЗB)lJ.p.znfojxŬFr-})Xzj_?Pceǽx3VE|.R_'WSܟ񤠌Xo]B9U}F HQ:^{ ؆cnTIs/ЈWF*?}+ %-ZV[iQiW؂-8 p;&layy{m9 }  ޾i>Pb(K )\SJe Dq;Vc֢qd܌v| .2#MY13n&OZձ"Մ-I<p <ӾqlVp!vΠnŗ;)xAdRPD%@r܃&>g- ))_mit\cX pn#X 0<`b=v:h*Wg~:qLbPDeQ) ma%P5RQ=TԜ'UE꯮'Ii\̆؍ kp6S{g"? !E)k. %&ޭւ=y igg/O7>Ord>Yi$vXLV_p6bz/ p: fx8.Uz ye!-Vğ1oOYU,^XGmDJ3Ɓ&E =ŅX(8>ǔ5%IܥX'mnkk$ (*3`:ˬ"<%Z{ i8)VٰrlD==׋|}e1b-Nc^ċ\gxP ;GV}8\'ټ?$7<=U_mk0 =>7 4C^"jЋ x;0a-C)ZƳx}밪CGgQ2ӱ 'ϭcXךYqWhsr_5VJjH<'-V0 e5aOIXe/I<~:VL{x_oM4W;d._h]TYk_w%:QQSЋmb"9U{|026`S{p7Z@@2*oXrI+xbP8p 'o$Ǘ{e;zU_2`A7&YXJko/3= =Ri6 D fdG@ox soAo)gk oG9{>'6_E9(3H3(.~%%/:6v!`ۇ@qO)pv)jB4e0 @;ϼk:=*fROU\ӧ|R ^sFc1b-C*2d"o SV͗9~('{ތ(kxg&<_Lb H113/ )%mKa -ǝa` :98S<؊SB@ 밎)4j|nLG̗a.kx@J;zZY%|*r  dXwR 7_Էه}!=8p8ͱ TxleBۇ@J]|%뻛o_s])3y޲w_,)F 8]=TWFQ6<8% xKpRS*>ʢd~-[u.Η+/d_Ze ' h/ٍI4T? ^f ;p_SdG ^sQvxH>[$рx; 5uCUfa[Nt L& 6֯* ;^ )41t+_0Լg4 GWr&>7a q_LFS|'B؂թB /9cRP/V_7?fL- P>Q| jPl-V1uq Ȏ{j饙S|ĖCQg/s}jU:}x`sxq }XXT3۽b)H;Dc3(HK@1}'mh/)FtGNtV0 IJn cl>.Up.NǏـ.8|&?b^`x8?&Bg,n)_L;ې&$kBH'2^DOs*vI"10v 8(򘇗{Ng5?tMQAeX @X-_y8G'toH/b2S-8d! ;ZL@9mG _3*SYVT'KO*^hFzbjĔqv /@Z P %)}؇{p,l}z>`+}9#@-/B퉵 /@0I8x|H Xے BxdPGgg , `a\ts`_$YO!j}$j|ЃXBFcylemG880I@j][s|fVA06K|+ Hc}weD/Ml V>_~|,d ~o]s-Fh|<$Hh<>/st,8NOKx< ?8Ӂn4h.Oy>_&<13U=M||A3_`AOOЋ/$@lԝlZT@0ёҪS=#>R%\jq}s? p 뷯GZ1ڊ3a86 6P (F-Cb|yB_|HxF WEu6 z}ZSh ػ|qwZQAY/kL>W+Ch9S+:1-m4&ɛ _>hz}*JƮ at$-c$:^ ew _i.aNcZտ`y||ov褜>S[Z"X6>o|ɱӊ< 8:1"μկ5aZC/>^_>xhi}P!'R|5oo$=ƞi`5Z%9 o>~WdB^_v}TW,_~M'™:/QLŤx gz獅@-ڳVVTZMl^bwh6H  wENj>B3U$);{Et{Nm%rb9}K"3 kY/P;cG^{g<Ntw2/RRֺ R1QȘ#BWHM瓥 Wb^2OXJzMܳ']>y?Ȳ ]?IA AW[(iFC*.^2P# Y[΂i[AA^#Q#*潝΅&"%i*O|K$@~Lf<>?,s7gNUo-qw/@|1sa[4>0NGe#|@ڔ`0Ûs; a0/6P4hbe0d_| 0:oW7ltp@>EӆGs3:;DD^=a"ai=NLz}yOAKƧ0}|O.Sɍͯ:{P6J$ݲ 7sU0&<&4pWrp|BׇS,f>F?syڿef`tp ,2 43 x:i/ `+??;>f>2Ȃ^X~!sҰ/wV-c Ƴ`9^/ oe 'ʐ{Y:(w 殉 p}l^dt)Z+D^3v|h@S)S4ȫk`ko?t|~}֔BrߛO01b~"qMFn -@k"4uK{OY~b1j0d}JO,F/" &o|q '=lw:_m7k[?H?' r}ͷMxxЗԟ/bPK|`6-DL?B|4x)V1Q#GL)LT@ >oď@MRϗޘ zWƂ| [ݩx &I > *@ > #N4/<_g V/ 9/@s˧X;ﯗ1&"wx>* ;7 @}<3f<]|(pDE08|Ă8|I#_@3| 0KO>Oλ3`[hQM~%t7H%>G[ 0q 6~t^[00 { 0S vS{lѥ U'\6x~aMH96&k۾Rs4rs!K=v'$ ?@P>~Kr,}V$aWO(HHnZ2%ZPmTG>Fj+^/r?>ltFM0fղL;3Z_>,V` }KA3crlAky/-;<֑Z ʼDȱޯ:jUCmǏ2-{13 W}\.p@tDJ|)6_%\(>ez}-0 2`& WM$1.H7c䞹TL;_@| <:#j}:OAQ+{qht>#XTǠ*:tWB3&xmtTA[*ޯ*tO_5[ H<av`h/ 99O,+zԀ^_^_BeT~9]s%əM~i[+Wlxm?#IV<2 /i'B >pPXztО Y|M_S_YɉOKJ)l,Qm| pYc_s|"f>е _=7<~C'ks'5bP+ hFu; ^Q8[_V\* f;/eɰQ<ϖAjx2(_)]_g:ݧvyU zczU0BG6 l,ţqMoZ yn5e&*xpԯOhꢶcp2G!1]ӿ}%2F ~ rj$| %G*{ &S@=E B yƤWod#QZ )oOMa(Zw}Zcu}T5 G]?Pq:Ӑi}2}|;Uȭ,! RJg07lviVS @_̬=q78C!8SK.7i9'+i?Qz.7dTp#KMS8O %[6夹@>٦+ZR[a Z|ׇgB{~:AQB\.AT热TXMKl+z4Ixj9iTF &Z]Z 3嶶"TdbL c 0F4%D^}D6)'\ǧlIcVOrc1[a Gg109+[~<u}fBy[=gIod5@x֧u>vO=_/=v(eDS7&)5`v})5ytRߖ`i"嘠дecS=1|n=עL( -*1]K^dDjzPǰ_^ e{2 =4ef1'_Cῶ% ]:30L}=$`fSjPʈ4[ @ck*Q ̭jw5quodL|a7/@5jBUOQ*ŧY| 1>B Roj( ÆY[;D_$xm ~C=Ut0>~qj݄-I`ob7f)x/-XO ݑfq/,y<UZ%?s?@H ?=(Fч yZ9UO0v&/Vgoӣ \@rb\VN>S.XFXzV)ah>_XۢT@Yp b7f"} $ScGes`;dܨz\0p`l? *Ղ|&I˜I@KG t3[Z>#OjU`jxsPp*X5R!k4z>=NuKȐHs}Ic'8_fo\(iHfeaK@+73mKf6@'Fq1V+rBK n|j\- )\x|y5>s:0w:`Ksu؂n^0ʁG ؑ%QB{7eN!=Җo{UXs}`6>o\=t&S!7 }vq!<Eb$|I2=)h @K|'7ǧ*ywO?MI>ݛ y#@}r~7sbi`.J*F^RV+8tB7`y[ & .\A&'an%E `S?ĕ^ ⹣ `;# $(Վ|Pc/Tw3aN92qn=>|ç܋/ek>U/4?jE1}S 1gd@S-l\C.y8Ps AtYf0ڄ|,G iE4ׇbXZWyYWOəx9|!,'4%|d~(M@$|QGUASY=` +l| M_RShz}v. le;d o$?7ehY)\*[4"GLg=/@XbF3A^7 GzH':9E~ $_$O`JfHjVk?{ AA@i pp|Ixd~| ZHq4wBe}Ȁ @H }.oa/% .@="RڟG|Q9G 2E@J#0t+Ɉ||ftz2@qsd@ ?3<߀*n#]@-9/PQ:|<#Q1|^Y l}wZ"R|p~ʛKDhsOӥ&Qd{*da˟ KAأ m1?؛@6% 8Pl|`kdys@ZU>K⋴Vx`2=al}a,׃xyhm|h|@J| z3}EB[y#R%ORO(w+5b7ˊ 0M?k>@/cG1) &{ϝoNF`tZ~Q 6-ko#H>bF>AY,z+TaW2)Q[r 0Gdĉfct 7rE27M/@ݾ ͧ` 0)LNJW|I$&Oȗfi8 :zd3c08ܵ%;=cH0m8#`"@{~<(ST7TU.@(h7@6ޣ(Q~=`bP @ )OAۭO( :5Z(&QF %I]0{,5x+[ Ĩ8_1oJ%!tB"/.4u_C{Z 6)ՌBs8 ~Rv_O<в|]|9'?3 K1rh7ȷa  yILЏG/@I|\`N'׿M <-w|Qw81 $Ž/T;6@EaPHANf>(!y*$Ix<3_W%_Đ/)O.#wZXwr=n^ֆM^%"߰8Ye: y( {{AĖp|`:_oP$_C [(Id| Y2dX_0]>eCsBb/atB!Fwg)h}#ġ4|<q4_煫qo ʁd|xyr5 q)>\syl=W 5!>/꛿apYR< *Ø([&/axk$зP^2j3+[M\]~{?w{/J.@EOS `.@X;>Xͩ-:}^5es"y\O <~{K&>Ys u ~5(&BrtZa B V a0}y"#^>xW7ez9 T|y&"Ψ8Ue&O&@F)w`Ķݎ?\w#k]>`dGPffB|ִxtI؇J| 4/?$-ܓ?ᗏ7#_0n9I1a TfzFa^$xzbO`s^O#/d<߆տ.<|sxna8Kt'#Hnߢ}F UۊXcx??v>xWN26k7x/1nsmbW4)ҹġ*?hޥ{02*H( ҩe;`ËDxrLe Z\6 _ɓz &,i+$[ԭ SSa^=|W(}N/iߠ3uB'"(X͆'OF!^j (,#+?ֳUpM__bNW9Jb~=IہT,߾'P\s1M0tȍ#v/'& /H|N\W͛{djb&$m1v{ /3_(&OWC F>o %4"-fpy,r _ bVYxd&JUa?|esL%? uW?Nh9=)xsD$ϟ0dV%#s|3wPezOU]xʿ@(?v]& ,X=oO>qǻS~K!Wn gOՙ!ҏrwC5d(GMHfsm Y~#>?20k̄ˆJx|j,AHgkvY'rqQߟґ:ѷUߎ `bE>}5H7<3c,dpcȗݛw7/I9ب.d`=· RU)=QMn@5t ȷkC`U;#Өun3[ xXp`A;\¬T_ v8)_t\;ɇE~mz3q3PV e0PZ[d  Oa0I 5+_V{WG؈88* XLH/@ <?oՄ^TDgm͡=q`GFK`$χ-ȿhSa|p@nWf m8{Ğ%607Gy[8 DtwBq6Lxh.]h >zyKE瞏WdU YHAJxG|Nh}?wF_G`/& tnC %;߂*_ۏ/H>uϿϰoȥ\9Gzۿ7X7ֶf`B*B+9st'B0%~ȣWG#d`ڨ!_?OnOQ+1Z.UGI 8 !Bἇ,"I A`/lU0WIǖ/0A* WC#S?|=|㬿 jZä/9nvJ|TOls5rn,'0a#NY׬(]XtbXv oqV%*KwU7fC_QѬ|=_OUWFl 3]ޡ>Um.c 7!'WsijB>H;=< .>ԿJ @3Ếғ/#Ϲ5gQaʍ6_|jy (b^LtB|9Ke)ȷ'b'/'hBsn-q}׊|]y`x>LyX>loL*Ie?%c^ќ,4%0g bN̹%g (=\ZW{ů-\B+|=M}Z3|I;г,4|mfok7 xk0x_Ɠ@$j}\x))o$^W]t{{-sűg<z}ȧo׋ÔkQ᱿(ΰ6_Ԕ@@ߟͧ𦤓IhJ>p2F`4ȟ9r|,xaqлv*04;RZ|]%[ltDZ/Oθ6_7cCczΈ閯V2x|YOb+}e Gωů.Xqk˪&Vt^b&F9G&<9|5ӽZ)PŜIks2c#fݬWkoҴ˟:=B{49߁ImPopd#W[ 8oc>&G11geZ򉕏(꤭,9hL =yR|47@v=G\cJ sbs;fr&䗊_ߣysSJ`)rxz( *hOC;|09JUhIi}Uu%}8^%al3j@(: MRJ+c e?r/D "1?҉Omf|`J.b7pK1S a/=Ac3W'*iCIB>]bggIÛSIo{O/;|brjԸGm:SH^> $_ųxnSIsGo.VyC!|l]Cž_#LKjxs8>Ԍ|Btzs3Oa <X$ؿ8h@~uYs]Z8E7Iʼ?)ɇ_ϟ3ɧ&&y>LNVR՟<Ͱ6_w[CJ ϰf%_h)"D(_NfR&Mm}IIz}|tE8tScB}>AVXǷ&_²EMFˇ/_De -igګ?CC@@\wbִ `}i0:_DtS\NM.BWӑoڅA>SPy\QmX%^/Pk@p0Fp`?<߿^0&C~&(/_ۊ|>$(S/42|u >nkB\"qIz0E/ɇRo\WYTF-W'u_۟][e_Ohp=V 2$WkHih v/|n>!#<2|un IQx{vwORz|`)u_1ɏm[~Ss,l.@E@67lnEڅͣ\'PĨ(:*_-_d ʼQĐϯ)9ͱlIVBw6_ Yb [`ր?<ϣdxLM/"tqÚ#d`K lXH ?ОW3} E+M`zI1h_Ovt`,G=UuAz f&<5~n Q Yˆx4o8U/[>9k-`G;#| xn{m1{LĚRE6Q>%odz |$2T|X~d| S>7gsNv"i ҟ]0DD@rԊ#ǕCj._Xk̇ҬՓv-=d,o.@0PZ|k8VFE&G 0fd) Txk/˳W*Q?bN!N.b9~"M,am|h wz-?zʳWg*0x6< &ԕjN>5g狃[O ߟ r 7-.tEfF-R^p6zY /gaEsK1S \ĂK"RMKrI|+|x|u x#[NK͛`llaʅ>|Dʇ:K&_VsR>4Rx;|u @*їG6n簚sDd6|]0gMԼS 錋@ZVWIO~ xAS xO4 /&z#|,6_@X..?\Yj^~8Jvk /u_UPPRʖFv7A'$> 1Lz=Z0є|KQS ! Z:*ߜ||u %z{|"i=Twd`/}ww -I"Č'(1td#~2G>ȇ!.O>$_h.Ts7[|u^D(A8m'{zpѕj]/jM6rEDk։+_°e {9W+lU"<8i1:j]:YX~fZDsm3JG>gF,5 &hB&?^F_W׍m:_}%d%b`Oyrɍ=anzaB62vXX<S$uF3*%C0|~O?mf۶Vu G }cіqF鼋_*M'n0s6%Q~M!ŸIQ6qUz[е8y6_3CK!PU%.P1}"Em7a "푏Knk)bXd% LߒۖVW )U|4)_h!sM)1#|J\>حmfP%x|ƞ=Xl>my甄ho@ɏ:m M| '-oNPjU6_3QpPBv%G&^+ܞ{,ê* [F-gym#R>7ʸ>߁eM'C3Um_b(mfA` TQAmb^|bG,`Zd$\asOCϔ6_3[AR( &^*L_,8Ƴ}W+ 1|H=OHO/qyV*udXdHFeCLCO6_O@Da.(O<[Ի3+E7dEV/:A:Nn| k}@/O|rD/><{"nА+'!mxv7qYU%>fޑX>ǧ% OweܵJou5B/^2CwO>woxWs *`X7MƧWonsGi~`܌v?B[.3,Y `X/M19HXM^^H6/x~|Nwjz8o<5a_Ww)!P[9#V6XGU<;E\ k³C7*ˏ_GxKǀå|lW|p0Iѐ_|xk\3^d&PWTǂl䋦ViLb۠khKes/t_4d(~ăóL|Š7u0t>3YbB;!%)-e@vM=/z!r^jG QrDź&Kw\O1\nt%kWFg̾0T~@9ws>FƀwNR",n ډc?m:8c.M~.,HU)Wiw>0dD!#G&w6LL=^1a M K>|vY(!Rڒk@̸@D`܃^@B03@at]>jh:>C%ٕL_.6_N$XAcM}tXhD+-%d\.?)߁' |" k@A|vC n=ciȡId<\?"TI\j-ڑ 㒈A ~}"zr E[ )/ҡɇ8V t]# v46WgXo\/R BFАW& /7< സ7v̛@m(ތ| нrw]^ 4:ߞtmH.!/[cjg1`<go@>?)-$u߬~7+?I+sޤ͗;HAC^+Q|ߘ{\ XIDAT[)}4A\}Ց~=Urm([%rQ*Qܜ{8o//$\}4BxB?χ`~sE)vԨ@,vlJ,ǝO2> 7 k 6AC^zſ{*'E!I#1{=O0Z/%GV-z͗S3hr n{77rX~je|m:@DFCO7^Su\3xy9EjfD})k G ̀(FDtoY ۛ~77~$2P)YHDC^)?lȿN'em|g3 $ '|x 2t|90+l&iox|hȣOظ|V?i#ݗڲk 6AC+=lS7>ݼOb7]8%mj볽_+V\/fC (_o^@<\Wi>riBpmyWتa@4CSW!mFn0W 9X)pm`jdE#=i?S`FqBrɵr KlfRqf8G P |9@ `l) -S:.EnTD-KNY>!^rmB[I[-^ g!'@ ) 4~I!Yrm-Q( x`cn7DD[2ޟ%)4CC.JׂiGXMK`ʴf^ѵr АˏtJo O >]/P J1"qpxKn6_NCC^ʕ=>*,IMx~OPS*͗SnIJ̉Ba!NT7 -h\qmp#-" yÞgg۰MOx3T woŵr &5*VM; B_ ~x*\/h!hj WĭSԋ3d/ %PQ͗Sna ħ!+y)'-߇zq݂ |9Fʶ"R*'&zx,*!8'|9FOCnErw7K-(C})72 $!F*`L kjkMҐ; $#q.O{r 8-`!N4=A/k_嫁kmu!_^V`.*_ \/ܘ[ ~Μ!m{pr id  _9 4yFPFp|9ƴMѐ-4"VS_R>#շkl Ģ!MN, O΄aw@AyeF|94qiբrPX|%ݵ|܇ƍt?м?^Oԟ|@Z^y SrYntC :n<K>ˮ͗d[S)K rE*q2T_/WAf?Ynt-JD`'6agI@㡭_6$wm T)րyƕX$@o‡J(~]/`5`J`Jx;*qo "1pm8EՋDMU毸>?Nj(m)'_o]N12 %Pԓ}Sppc9 6:f;)7f-.WWA3p)7f-)B 1%n;l`oXBПN1$9 ZſN1lm(WN1Km0 w)7fA0˧N1@'(pkp 7p 7p 7p 7p 7p 7p 7p 7p 7p 7p 7p 7p 7?u *8eXIfMM*i(0%tEXtdate:create2020-12-03T15:47:23+00:00o%tEXtdate:modify2020-12-03T15:47:23+00:002putEXtexif:ExifOffset26SetEXtexif:PixelXDimension512.tEXtexif:PixelYDimension512+!YIENDB`pappl-1.0.3/testsuite/label-md.png000066400000000000000000000133621403603036100170440ustar00rootroot00000000000000PNG  IHDRi7@gAMA abKGD̿IDATx]{te~2d22פ=MkSPʥ E]nbP\Q xY*ꮫz9 U9v)"[D-Py~IfN.͛{~G֑udYG^upުAuo-Coױf_dV=O0|p=&ъne[ߵ`w-80h'?R3D ^ F@K]twdzci1`7_ W> C ?`5nTk&W}hc+up# ܢ,N7s8/0>Q/ t^{uZ.P.^y ]Wo7Zm=a-d_7[y˂o5y@}1|ˢ2|c.fzi}{18ˑV _RE/LSg`zܧ1xN*:<)~4.n*+W?PIFK9Yx%j~SU"޹TԆqY*/1Uc_n_z"RE..;߿,~_sϔXd8=;B%J$߸kcTDO51s0. rCB=4 rr"ȅ6p+?nrR"eDp?ND‹)Z8 qL1¨f 8jQrJѤ i.8M̉>m-uRRsF" ƦS'8qQ ]1U =q͐/ )edׅaCPv,J8g~{CѵNsJ1äƅ՚.\/^ m>]HX"u w5tC,Mc:jZNsZ1'B6Ð .]2fJ/.;rO9~.űh2=gviW;!^kijrڧu]#ja@ scYPf$ڬCZ`g-mic#5/^7&}x|f.L(Z/ Nk9Lly( \%zxMךĊ+%ٹspmD9㰭hқn[]my(˿-C6X&JrmJ%PyN5'WϮyvR4sP4 9pEP4tml{:\Fv[m3hVfn (U2L25Lj\tj3PHYry͓OO9°Qp#YyLȽGc.nH[-s5 巜&izv]% zX-?| Fu`]KUmL`v'Ҽ;\Ƌq8}hYn BNn3lߙ* vL9"c(ѯC !|]seR誚'ԼI;b'g\ 7"я+VOvea' VIuaǴC1ZVNC<_I;[w+=is]ZeQkTE\J\14p!f )TctE3hd'^QgCOx.ĉDbmE_r_,|&lZG՛!="?y.;0@Ƅ3|.jJmq-uoKp!abCl჈2Īb$~P/Do#p6 {Ƴ2|_ "ȣ (jv{tc[ <N*X:!EAύAxiGZ9lqEG߆9 @kp:ƑCPoǐ&9+vѷ ULA2 g.?]<;\ &0>y x&Q,MhEFG/72 hp$ ԯ|woKWpP<[1vtc}=;C-8kPDp,^σ[p2& s0ȡ#rQ%Ђ4~+7s!7.YG֑ud0 (~*8eXIfMM*ikF&M%tEXtdate:create2020-12-03T15:47:23+00:00o%tEXtdate:modify2020-12-03T15:47:23+00:002putEXtexif:ExifOffset26SetEXtexif:PixelXDimension128zߪtEXtexif:PixelYDimension128KIENDB`pappl-1.0.3/testsuite/label-png.h000066400000000000000000005240511403603036100166750ustar00rootroot00000000000000/* label-lg.png */ static unsigned char label_lg_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82, 0,0,2,0,0,0,2,0,8,4,0,0,0,94,113,28, 113,0,0,4,25,105,67,67,80,107,67,71,67,111,108,111, 114,83,112,97,99,101,71,101,110,101,114,105,99,82,71,66, 0,0,56,141,141,85,93,104,28,85,20,62,187,115,103,35, 36,206,83,108,52,133,116,168,63,13,37,13,147,86,52,161, 180,186,127,221,221,54,110,150,73,54,218,34,232,100,246,238, 206,152,201,206,56,51,187,253,161,79,69,80,124,49,234,155, 20,196,191,183,128,32,40,245,15,219,62,180,47,149,10,37, 218,212,32,40,62,180,248,131,80,232,139,166,235,153,59,51, 153,105,186,177,222,101,238,124,243,157,239,158,123,238,185,103, 239,5,232,185,170,88,150,145,20,1,22,154,174,45,23,50, 226,115,135,143,136,61,43,144,132,135,160,23,6,161,87,81, 29,43,93,169,76,2,54,79,11,119,181,91,223,67,194,123, 95,217,213,221,254,159,173,183,70,29,21,32,113,31,98,179, 230,168,11,136,143,1,240,167,85,203,118,1,122,250,145,31, 63,234,90,30,246,98,232,183,49,64,196,47,122,184,225,99, 215,195,115,62,126,141,105,102,228,44,226,211,136,5,85,83, 106,136,151,16,143,204,197,248,70,12,251,49,176,214,95,160, 77,106,235,170,232,229,162,98,155,117,221,160,177,112,239,97, 254,159,109,193,104,133,243,109,195,167,207,153,159,62,132,239, 97,92,251,43,53,37,231,225,81,196,75,170,146,159,70,252, 8,226,107,109,125,182,28,224,219,150,155,145,17,63,6,144, 220,222,154,175,166,17,239,68,92,172,219,7,170,190,159,164, 173,181,138,33,126,231,132,54,243,44,226,45,136,207,55,231, 202,83,193,216,171,170,147,197,156,193,118,196,183,53,90,242, 242,59,4,192,137,186,91,154,241,199,114,251,109,83,158,242, 231,229,234,53,154,203,123,121,68,252,250,188,121,72,246,125, 114,159,57,237,233,124,232,243,132,150,45,7,252,165,151,148, 131,21,196,131,136,127,161,70,65,246,231,226,254,177,220,74, 16,3,25,106,26,229,73,127,46,146,163,14,91,47,227,93, 109,166,232,207,75,12,23,55,212,31,75,22,235,250,129,82, 160,255,68,179,139,114,128,175,89,6,171,81,140,141,79,218, 45,185,234,235,249,81,197,206,23,124,159,124,133,54,171,129, 127,190,13,179,9,5,40,152,48,135,189,10,77,88,3,17, 100,40,64,6,223,22,216,104,169,131,14,6,50,20,173,20, 25,138,95,161,102,23,27,231,192,60,242,58,180,153,205,193, 190,194,148,254,200,200,95,131,249,184,206,180,42,100,135,62, 130,22,178,26,252,142,172,22,211,101,241,171,133,92,99,19, 63,126,44,55,2,63,38,25,32,18,217,131,207,94,50,73, 246,145,113,50,1,34,121,138,60,77,246,147,28,178,19,100, 239,250,216,74,108,69,94,60,55,214,253,188,140,51,82,166, 155,69,221,57,180,187,160,96,255,51,42,76,92,83,215,172, 44,14,182,134,35,203,41,251,5,93,189,252,198,95,177,92, 233,44,55,81,182,226,25,157,186,87,206,249,95,249,235,252, 50,246,43,252,106,164,224,127,228,87,241,183,114,199,90,204, 187,178,76,195,236,108,88,115,119,85,26,109,6,227,22,240, 209,153,197,137,197,29,243,113,241,228,87,15,70,126,150,201, 153,231,175,244,93,60,89,111,46,14,70,172,151,5,250,106, 249,86,25,78,141,68,172,244,131,244,135,180,44,189,39,125, 40,253,198,189,205,125,202,125,205,125,206,125,193,93,2,145, 59,203,157,227,190,225,46,112,31,115,95,198,246,106,243,26, 90,223,123,22,121,24,183,103,233,150,107,172,74,33,35,108, 21,30,22,114,194,54,225,81,97,50,242,39,12,8,99,66, 81,216,129,150,173,235,251,22,159,47,158,61,29,14,99,31, 230,167,251,92,190,46,86,1,137,251,177,2,244,77,254,85, 85,84,233,112,148,41,29,86,111,77,56,190,65,19,140,36, 67,100,140,148,54,84,247,184,87,243,161,34,149,79,229,82, 105,16,83,59,83,19,169,177,212,65,15,135,179,166,118,160, 109,2,251,252,29,213,169,110,178,82,234,210,99,222,125,2, 89,211,58,110,235,13,205,21,119,75,210,147,98,26,175,54, 42,150,154,234,232,136,168,24,134,200,76,142,104,83,135,218, 109,90,27,5,239,222,244,143,244,155,50,187,15,19,91,46, 71,156,251,12,192,190,63,241,236,251,46,226,142,180,0,150, 28,128,129,199,35,110,24,207,202,7,222,5,56,243,132,218, 178,219,193,29,145,72,124,11,224,212,247,236,246,191,250,50, 120,126,253,212,233,220,196,115,172,231,45,128,181,55,59,157, 191,223,239,116,214,62,64,255,171,0,103,141,127,1,160,159, 124,85,3,92,11,239,0,0,0,56,101,88,73,102,77,77, 0,42,0,0,0,8,0,1,135,105,0,4,0,0,0,1, 0,0,0,26,0,0,0,0,0,2,160,2,0,4,0,0, 0,1,0,0,2,0,160,3,0,4,0,0,0,1,0,0, 2,0,0,0,0,0,40,48,151,191,0,0,3,99,105,84, 88,116,88,77,76,58,99,111,109,46,97,100,111,98,101,46, 120,109,112,0,0,0,0,0,60,120,58,120,109,112,109,101, 116,97,32,120,109,108,110,115,58,120,61,34,97,100,111,98, 101,58,110,115,58,109,101,116,97,47,34,32,120,58,120,109, 112,116,107,61,34,88,77,80,32,67,111,114,101,32,53,46, 52,46,48,34,62,10,32,32,32,60,114,100,102,58,82,68, 70,32,120,109,108,110,115,58,114,100,102,61,34,104,116,116, 112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,49, 57,57,57,47,48,50,47,50,50,45,114,100,102,45,115,121, 110,116,97,120,45,110,115,35,34,62,10,32,32,32,32,32, 32,60,114,100,102,58,68,101,115,99,114,105,112,116,105,111, 110,32,114,100,102,58,97,98,111,117,116,61,34,34,10,32, 32,32,32,32,32,32,32,32,32,32,32,120,109,108,110,115, 58,100,99,61,34,104,116,116,112,58,47,47,112,117,114,108, 46,111,114,103,47,100,99,47,101,108,101,109,101,110,116,115, 47,49,46,49,47,34,10,32,32,32,32,32,32,32,32,32, 32,32,32,120,109,108,110,115,58,73,112,116,99,52,120,109, 112,69,120,116,61,34,104,116,116,112,58,47,47,105,112,116, 99,46,111,114,103,47,115,116,100,47,73,112,116,99,52,120, 109,112,69,120,116,47,50,48,48,56,45,48,50,45,50,57, 47,34,10,32,32,32,32,32,32,32,32,32,32,32,32,120, 109,108,110,115,58,112,104,111,116,111,115,104,111,112,61,34, 104,116,116,112,58,47,47,110,115,46,97,100,111,98,101,46, 99,111,109,47,112,104,111,116,111,115,104,111,112,47,49,46, 48,47,34,62,10,32,32,32,32,32,32,32,32,32,60,100, 99,58,99,114,101,97,116,111,114,62,10,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,83,101,113,62, 10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 60,114,100,102,58,108,105,62,77,105,99,104,97,101,108,32, 83,119,101,101,116,60,47,114,100,102,58,108,105,62,10,32, 32,32,32,32,32,32,32,32,32,32,32,60,47,114,100,102, 58,83,101,113,62,10,32,32,32,32,32,32,32,32,32,60, 47,100,99,58,99,114,101,97,116,111,114,62,10,32,32,32, 32,32,32,32,32,32,60,100,99,58,114,105,103,104,116,115, 62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,114, 100,102,58,65,108,116,62,10,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,108,105,32,120, 109,108,58,108,97,110,103,61,34,120,45,100,101,102,97,117, 108,116,34,62,67,111,112,121,114,105,103,104,116,32,50,48, 49,57,32,77,105,99,104,97,101,108,32,83,119,101,101,116, 60,47,114,100,102,58,108,105,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,60,47,114,100,102,58,65,108,116,62, 10,32,32,32,32,32,32,32,32,32,60,47,100,99,58,114, 105,103,104,116,115,62,10,32,32,32,32,32,32,32,32,32, 60,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,78,101,119,32,73,99,111,110,60,47, 73,112,116,99,52,120,109,112,69,120,116,58,72,101,97,100, 108,105,110,101,62,10,32,32,32,32,32,32,32,32,32,60, 112,104,111,116,111,115,104,111,112,58,72,101,97,100,108,105, 110,101,62,78,101,119,32,73,99,111,110,60,47,112,104,111, 116,111,115,104,111,112,58,72,101,97,100,108,105,110,101,62, 10,32,32,32,32,32,32,60,47,114,100,102,58,68,101,115, 99,114,105,112,116,105,111,110,62,10,32,32,32,60,47,114, 100,102,58,82,68,70,62,10,60,47,120,58,120,109,112,109, 101,116,97,62,10,221,242,56,27,0,0,136,242,73,68,65, 84,120,218,236,189,105,152,28,197,149,46,252,70,86,117,183, 122,213,46,33,196,34,1,66,18,8,12,24,240,138,61,198, 120,199,44,234,70,120,27,207,238,103,54,143,239,60,119,190, 59,51,159,231,218,247,142,237,241,246,217,30,251,218,227,229, 122,240,12,94,198,136,110,36,36,192,160,21,16,2,1,98, 19,59,18,8,132,214,214,174,222,107,201,243,253,168,45,35, 226,68,102,86,85,102,117,117,87,4,143,161,93,167,59,35, 43,50,207,137,179,189,111,0,118,216,97,135,29,118,216,97, 135,29,118,216,97,135,29,118,216,97,135,29,118,216,97,135, 29,118,216,97,135,29,118,216,97,135,29,118,216,97,135,29, 118,216,97,135,29,118,216,97,135,29,118,216,97,135,29,118, 216,97,135,29,118,216,97,135,29,118,216,97,135,29,118,216, 97,135,29,118,216,97,135,29,118,216,97,135,29,118,216,97, 135,29,118,216,97,135,29,118,216,97,135,29,118,216,97,71, 77,135,176,75,208,64,207,148,236,226,216,33,143,164,93,130, 73,168,248,193,102,221,154,2,59,172,7,48,9,159,163,40, 254,44,12,74,79,30,245,183,102,192,122,0,118,9,38,137, 250,203,255,83,141,128,172,252,36,41,191,53,3,118,231,176, 99,130,63,193,130,218,139,188,242,11,143,47,0,143,242,23, 254,205,153,2,107,8,172,1,176,99,130,170,191,252,143,35, 249,1,5,245,46,41,63,121,140,128,53,3,246,5,178,99, 194,187,254,5,181,247,254,79,55,0,165,127,92,197,8,144, 20,38,88,51,96,13,128,29,19,72,253,75,106,95,248,71, 180,53,15,103,61,38,64,85,126,87,49,8,124,88,96,205, 128,53,0,118,212,185,250,59,249,221,223,129,131,4,156,142, 38,231,3,180,28,87,97,182,120,197,189,71,108,74,63,55, 90,218,231,221,226,255,74,70,192,181,97,129,53,0,118,76, 100,245,119,32,144,128,131,4,18,93,31,197,255,166,133,148, 23,18,0,113,68,220,75,155,199,30,79,141,122,20,191,240, 15,25,13,129,53,3,214,0,216,49,65,212,63,183,247,39, 144,232,248,107,241,101,210,30,41,65,128,70,157,205,180,41, 243,240,200,49,143,250,7,25,2,72,255,182,97,129,53,0, 118,212,205,83,211,213,63,217,249,71,248,102,65,225,229,127, 151,108,129,216,78,27,178,91,134,95,135,11,23,89,131,17, 112,67,100,7,172,33,176,6,192,142,186,81,255,36,18,93, 55,187,63,16,194,171,242,37,19,160,154,4,241,58,221,231, 62,152,222,145,74,27,188,1,155,29,176,6,192,142,186,86, 127,201,245,71,178,243,227,248,25,28,72,170,175,27,3,89, 46,78,184,235,177,121,108,123,122,128,245,6,248,236,128,45, 26,90,3,96,199,184,71,255,165,130,95,18,9,36,219,175, 113,254,19,201,210,94,207,7,0,6,121,26,91,104,83,102, 235,232,33,197,8,232,217,129,194,127,97,139,134,214,0,216, 49,190,234,95,200,251,39,145,236,184,74,252,154,90,4,224, 179,219,19,2,229,59,176,62,187,37,179,51,149,85,242,3, 196,24,2,155,29,176,6,192,142,250,80,255,174,43,220,149, 162,45,183,187,67,138,247,75,197,64,129,208,242,3,184,215, 189,63,245,76,122,140,241,7,204,105,66,155,29,176,6,192, 142,26,170,127,62,239,143,100,251,91,18,189,212,165,254,42, 5,60,214,32,57,134,176,209,221,236,110,27,57,145,87,253, 172,209,27,176,69,67,107,0,236,24,47,245,239,88,234,220, 225,206,224,163,254,210,190,94,177,60,139,109,180,49,253,208, 216,94,45,73,168,166,9,109,75,177,53,0,118,212,64,253, 133,39,243,223,212,122,94,114,21,230,148,252,109,225,179,191, 87,35,23,175,184,235,178,247,103,95,74,101,60,254,0,41, 129,129,13,11,172,1,176,163,134,234,159,108,91,144,88,133, 249,222,184,94,175,249,3,17,202,15,139,245,153,251,211,219, 211,35,6,127,192,182,20,91,3,96,71,173,212,191,125,126, 98,149,187,64,248,70,249,132,24,228,163,226,254,124,75,113, 54,100,75,49,20,83,96,195,2,107,0,236,168,80,253,11, 133,191,185,206,29,180,72,141,227,185,253,59,38,57,57,79, 208,134,236,150,225,221,182,165,216,26,0,59,106,172,254,205, 51,91,122,113,161,30,171,115,105,190,120,229,206,27,116,159, 251,64,250,217,84,202,182,20,91,3,96,71,77,212,191,101, 122,203,111,233,82,147,187,206,39,248,226,149,227,36,54,208, 166,177,199,211,3,154,63,96,91,138,173,1,176,35,66,245, 79,76,233,106,254,13,189,77,194,251,51,251,245,184,200,211, 226,33,218,148,217,58,122,48,68,75,113,238,39,219,82,108, 13,128,29,161,213,63,129,228,148,206,166,91,233,221,194,184, 67,243,169,187,26,203,159,21,235,179,91,50,175,216,150,98, 107,0,236,136,84,253,91,219,154,110,161,247,251,226,253,181, 88,125,220,228,7,112,159,123,127,234,153,244,168,109,41,182, 6,192,142,40,212,191,53,249,19,124,52,28,222,191,94,228, 206,16,109,114,55,187,143,216,150,98,107,0,236,168,68,253, 69,161,238,223,218,146,252,33,110,128,18,143,251,227,253,235, 70,238,226,81,218,144,222,226,219,82,28,46,44,176,102,192, 26,128,70,84,255,182,230,228,119,233,230,10,241,254,117,35, 199,78,177,46,253,96,246,249,98,75,177,139,172,109,41,182, 6,192,14,78,253,133,167,235,47,153,248,166,251,217,170,241, 254,245,34,63,66,235,196,253,163,92,75,177,31,93,57,108, 209,208,26,128,70,83,127,7,73,36,145,236,252,10,254,52, 50,188,127,189,200,199,156,7,178,27,221,135,71,142,218,150, 98,107,0,236,48,169,127,2,77,93,255,68,127,165,254,106, 181,120,255,186,145,147,120,146,54,100,31,26,126,213,182,20, 91,3,96,159,131,144,64,63,73,36,208,212,249,63,232,111, 99,195,251,215,141,92,236,161,251,220,7,210,59,82,105,67, 209,208,102,7,172,1,104,48,245,79,32,137,100,231,223,226, 239,75,239,117,92,120,255,122,145,59,39,105,35,109,30,123, 212,182,20,91,3,96,213,63,137,100,215,95,208,151,107,136, 247,175,23,121,70,60,68,155,50,15,133,110,41,134,45,26, 90,3,48,25,119,255,63,161,175,141,3,222,191,126,228,207, 209,6,247,1,219,82,108,13,64,99,170,255,103,241,173,113, 196,251,215,143,252,0,214,185,15,164,158,178,45,197,214,0, 76,94,245,47,29,243,85,112,254,63,73,223,133,24,127,188, 127,189,200,197,16,109,118,55,141,109,203,28,151,90,138,201, 30,109,106,13,192,36,84,255,142,155,196,15,188,207,101,188, 241,254,117,35,119,241,24,109,72,63,104,89,138,173,1,152, 196,234,223,121,61,126,76,78,29,226,253,235,70,46,118,186, 235,179,15,100,95,240,28,109,106,91,138,173,1,152,36,234, 255,49,250,153,72,152,118,200,186,192,251,215,137,92,28,197, 58,218,60,106,89,138,173,1,152,36,234,159,64,83,215,7, 233,150,220,33,159,117,143,247,175,23,249,168,115,63,109,206, 108,45,131,165,216,102,7,172,1,168,19,245,87,142,248,110, 191,198,249,15,52,77,36,188,127,221,200,201,217,78,27,178, 15,89,150,98,107,0,38,198,154,51,234,223,241,62,113,43, 154,229,120,119,130,224,253,235,70,46,10,44,197,182,165,216, 26,128,9,162,254,57,200,79,178,227,61,226,87,212,50,177, 241,254,245,34,23,39,104,3,109,182,44,197,214,0,76,4, 245,207,165,254,222,69,191,22,173,240,217,237,38,16,222,191, 94,228,105,60,68,27,51,15,91,150,98,107,0,234,92,253, 219,222,158,252,47,183,109,146,225,253,235,71,254,44,214,103, 31,204,236,180,45,197,214,0,212,165,250,119,93,129,219,168, 93,254,197,73,131,247,175,31,249,1,220,231,62,144,122,42, 61,102,91,138,173,1,168,167,221,255,173,137,219,208,201,185, 178,4,129,165,248,26,230,99,53,214,224,249,73,205,7,80, 43,185,24,162,77,238,38,119,155,101,41,182,6,96,60,214, 185,68,245,153,87,255,246,75,19,43,169,171,240,62,201,25, 237,185,248,159,248,12,156,252,103,111,96,53,238,194,99,249, 55,113,114,242,1,212,76,238,138,109,180,49,128,165,184,97, 90,138,173,1,24,63,245,127,75,226,118,119,42,135,151,111, 194,23,240,223,209,174,93,230,48,238,198,29,216,138,244,100, 230,3,168,153,92,188,226,174,203,62,152,125,161,200,82,156, 245,0,140,26,166,165,216,26,128,241,82,255,139,156,219,49, 157,251,245,247,226,59,56,223,231,114,39,113,15,214,98,19, 134,208,208,124,1,81,201,143,136,117,84,29,75,241,132,54, 3,214,0,140,139,250,119,92,232,244,210,12,29,15,63,27, 223,192,77,161,46,59,138,245,184,19,27,112,180,177,249,2, 162,146,143,138,7,178,155,202,98,41,158,36,217,1,107,0, 198,67,253,151,58,125,52,83,199,195,223,140,111,97,70,89, 151,207,224,1,172,197,61,56,136,70,230,11,136,76,78,226, 201,236,122,218,218,72,44,197,214,0,212,92,253,59,23,163, 15,179,85,119,116,46,190,143,143,85,56,13,225,81,172,197, 221,120,173,177,249,2,34,147,139,55,104,93,163,176,20,91, 3,80,27,245,47,1,126,23,163,151,230,168,120,247,27,240, 175,101,238,253,220,120,22,119,99,53,94,104,112,190,128,168, 228,226,164,187,1,155,199,30,155,220,45,197,214,0,212,86, 253,23,81,159,152,43,255,226,20,124,27,127,16,225,196,175, 99,53,238,194,227,158,77,200,242,9,84,33,79,211,86,108, 204,108,157,172,44,197,214,0,212,84,253,209,135,185,50,184, 245,34,252,135,111,206,191,210,113,8,107,177,6,91,144,101, 93,94,203,39,80,182,252,89,108,200,62,56,249,88,138,173, 1,168,141,250,39,208,212,122,94,178,23,243,228,36,212,103, 241,29,180,196,120,35,167,112,23,214,98,35,70,97,249,4, 34,145,31,192,125,238,253,169,103,2,88,138,39,80,209,208, 26,128,218,168,127,178,245,188,100,47,78,247,190,90,205,248, 54,254,168,38,55,52,130,117,88,139,117,56,161,196,187,150, 79,160,50,57,134,176,201,221,236,110,27,57,62,241,91,138, 173,1,168,141,250,159,155,236,163,211,189,217,230,249,248,21, 46,175,233,141,101,112,63,214,226,30,28,130,229,19,136,68, 238,226,81,177,49,187,101,232,77,159,150,226,186,15,11,172, 1,168,133,250,159,147,232,19,243,189,47,210,229,248,45,230, 140,203,13,18,30,195,157,184,27,187,153,136,215,188,27,90, 185,89,238,236,194,186,244,131,217,231,61,44,197,122,96,80, 183,69,67,107,0,226,87,255,133,201,62,58,195,139,87,191, 30,63,67,235,56,223,234,14,172,197,157,120,9,150,79,32, 26,185,115,204,93,239,222,159,126,60,61,84,70,75,113,29, 20,13,173,1,168,129,250,227,12,239,175,253,45,254,87,25, 203,254,69,252,0,87,160,27,203,49,47,134,91,126,13,107, 176,22,143,107,94,130,229,19,168,80,158,18,91,104,99,250, 225,209,254,137,82,52,180,6,32,86,245,111,91,224,244,137, 51,189,47,205,191,226,143,203,186,108,39,6,243,151,127,55, 150,99,57,206,138,225,214,15,97,13,238,196,195,200,88,62, 129,168,228,207,96,125,118,75,102,87,42,235,211,66,84,23, 217,1,107,0,98,85,255,100,31,157,89,120,138,2,77,248, 57,110,44,243,194,95,196,55,224,122,254,255,149,232,70,55, 206,141,225,43,28,207,163,12,71,44,159,64,84,242,253,88, 231,222,159,126,42,149,170,152,135,40,118,127,192,26,128,248, 212,255,236,100,159,123,86,41,110,108,195,111,112,117,5,23, 63,136,62,244,225,65,169,165,231,50,116,163,39,150,6,162, 97,172,199,26,220,135,83,158,23,219,242,9,84,35,119,6, 105,179,187,217,125,164,62,121,136,172,1,136,77,253,157,62, 113,150,215,149,95,133,43,171,152,228,8,86,161,15,155,144, 246,124,118,1,86,160,27,203,98,248,74,105,60,136,59,113, 15,250,125,162,96,203,39,80,150,220,197,163,180,33,128,135, 40,56,63,16,185,17,176,6,32,182,221,159,206,42,197,137, 83,177,58,146,170,255,9,220,137,94,108,192,168,231,179,69, 184,9,221,184,44,134,175,230,226,81,220,137,123,176,219,242, 5,68,38,119,118,98,125,90,62,218,212,156,38,116,125,139, 134,214,0,212,191,250,231,70,23,238,196,91,35,156,116,16, 107,208,135,123,49,236,249,108,1,122,208,131,43,99,121,160, 207,96,45,214,224,69,203,23,16,153,92,28,197,250,204,253, 233,237,198,162,33,31,26,168,33,129,53,0,245,171,254,165, 95,234,192,218,72,213,191,20,173,223,131,62,220,141,1,207, 103,243,209,141,30,188,171,72,38,26,229,120,21,107,113,39, 158,240,117,119,45,159,64,89,114,190,104,152,85,76,129,203, 96,11,34,51,1,214,0,196,150,250,203,61,163,86,172,198, 59,99,188,137,49,220,135,94,220,133,227,158,207,230,98,57, 186,241,94,36,99,152,239,0,214,226,78,108,205,135,168,150, 79,32,18,249,51,88,159,221,50,188,211,163,254,89,35,39, 145,220,58,100,13,64,157,169,255,2,167,215,155,250,75,226, 191,240,161,144,23,220,131,63,193,22,44,195,10,220,136,69, 101,39,237,54,162,23,107,112,216,243,217,76,220,128,30,92, 93,56,113,52,210,113,12,247,224,46,108,42,162,12,45,159, 64,4,242,125,88,231,222,159,126,58,149,59,188,36,43,253, 219,107,6,34,53,1,214,0,68,170,254,201,62,58,211,67, 60,141,91,208,29,250,146,215,96,99,241,231,75,208,141,229, 184,160,204,155,202,226,126,244,97,53,14,120,62,155,138,235, 208,131,15,98,74,12,139,48,148,71,25,158,50,198,186,150, 79,160,92,185,24,164,77,238,38,119,59,57,180,127,44,139, 44,50,200,42,102,64,14,5,172,1,168,71,245,207,61,232, 111,226,47,203,184,232,20,140,41,159,44,198,205,184,17,151, 148,121,115,46,182,162,23,171,240,38,188,121,136,107,209,141, 143,48,103,13,84,63,82,121,148,97,191,229,11,136,78,190, 79,164,220,227,226,171,3,59,144,65,70,50,3,37,35,16, 137,15,96,13,64,68,234,223,186,176,169,55,215,245,87,120, 136,127,129,111,150,117,217,15,96,3,251,249,185,184,9,203, 113,69,217,183,249,40,122,113,7,94,243,124,210,138,143,160, 27,215,162,43,134,69,113,177,13,107,112,23,246,192,242,5, 68,38,31,166,27,134,94,71,6,105,205,8,68,230,3,88, 3,16,145,250,23,16,127,133,71,123,45,126,93,102,46,126, 15,254,16,155,141,210,179,208,141,110,188,179,236,7,246,36, 250,112,7,94,242,124,210,140,15,162,7,31,143,128,132,148, 27,79,99,45,214,224,37,203,23,16,137,220,185,247,212,223, 33,141,116,222,8,148,76,0,21,189,0,107,0,234,64,253, 207,73,244,138,51,188,15,242,82,220,91,17,224,247,85,220, 142,62,108,55,202,231,161,7,221,120,55,18,101,94,247,57, 244,161,23,207,193,155,158,124,63,186,113,125,76,172,4,187, 176,22,107,240,4,44,95,64,149,114,74,125,120,108,63,210, 72,21,141,64,201,4,68,226,3,88,3,80,189,250,159,155, 236,165,249,94,188,248,108,60,136,249,85,76,179,7,189,232, 197,54,227,211,157,141,229,232,198,239,161,169,204,235,238,196, 237,232,195,147,158,79,18,120,15,186,113,99,129,171,44,226, 177,31,107,177,6,91,225,90,190,128,138,229,244,149,193,213, 72,33,133,84,222,19,200,32,83,60,199,208,26,128,113,83, 255,68,129,235,215,203,245,151,27,77,184,11,239,136,96,186, 3,232,67,47,30,98,120,125,115,99,58,110,64,15,222,95, 54,169,232,235,232,69,111,254,164,225,194,23,123,39,122,98, 130,26,3,71,243,37,195,49,195,30,168,15,43,47,201,197, 186,83,95,66,10,41,140,21,253,128,140,148,9,176,6,96, 220,212,63,137,68,235,121,137,94,113,186,236,202,253,0,127, 24,225,180,135,113,7,250,112,191,4,2,242,142,46,124,28, 61,248,80,217,1,199,62,244,161,55,223,208,83,24,87,162, 27,203,113,94,44,203,55,152,71,25,14,42,17,175,229,19, 240,151,227,240,192,114,164,48,134,177,162,23,144,246,212,3, 172,1,24,71,245,207,237,254,243,114,143,171,176,148,159,197, 15,99,152,254,56,86,163,23,27,181,66,97,97,180,227,99, 232,198,71,209,81,230,117,15,225,14,244,225,1,100,60,159, 93,130,110,244,96,73,44,203,152,194,102,172,197,61,56,162, 237,121,150,79,192,36,79,93,55,118,20,99,146,9,200,72, 121,0,107,0,198,71,253,59,23,161,151,78,147,241,224,203, 176,41,212,94,92,89,215,223,41,172,69,47,238,195,136,65, 62,5,31,70,15,174,197,212,178,157,244,213,232,195,70,164, 60,159,93,128,30,116,227,226,88,150,211,197,195,88,139,187, 240,166,229,11,8,33,119,63,55,252,10,70,49,234,49,1, 17,6,1,214,0,84,170,254,231,83,175,122,200,87,59,30, 12,169,206,213,116,253,13,225,110,244,225,158,162,51,173,142, 102,92,131,30,92,135,153,101,126,205,147,88,131,94,172,147, 160,198,231,161,7,221,177,209,151,63,133,181,88,131,87,12, 81,176,229,19,200,201,179,255,115,248,97,140,98,172,104,2, 10,62,128,53,0,227,168,254,139,209,139,57,42,222,251,23, 232,9,121,209,234,187,254,70,113,47,250,112,87,241,176,15, 117,36,241,62,116,227,6,204,45,59,86,191,11,125,248,29, 134,60,159,157,141,110,244,224,237,49,189,44,175,96,13,238, 194,147,150,47,192,32,199,247,7,239,193,40,70,242,38,64, 246,1,172,1,136,93,253,115,6,192,160,254,222,241,73,252, 52,244,101,163,234,250,75,97,3,122,177,6,71,13,114,7, 87,161,27,203,203,46,74,142,224,119,232,195,93,82,151,255, 233,232,70,55,174,138,5,106,12,236,195,90,172,193,195,200, 90,190,0,85,126,235,192,111,49,130,81,79,24,32,7,1, 214,0,212,86,253,59,150,136,94,204,86,221,181,5,120,24, 157,161,47,28,109,215,95,6,155,209,135,213,56,100,252,26, 111,71,15,150,99,65,153,95,127,12,235,209,139,53,18,212, 120,14,110,68,55,222,23,11,212,24,56,146,47,25,166,12, 238,112,35,242,9,184,107,135,126,138,81,143,9,40,21,3, 173,1,168,145,250,231,254,201,169,255,5,226,118,154,165,226, 189,29,252,174,236,218,127,212,93,127,46,182,160,15,171,176, 215,248,27,151,163,27,221,101,67,141,51,216,136,62,172,150, 160,198,51,112,61,122,112,77,44,80,99,96,16,235,176,6, 235,48,0,203,23,32,128,7,6,190,147,55,0,35,249,106, 64,161,20,152,205,115,7,90,3,80,51,245,191,80,220,174, 102,215,8,2,255,29,95,174,112,154,168,187,254,8,219,208, 139,59,240,186,241,55,46,70,55,122,42,128,26,63,136,62, 172,194,126,207,103,93,184,14,221,21,244,32,132,245,63,10, 37,195,6,231,11,120,114,240,159,243,234,95,8,2,74,165, 64,107,0,106,169,254,237,23,37,86,210,12,29,188,185,4, 15,85,185,23,70,223,245,183,29,125,184,131,205,176,23,146, 142,55,161,187,108,168,49,225,97,244,226,14,236,241,124,150, 235,65,248,88,44,80,99,32,139,71,176,6,119,121,252,154, 134,227,11,120,121,224,31,48,138,145,98,16,80,74,3,230, 154,129,172,1,136,92,249,193,170,255,197,137,149,52,93,199, 115,39,176,33,162,66,89,244,93,127,59,208,135,94,188,96, 148,159,131,30,116,227,138,178,95,131,199,208,135,62,188,234, 249,164,210,30,132,176,227,73,172,197,26,236,108,68,190,128, 189,3,127,195,26,128,140,53,0,181,84,255,183,36,86,210, 52,57,94,203,253,247,243,248,90,164,55,16,125,215,223,203, 184,29,125,120,218,40,63,19,221,232,193,59,202,206,239,63, 141,62,244,225,69,207,39,185,30,132,235,99,129,26,191,140, 127,204,215,78,26,139,47,64,28,61,245,185,124,8,48,194, 212,1,170,44,4,90,3,16,78,253,47,77,220,230,78,229, 58,183,23,98,91,44,49,112,244,93,127,175,161,23,125,120, 220,248,190,204,195,114,116,227,61,101,67,141,95,64,47,250, 176,195,243,73,174,7,225,198,8,161,198,71,241,47,248,247, 60,170,16,104,44,190,0,49,116,234,15,242,202,63,82,244, 0,82,214,0,212,80,253,219,222,154,248,109,137,70,71,126, 108,125,248,64,140,55,20,125,215,223,155,232,67,47,30,49, 122,142,179,112,35,186,113,117,217,80,227,93,232,85,106,26, 14,254,33,18,223,200,197,207,240,85,79,71,66,195,241,5, 100,6,62,81,220,255,71,60,221,128,214,0,196,164,254,78, 209,0,228,212,255,242,228,111,221,78,30,175,125,61,126,85, 131,27,139,190,235,143,59,111,80,78,58,94,135,30,124,160, 108,168,241,27,232,243,212,52,58,164,51,11,42,27,207,224, 243,120,186,193,249,2,70,110,202,142,72,117,0,111,39,128, 53,0,177,169,127,18,9,36,59,175,20,255,69,29,92,158, 22,104,199,246,208,61,118,95,196,15,112,5,186,177,60,7, 31,172,96,68,223,245,199,157,55,232,29,157,248,56,186,241, 97,180,149,121,167,251,176,10,189,120,2,127,83,165,7,48, 136,175,225,199,140,175,210,104,124,1,195,55,103,135,60,105, 192,49,79,18,48,107,13,64,188,234,255,118,252,38,87,223, 226,114,180,255,140,255,22,122,130,206,188,27,47,240,110,44, 175,130,124,35,250,174,191,227,88,131,94,172,55,38,29,219, 240,81,116,227,99,101,244,57,70,51,182,224,207,241,166,229, 11,128,192,240,39,179,131,74,8,144,178,6,32,78,245,207, 147,125,117,190,83,252,154,218,114,143,67,239,217,90,136,199, 203,168,254,127,17,223,208,200,55,186,113,110,197,113,113,212, 93,127,3,88,171,157,55,40,39,29,115,84,162,211,106,242, 64,70,240,191,241,111,210,158,216,200,124,1,195,159,202,14, 40,33,64,202,83,6,180,6,32,38,245,127,151,248,149,219, 102,198,107,255,26,31,47,107,26,46,238,190,12,221,232,193, 249,21,222,120,244,93,127,220,121,131,222,209,132,107,208,141, 235,49,43,214,7,242,4,254,12,187,60,138,209,232,124,1, 195,159,206,158,210,66,128,116,52,112,32,107,0,12,234,223, 113,21,126,41,90,205,170,119,21,238,169,104,58,46,238,190, 0,43,208,141,101,21,127,133,168,187,254,114,231,13,174,53, 38,29,19,248,61,116,227,70,156,22,195,227,32,252,16,95, 70,218,242,1,120,228,195,159,201,158,180,6,160,182,234,255, 94,113,43,166,248,225,181,183,224,45,85,76,123,2,119,162, 23,27,36,242,141,69,184,9,221,184,172,226,107,70,221,245, 151,59,111,240,78,141,190,171,148,116,124,23,122,176,28,103, 68,248,56,142,226,47,112,175,229,3,80,228,195,191,159,61, 137,17,107,0,106,167,254,239,19,255,137,22,63,188,118,15, 110,137,96,250,65,172,209,226,238,5,232,65,15,174,172,248, 193,68,221,245,151,59,111,112,21,14,26,151,240,74,244,160, 27,11,35,88,143,109,248,67,236,179,124,0,154,124,244,179, 233,19,158,86,32,107,0,34,85,127,145,231,251,41,170,127, 251,251,157,95,120,11,224,122,174,54,129,237,21,167,239,194, 197,221,243,209,141,30,188,171,98,242,141,168,187,254,92,108, 69,31,250,124,146,142,87,227,23,85,210,138,255,2,127,135, 84,3,226,253,131,229,99,178,1,72,121,0,193,214,0,68, 172,254,77,237,215,56,183,80,179,63,94,251,143,240,253,200, 111,39,23,119,223,37,145,111,204,197,114,116,227,189,21,147, 111,68,221,245,71,120,12,189,232,195,110,86,122,13,214,87, 252,237,211,248,127,60,62,85,131,225,253,3,229,146,7,80, 96,4,136,136,18,68,88,245,247,170,127,215,7,220,91,68, 147,201,66,231,126,154,130,29,21,55,243,132,139,187,215,72, 228,27,51,113,3,122,112,117,197,128,227,232,187,254,158,68, 31,250,240,178,242,105,139,148,209,40,47,49,250,41,108,67, 195,226,253,3,229,121,15,64,102,4,176,6,32,98,245,79, 34,217,245,33,250,57,154,130,240,218,159,195,255,23,243,237, 229,226,238,213,56,224,249,108,42,174,67,15,62,136,41,21, 43,89,212,93,127,234,121,131,239,55,240,28,6,141,157,232, 145,124,138,134,195,251,7,202,71,173,1,168,133,250,183,127, 212,249,25,146,65,120,237,36,158,11,117,146,94,101,204,255, 106,220,221,139,85,120,211,243,89,7,174,69,55,62,82,49, 249,70,244,93,127,175,160,23,119,224,57,92,133,127,175,40, 7,240,8,62,137,163,147,27,207,95,181,60,95,5,176,6, 32,78,245,239,188,22,63,41,4,219,126,120,237,63,198,191, 134,154,166,26,230,127,121,60,138,94,220,129,215,60,159,180, 226,35,232,198,181,37,128,98,153,163,126,186,254,86,225,115, 24,195,228,198,243,87,47,183,6,32,126,245,255,56,126,66, 137,96,188,118,2,207,132,220,231,170,103,254,215,227,238,59, 240,146,231,147,230,188,154,86,74,190,49,254,93,127,255,129, 47,120,222,222,201,138,231,175,94,62,252,153,236,41,107,0, 226,84,255,235,233,223,68,2,62,214,184,240,217,39,240,179, 144,83,69,197,252,239,31,119,3,73,188,31,221,184,190,98, 242,141,241,235,250,251,63,248,34,26,2,207,95,181,220,26, 128,88,213,191,227,70,241,35,114,194,225,181,31,9,221,176, 27,45,243,191,154,54,187,29,125,120,82,82,211,247,160,27, 55,134,202,78,112,163,246,93,127,95,195,55,208,40,120,254, 170,249,0,172,1,136,81,253,151,139,31,170,253,54,166,46, 237,247,225,206,178,166,140,154,249,95,30,175,163,23,189,120, 204,243,252,5,222,137,158,42,160,198,181,235,250,251,95,248, 174,143,180,209,240,254,65,242,225,79,43,104,64,107,0,34, 83,255,30,252,64,56,64,56,188,246,42,188,191,130,169,163, 102,254,151,199,62,244,161,23,91,53,168,241,114,156,87,225, 82,197,223,245,247,85,124,171,161,240,252,213,202,173,1,136, 75,253,111,18,223,71,94,253,131,241,218,75,241,88,21,183, 16,61,243,191,119,28,194,29,232,195,3,200,120,62,187,4, 221,232,193,146,10,239,55,190,174,191,175,227,235,236,250,54, 46,222,63,4,31,192,160,53,0,145,171,127,215,10,250,62, 137,240,120,237,239,226,79,171,190,149,232,153,255,189,227,40, 86,163,15,27,165,147,245,46,64,15,186,113,113,197,119,28, 117,215,223,247,240,101,52,26,158,191,106,62,128,146,1,176, 173,192,145,169,255,205,238,191,10,225,23,133,201,150,184,19, 47,151,193,196,239,63,162,103,254,247,142,147,88,131,94,172, 147,84,244,60,244,160,187,138,227,75,162,234,250,251,37,254, 202,176,190,65,235,223,200,242,225,79,100,135,53,44,128,5, 3,85,165,254,159,160,239,65,148,131,215,142,190,1,56,122, 230,127,239,24,196,93,232,195,239,48,228,249,236,108,116,163, 7,111,175,248,161,87,219,245,119,55,62,5,2,44,222,191, 92,62,128,79,102,135,172,1,136,82,253,63,69,223,201,169, 191,158,123,53,117,103,61,142,197,177,220,98,244,204,255,222, 49,130,223,161,15,119,121,88,245,129,211,209,141,110,92,85, 49,212,184,210,177,21,55,96,172,33,241,252,213,202,135,111, 206,142,88,3,16,177,250,243,238,150,9,156,113,101,133,46, 111,216,17,61,243,191,119,140,97,61,122,177,70,130,26,207, 193,141,232,198,251,42,134,26,151,59,118,225,253,56,222,160, 120,254,106,229,35,114,8,48,106,9,65,170,84,255,92,234, 175,28,188,246,15,241,217,26,220,114,244,204,255,222,145,193, 70,244,97,181,4,53,158,129,235,209,131,107,170,60,219,56, 120,28,195,213,30,52,131,197,251,151,39,31,94,145,29,181, 140,64,17,169,191,251,29,53,245,23,140,199,238,192,206,200, 18,128,97,20,53,106,230,127,239,200,226,65,244,97,21,246, 123,62,235,194,117,232,174,184,246,16,60,210,184,14,91,97, 241,254,149,202,71,172,1,136,62,246,47,7,143,253,251,248, 81,168,105,115,167,255,172,64,55,102,87,253,21,162,103,254, 151,191,227,195,232,197,29,216,227,249,44,87,123,248,88,197, 80,99,243,248,43,252,210,119,125,131,214,191,209,229,3,43, 96,13,64,245,234,223,254,41,71,74,253,133,199,99,223,141, 171,66,77,92,56,253,39,129,247,97,5,150,87,152,184,147, 95,134,168,153,255,229,241,24,250,208,135,87,61,159,84,87, 123,224,198,45,248,130,197,251,87,37,47,26,0,203,10,92, 197,238,255,73,250,110,225,219,150,135,199,62,29,47,134,92, 38,249,244,159,36,174,198,205,184,161,98,184,174,119,68,205, 252,47,143,167,209,135,62,188,232,249,36,87,123,184,62,130, 123,127,28,31,70,218,226,253,171,145,211,192,205,214,0,84, 171,254,159,160,239,145,168,12,143,253,5,124,37,244,244,7, 209,139,149,82,103,126,19,62,128,21,184,62,18,114,141,168, 153,255,229,241,2,122,209,135,29,158,79,114,181,135,27,43, 134,26,3,135,241,238,34,177,153,197,251,87,40,119,139,199, 131,91,3,80,161,250,75,93,127,229,226,177,31,198,69,101, 222,198,126,220,142,149,120,196,243,100,154,241,33,172,192,117, 21,115,248,120,71,212,204,255,242,216,133,94,5,193,232,224, 31,42,60,229,215,197,117,120,192,226,249,171,149,103,7,62, 105,13,64,21,234,223,113,147,248,65,110,247,175,4,143,189, 8,79,84,120,59,123,177,18,43,37,184,110,11,62,140,155, 113,109,36,231,236,70,205,252,47,143,55,208,231,65,48,118, 24,89,131,252,199,183,240,85,88,60,127,213,242,204,224,167, 138,72,0,107,0,2,212,95,62,229,39,137,100,71,143,248, 65,88,188,63,39,255,59,124,169,170,219,218,131,149,184,77, 218,81,167,224,163,88,129,107,35,201,182,71,205,252,47,143, 125,88,133,94,60,129,191,169,200,3,216,134,15,27,238,203, 226,253,203,146,103,6,172,1,168,88,253,111,196,143,194,227, 253,57,121,117,103,0,22,198,110,172,196,74,137,195,167,13, 31,195,10,124,180,12,26,110,191,204,67,212,204,255,213,143, 227,120,23,222,180,120,254,40,228,233,129,79,91,3,80,145, 250,119,94,143,31,135,199,251,115,242,179,241,108,132,183,249, 42,110,195,74,60,227,249,164,29,31,199,10,124,164,98,182, 127,239,136,158,249,191,154,241,71,232,11,177,190,86,30,66, 110,13,64,5,234,159,64,83,231,181,248,41,37,170,195,99, 255,53,254,37,242,219,125,5,183,97,165,68,236,217,129,235, 112,51,62,20,201,14,29,61,243,127,37,99,21,254,32,212, 250,90,121,8,185,53,0,161,212,63,167,252,197,35,62,59, 63,66,63,23,73,191,40,43,12,30,251,30,188,59,166,219, 126,9,183,225,54,169,246,222,133,235,177,2,31,140,164,47, 127,124,153,255,251,113,133,1,248,83,238,250,91,57,128,244, 192,167,242,61,128,214,0,176,223,128,81,255,174,15,208,47, 114,135,124,85,131,199,238,194,235,49,163,229,158,199,109,88, 41,241,237,76,195,13,88,129,107,42,76,219,201,99,188,152, 255,87,224,222,80,235,107,229,161,228,153,98,25,176,0,7, 78,33,133,140,53,0,186,250,59,72,34,129,100,199,213,226, 63,209,92,61,30,123,57,254,179,38,95,226,25,172,196,237, 216,41,165,237,150,99,5,174,142,196,252,212,154,249,255,118, 252,73,200,245,181,242,80,114,221,0,20,40,193,220,70,55, 0,133,202,191,83,220,255,147,72,118,188,71,252,202,27,78, 87,142,199,254,9,62,85,195,47,243,20,110,195,237,210,17, 96,51,177,28,55,227,247,42,172,229,43,153,164,26,49,255, 31,195,229,202,28,22,207,95,165,60,59,248,9,38,7,144, 227,4,164,70,54,0,37,245,23,72,20,212,191,243,157,248, 13,181,70,131,199,126,53,2,84,95,185,99,59,86,226,118, 9,252,51,27,221,88,129,247,70,194,224,19,63,243,255,95, 225,151,22,207,31,173,220,245,160,1,85,3,224,130,26,213, 0,176,234,223,117,5,221,166,118,216,84,138,199,190,16,143, 140,219,151,123,20,43,113,187,116,46,240,92,244,96,5,222, 29,137,25,136,143,249,127,11,62,106,241,252,81,203,105,240, 38,143,7,48,166,36,1,27,212,0,120,213,191,4,248,189, 36,113,59,117,69,133,199,254,107,134,191,190,150,131,240,8, 86,162,23,251,60,159,205,195,77,88,81,241,145,98,234,245, 163,102,254,207,224,29,120,217,226,249,163,231,3,232,150,60, 128,20,198,144,105,108,3,160,171,127,2,77,29,23,56,119, 208,244,232,240,216,183,227,67,117,240,85,9,91,113,27,250, 138,152,58,0,152,143,155,112,51,222,22,209,195,139,142,249, 255,71,248,71,139,231,143,92,142,159,14,174,99,60,128,82, 21,0,141,102,0,74,160,31,111,221,255,124,172,42,132,236, 81,224,177,147,216,83,67,26,176,96,167,125,11,86,162,79, 162,9,59,11,55,97,5,174,140,104,134,234,153,255,15,227, 210,60,251,176,197,243,71,39,111,218,113,242,155,217,17,197, 3,104,104,3,192,170,127,235,57,201,85,52,47,74,60,246, 21,216,84,119,95,221,197,253,88,137,59,36,90,207,5,88, 129,21,120,107,68,51,84,195,252,255,151,248,149,197,243,71, 44,111,122,110,238,217,47,125,14,163,158,70,160,156,7,144, 241,148,1,27,202,0,200,234,159,79,253,181,207,119,86,227, 108,248,88,211,242,241,216,159,175,16,3,31,255,200,98,19, 86,98,149,196,29,124,14,110,198,138,138,25,129,162,24,59, 138,61,147,22,207,31,149,220,217,53,119,129,24,120,249,47, 139,234,95,96,4,44,120,0,17,24,0,103,226,171,127,199, 220,196,74,58,27,249,108,72,49,114,202,255,127,81,204,145, 148,39,127,71,221,46,66,2,31,192,255,197,65,220,139,63, 194,244,252,103,175,225,235,184,20,231,227,159,34,5,47,149, 51,190,20,241,250,91,57,246,206,158,47,146,148,1,21,255, 241,254,34,10,191,214,56,30,0,171,254,45,51,154,123,177, 140,203,163,250,37,214,130,229,187,35,160,244,140,127,164,177, 30,43,177,26,39,61,159,45,193,10,172,192,133,53,189,143, 77,184,33,226,245,111,120,249,225,57,77,137,105,128,115,224, 165,255,150,15,1,74,109,64,169,124,23,64,182,218,54,160, 137,228,1,112,124,63,137,41,157,205,191,204,169,63,121,76, 98,201,198,66,49,150,97,229,231,77,8,245,7,154,240,81, 252,7,250,177,6,159,41,210,141,189,132,127,198,50,92,136, 127,198,75,53,186,11,194,151,34,94,255,70,151,139,83,179, 221,196,52,1,128,210,249,61,159,164,95,162,106,203,127,19, 205,0,176,116,95,173,173,77,183,224,10,175,47,36,164,69, 22,69,23,167,92,249,91,49,145,70,51,62,142,95,162,31, 171,240,201,98,229,226,5,124,25,75,113,49,190,38,97,12, 226,25,125,69,142,131,168,214,191,193,229,163,51,142,39,231, 230,129,66,25,201,86,144,100,53,26,198,0,200,234,159,239, 251,107,157,210,244,99,122,111,33,110,42,89,85,33,217,213, 202,228,151,97,226,141,22,220,128,223,224,48,250,176,162,216, 12,249,44,254,9,231,227,82,124,93,226,254,143,118,184,248, 70,228,235,223,208,114,119,218,158,150,179,139,58,159,246,40, 125,228,234,63,241,60,128,34,230,175,61,153,248,54,125,76, 120,172,40,164,159,11,157,212,149,201,39,162,1,200,141,41, 88,142,219,208,143,149,232,41,30,245,245,52,254,95,156,135, 203,241,45,159,3,70,42,31,189,197,243,10,162,91,255,70, 150,119,188,208,118,126,33,75,32,128,140,199,237,87,99,133, 6,49,0,133,181,144,42,255,137,47,137,79,144,182,50,228, 73,178,8,84,42,119,34,225,1,28,207,209,134,155,112,59, 14,227,191,112,99,145,110,236,9,252,61,22,226,109,248,142, 116,32,88,181,35,139,111,70,190,254,141,44,111,125,186,107, 153,244,89,198,152,48,104,16,3,32,239,253,5,166,255,207, 211,95,144,18,71,161,184,168,66,90,230,242,229,75,34,225, 232,27,255,209,142,79,224,14,244,227,87,184,174,136,143,126, 12,127,135,5,120,7,254,213,7,10,84,222,254,191,51,242, 245,111,92,121,203,115,211,47,33,207,158,135,92,8,0,237, 127,136,42,24,112,38,140,250,123,146,127,157,159,166,47,122, 129,148,144,56,214,101,199,161,18,249,50,76,166,209,137,79, 227,78,244,227,86,124,44,79,55,70,216,134,191,197,89,120, 55,126,32,157,18,92,201,248,94,12,235,223,168,242,228,115, 51,151,145,39,60,200,39,1,229,93,159,164,24,98,146,27, 0,54,247,223,249,17,250,54,121,82,40,37,119,170,100,91, 171,147,95,136,201,55,186,240,251,184,11,135,240,11,124,36, 79,55,70,216,138,47,224,76,188,23,63,50,30,69,30,52, 238,195,11,49,172,127,99,202,157,151,103,93,32,60,249,128, 124,74,48,27,143,235,63,17,12,128,222,245,159,68,178,243, 29,248,137,112,74,105,1,104,253,85,133,207,43,151,47,195, 100,29,211,240,135,184,7,7,241,115,124,48,79,55,230,226, 65,252,53,78,199,23,43,220,255,227,88,255,70,148,59,187, 231,44,116,28,42,26,138,162,105,200,104,25,128,6,73,2, 234,125,127,9,36,58,46,20,183,82,139,156,12,21,82,204, 36,180,100,105,249,242,201,107,0,114,99,6,254,4,247,225, 0,126,134,247,231,233,198,92,252,160,130,235,60,134,173,177, 172,127,227,201,157,3,167,205,22,205,114,155,80,110,184,25, 105,201,35,247,1,156,9,164,254,201,182,51,197,111,220,46, 33,217,72,189,62,82,173,188,19,243,208,8,99,22,254,12, 27,176,31,63,198,251,208,129,191,169,224,10,63,138,101,253, 27,79,142,99,179,155,209,1,6,36,140,66,25,16,113,153, 129,100,221,190,159,66,106,253,73,32,209,50,35,241,27,156, 38,224,45,163,8,229,39,111,170,165,82,249,34,52,210,152, 131,63,199,159,87,244,151,251,113,167,103,87,139,110,253,27, 77,46,134,102,141,36,230,11,232,238,191,100,0,116,40,208, 36,246,0,228,202,127,174,239,175,173,229,22,177,184,240,49, 49,127,80,178,181,213,201,27,203,0,84,62,126,238,57,139, 48,202,245,111,48,121,122,250,161,228,124,21,37,80,210,115, 145,137,247,41,58,117,170,254,10,232,167,173,169,249,251,226, 157,5,229,151,173,169,26,107,201,117,212,242,229,214,0,132, 25,99,248,69,76,235,223,80,114,234,218,213,114,142,55,33, 40,164,36,97,62,7,64,141,100,0,88,186,207,230,127,204, 225,77,229,132,138,108,77,163,194,99,159,103,181,59,196,88, 133,35,22,207,95,181,188,253,185,246,165,178,103,95,144,20, 205,65,214,19,249,199,96,8,146,117,170,254,146,251,63,237, 51,248,60,36,27,170,90,83,161,88,93,174,3,59,172,252, 44,171,221,33,198,127,84,188,190,86,94,144,79,121,178,235, 50,41,25,88,60,42,204,147,50,204,54,82,8,192,86,254, 187,222,135,111,122,247,124,72,169,21,46,61,90,29,30,251, 108,171,221,129,227,21,108,133,197,243,87,39,111,218,49,227, 50,111,206,95,238,20,144,66,0,115,16,64,147,203,0,200, 165,63,7,9,36,166,45,77,254,76,36,160,197,70,197,28, 42,132,210,94,89,29,30,187,21,115,172,126,7,142,91,45, 158,191,74,121,211,243,179,47,38,205,84,200,158,109,62,4, 160,56,159,164,83,87,202,239,53,1,185,198,159,185,226,86, 234,132,164,230,222,95,43,152,65,217,52,84,131,199,62,211, 106,119,224,72,227,215,21,175,175,149,11,0,98,231,236,197, 36,25,139,82,62,139,100,111,192,212,8,68,147,205,0,48, 140,127,173,109,45,255,142,179,132,230,252,171,46,131,35,201, 100,43,90,46,30,123,190,213,239,192,177,30,71,96,241,252, 149,203,197,238,57,243,69,18,146,127,224,133,10,9,239,79, 217,104,21,190,94,13,128,202,249,147,163,252,250,22,174,52, 167,82,188,14,150,234,31,200,139,91,14,30,251,52,171,223, 129,227,183,22,207,95,141,188,127,246,116,167,141,20,35,33, 163,0,61,221,0,217,168,163,254,122,52,0,130,137,254,147, 51,62,39,110,86,99,127,175,157,240,86,12,161,248,0,66, 50,17,229,224,181,173,1,8,26,167,240,59,159,245,179,120, 127,127,57,78,206,166,28,221,167,23,27,232,221,218,20,62, 128,12,212,222,160,73,234,1,40,125,255,211,223,39,190,44, 59,252,66,219,225,101,169,195,248,2,234,223,7,227,181,231, 89,13,15,24,119,98,204,103,253,44,222,223,87,62,58,243, 120,114,174,148,228,99,67,123,15,31,128,94,6,140,148,23, 208,169,11,229,87,147,127,201,206,115,19,63,17,142,23,47, 173,182,0,9,201,114,136,162,51,37,32,91,87,33,153,138, 96,188,246,92,171,225,1,227,118,139,231,175,84,158,157,190, 167,101,1,151,240,147,115,86,94,121,222,0,80,92,97,128, 83,23,234,175,236,255,83,187,166,220,34,166,10,37,247,239, 77,141,176,206,85,126,217,8,142,20,28,168,158,131,63,94, 123,134,213,112,223,113,20,91,44,158,191,66,121,215,139,173, 231,107,120,127,5,35,160,201,221,120,203,128,201,186,81,127, 79,243,79,203,119,105,73,105,25,28,201,186,122,129,20,130, 61,82,153,60,11,232,74,149,89,146,26,51,133,39,100,16, 0,166,227,42,156,141,243,173,142,251,142,187,145,81,14,179, 214,241,238,86,206,201,219,159,106,191,84,86,115,64,110,13, 82,205,0,0,184,89,45,54,152,100,57,0,45,250,159,243, 57,92,167,59,250,165,239,238,104,142,190,252,95,199,243,23, 142,150,63,16,202,35,43,216,223,233,248,20,22,161,185,200, 168,111,7,63,86,91,60,127,69,242,230,103,187,46,101,241, 254,30,229,231,228,52,169,209,128,66,49,1,9,36,102,190, 93,124,9,172,85,85,35,124,53,211,47,52,85,87,255,94, 205,28,120,229,87,229,153,128,91,173,142,251,140,19,184,31, 0,239,198,170,8,56,43,47,201,19,207,206,186,72,253,92, 72,190,168,254,119,121,115,224,26,247,253,9,79,10,202,68, 255,211,231,37,127,154,103,169,82,162,117,217,94,144,178,243, 171,246,68,205,20,8,229,65,233,120,237,2,4,232,231,216, 138,227,86,211,13,99,3,50,176,120,254,114,229,137,23,230, 92,104,196,251,43,89,0,77,62,169,209,128,74,235,79,91, 115,203,255,193,92,82,50,161,66,203,150,170,221,86,114,89, 133,138,63,149,162,48,185,236,194,199,106,185,209,143,13,216, 128,51,176,12,23,160,211,106,188,50,238,3,88,118,27,175, 135,101,229,178,220,121,109,246,121,112,10,142,190,240,36,12, 189,209,190,73,238,170,85,0,154,44,6,64,232,173,63,83, 63,79,239,33,201,10,10,69,201,245,124,190,23,61,165,52, 81,230,93,28,23,28,123,128,122,173,55,36,34,144,189,216, 139,123,177,16,203,176,4,109,86,239,243,35,139,245,146,145, 149,107,51,186,17,182,114,129,196,190,217,179,69,179,140,10, 44,41,191,208,24,191,53,185,27,239,51,77,142,155,250,151, 204,64,222,253,159,243,14,252,143,220,7,46,211,25,77,18, 174,138,255,153,227,11,240,254,236,120,30,146,80,106,9,91, 112,166,118,30,208,110,236,198,221,56,23,23,225,252,226,201, 58,141,60,30,199,49,139,231,47,79,126,120,214,20,167,211, 23,239,31,192,7,80,68,3,82,60,89,128,228,184,169,191, 2,251,237,156,153,252,33,57,178,18,147,146,94,81,93,36, 189,40,8,233,33,184,140,92,190,126,73,118,2,191,193,123, 112,142,150,20,113,177,19,59,145,192,249,184,8,139,234,152, 67,181,54,1,0,49,207,71,72,222,154,149,123,140,192,208, 204,84,142,238,19,30,135,159,164,26,1,193,95,238,186,241, 36,255,198,223,3,16,74,239,223,55,49,159,43,234,241,188, 63,114,138,80,182,201,222,28,128,94,112,209,31,93,225,113, 157,196,90,76,193,98,44,198,233,26,230,48,139,23,241,34, 154,177,20,203,176,208,155,165,108,168,177,89,121,201,77,120, 119,43,7,4,144,154,126,176,229,92,82,240,254,84,84,116, 239,207,62,242,108,156,25,128,241,49,0,178,251,159,64,2, 137,121,159,192,117,250,46,14,67,138,132,148,128,64,70,254, 169,144,74,104,137,66,98,174,159,187,234,8,158,198,51,104, 199,98,44,97,154,130,83,120,6,207,160,21,23,224,34,156, 165,25,137,201,62,142,227,73,45,105,42,39,87,73,107,205, 106,96,57,77,221,53,229,2,217,79,21,74,132,175,250,177, 156,220,227,1,76,162,42,128,215,253,119,144,152,185,192,249, 170,73,49,189,177,187,126,194,138,10,166,16,70,107,67,12, 96,88,173,221,22,36,131,120,2,79,96,26,150,96,9,211, 26,60,130,39,240,4,58,176,12,203,26,138,61,96,139,178, 122,196,182,99,89,121,110,180,63,219,118,177,174,210,66,219, 134,130,228,110,204,88,128,228,184,40,191,228,254,183,53,77, 249,30,218,133,18,245,147,146,16,17,90,86,64,110,237,21, 18,10,91,40,9,21,161,192,53,56,31,67,157,255,36,182, 97,27,102,97,9,206,199,52,237,107,12,98,27,182,97,26, 46,194,133,13,2,32,218,172,69,186,114,48,165,238,143,141, 44,111,123,178,235,50,72,21,44,19,222,63,80,110,170,2, 208,196,52,0,186,251,159,156,254,39,226,157,122,162,78,87, 106,213,198,82,0,224,215,27,171,145,86,45,128,148,7,144, 73,28,28,143,252,40,30,194,67,152,135,197,56,31,29,218, 215,57,129,45,216,130,89,184,8,23,98,102,3,120,0,222, 10,138,9,15,111,229,205,207,76,191,76,223,223,133,146,222, 11,39,119,93,165,10,16,49,38,64,212,220,0,72,164,95, 104,154,179,168,101,35,77,129,98,255,228,188,135,208,218,38, 188,81,24,41,213,125,206,91,34,169,40,200,215,95,133,33, 219,82,250,233,12,44,193,34,99,179,240,233,88,134,11,48, 117,146,170,255,81,44,4,24,30,102,48,185,240,198,150,39, 159,155,189,12,154,135,202,245,160,132,145,31,252,179,147,253, 24,197,8,70,48,138,81,140,97,12,105,164,145,65,22,46, 34,64,10,38,107,172,254,94,35,144,115,255,191,77,83,212, 243,82,8,250,25,42,106,205,223,145,208,214,170,161,240,156, 175,174,76,47,36,163,65,26,185,184,208,154,50,75,115,238, 197,94,108,196,2,44,198,185,76,95,192,126,236,199,58,156, 133,139,176,116,18,66,138,30,81,214,66,101,93,36,37,39, 222,168,114,231,149,217,75,192,238,238,4,189,227,63,88,46, 92,196,122,50,80,237,67,0,9,250,51,227,211,244,78,221, 254,233,46,187,220,223,231,72,129,130,204,199,34,27,13,157, 66,148,32,164,164,34,105,102,135,197,100,121,174,191,27,187, 225,224,92,44,193,2,52,105,95,112,15,246,224,30,156,131, 101,88,162,53,22,77,228,177,205,99,46,85,122,21,53,10, 110,92,185,243,218,236,51,144,132,150,204,83,187,86,194,203, 179,174,244,154,83,244,10,57,30,202,159,63,242,99,250,188, 174,45,52,149,119,254,85,122,101,193,134,63,234,225,75,66, 169,149,112,142,189,183,193,216,213,102,33,99,32,33,180,131, 30,154,113,30,150,224,44,67,95,64,2,139,112,33,22,51, 70,98,34,142,171,177,93,89,83,225,227,18,55,168,252,208, 156,150,196,52,85,141,101,127,82,87,115,127,249,190,63,24, 58,145,15,0,70,48,138,49,164,144,66,102,34,134,0,204, 137,191,157,95,44,132,204,58,44,194,81,114,255,124,175,181, 147,79,146,122,139,40,142,146,97,149,29,123,239,149,189,100, 35,234,254,239,176,103,189,200,247,151,198,139,120,1,173,88, 132,37,56,131,105,30,122,9,47,161,9,139,113,17,206,157, 224,205,67,41,236,80,94,121,81,172,173,240,176,170,198,147, 227,196,28,74,76,243,110,35,60,207,79,121,242,236,36,194, 2,8,249,208,207,51,223,158,99,253,37,77,173,193,20,244, 248,218,0,105,142,188,252,59,58,50,16,30,67,224,230,31, 161,19,48,63,148,144,67,150,143,226,89,236,64,7,206,199, 18,134,82,52,141,231,240,28,90,112,1,150,97,65,157,158, 198,30,60,158,67,202,120,190,61,152,19,155,27,80,62,50, 227,68,98,129,158,195,130,212,190,86,129,156,226,226,2,170, 173,1,208,168,63,90,91,18,95,55,225,248,244,163,148,212, 148,28,24,57,135,247,23,90,6,64,237,4,80,97,24,114, 200,160,187,107,252,81,79,2,67,120,18,79,161,19,75,177, 24,179,181,175,63,134,167,240,20,218,176,12,203,24,95,161, 254,199,147,138,49,37,246,57,52,176,60,59,117,79,203,98, 239,110,174,190,33,234,110,31,86,238,198,124,56,104,45,67, 0,9,251,63,251,143,197,18,57,186,22,6,82,101,181,79, 175,22,124,1,220,157,232,8,68,253,254,6,240,24,30,197, 12,44,197,249,76,15,225,48,30,195,99,232,194,50,92,52, 193,206,31,120,66,89,95,139,247,151,229,29,47,182,47,171, 12,239,31,36,207,146,150,130,152,128,73,192,124,133,164,152, 254,107,154,51,175,253,97,106,151,17,126,196,54,72,66,138, 139,212,180,29,0,54,109,199,255,236,141,174,74,59,186,203, 124,170,194,52,137,253,73,191,63,239,156,115,177,4,231,163, 203,176,36,51,176,12,203,24,95,161,62,199,219,240,162,1, 239,14,166,131,179,209,228,237,79,79,189,4,76,189,137,227, 14,42,87,190,235,230,108,41,5,88,234,3,200,78,180,36, 160,144,11,128,109,95,16,197,82,185,83,220,111,29,101,223, 86,99,254,130,245,117,181,172,41,207,23,0,38,138,87,65, 155,80,248,2,100,27,15,54,195,0,237,254,28,169,21,36, 119,127,135,208,143,7,48,31,75,112,62,67,42,114,12,15, 226,65,204,197,69,184,144,105,52,174,183,20,224,78,88,188, 191,73,222,250,212,212,75,43,199,251,251,203,145,201,82,124, 37,192,90,25,0,141,251,239,244,115,157,63,144,247,84,40, 40,127,121,49,244,102,224,194,190,45,152,12,0,49,251,178, 138,27,224,249,2,244,136,15,198,164,34,88,102,194,18,12, 185,240,155,251,176,15,155,112,22,150,224,60,166,47,224,16, 14,77,0,2,178,87,144,177,120,127,131,60,177,99,198,165, 213,224,253,3,228,105,197,253,159,160,112,224,82,8,144,235, 255,251,123,55,33,43,146,26,227,203,103,172,131,205,193,235, 109,59,122,87,95,121,124,1,252,163,23,140,121,0,131,69, 228,179,6,185,159,223,192,27,88,143,115,176,24,231,50,125, 1,37,2,178,165,117,201,74,252,156,197,251,27,228,206,139, 115,150,85,137,247,247,151,167,129,137,94,5,208,206,253,61, 227,50,250,56,119,140,114,193,26,58,202,82,8,214,69,242, 62,24,82,232,191,120,88,113,37,124,1,114,76,230,199,71, 32,124,248,6,114,191,237,98,23,118,33,137,243,176,24,11, 152,133,47,17,144,45,70,115,157,25,0,139,247,231,228,226, 213,57,231,192,169,14,239,239,47,71,58,238,103,91,187,16, 160,136,254,159,242,79,28,54,95,69,252,171,173,57,60,208, 87,231,11,224,213,152,140,25,79,127,190,0,24,206,36,228, 234,16,66,99,49,228,130,147,76,177,61,104,9,206,52,16, 144,37,113,62,150,213,17,1,217,75,218,90,0,150,15,0, 251,230,204,113,90,170,197,251,7,200,51,241,30,12,22,191, 1,208,142,254,90,248,110,188,131,87,52,29,239,239,39,39, 248,49,8,240,124,1,38,51,18,134,47,0,236,108,208,90, 135,248,235,171,208,230,92,123,80,43,22,99,9,67,64,150, 193,11,120,33,79,64,118,78,29,52,15,189,194,239,127,141, 205,7,112,100,78,139,211,25,1,222,223,87,238,166,25,247, 159,162,76,10,214,42,4,40,238,255,201,191,225,249,125,133, 146,241,39,45,191,14,240,61,129,165,182,93,72,191,77,236, 241,161,213,243,5,8,86,14,173,153,184,116,109,199,99,134, 92,207,21,71,240,52,158,70,103,157,19,144,165,176,71,49, 195,150,15,64,12,206,28,77,156,17,5,222,63,64,158,81, 148,126,130,121,0,218,254,127,246,165,120,55,223,83,199,245, 233,169,93,121,144,148,73,245,34,28,133,27,192,203,23,160, 214,87,1,115,79,159,218,243,197,155,15,72,165,66,174,178, 171,246,43,56,249,147,139,213,111,52,128,237,120,28,211,177, 4,75,24,82,145,241,39,32,123,21,110,81,45,76,173,90, 13,38,79,77,59,212,116,174,208,186,250,161,236,236,213,203, 157,9,159,3,80,246,255,150,207,107,120,103,166,132,199,215, 6,160,237,215,60,95,128,76,31,226,207,23,0,205,56,8, 197,17,211,235,4,102,190,0,190,33,89,24,27,150,189,247, 116,2,219,176,13,179,177,4,139,25,82,145,241,36,32,219, 5,139,247,151,228,238,212,93,83,46,136,10,239,239,47,119, 179,241,56,254,181,13,1,138,21,128,51,22,227,195,36,37, 248,160,148,254,56,21,81,119,110,176,36,30,102,190,0,161, 21,248,184,29,218,107,130,212,164,162,154,59,0,252,248,2, 160,132,14,186,127,224,104,1,78,225,47,15,163,31,91,48, 15,75,124,8,200,102,99,25,150,49,141,198,113,141,221,202, 247,107,116,62,128,206,103,219,222,18,29,222,63,64,158,134, 185,173,181,238,13,128,80,250,255,156,182,191,212,155,108,212, 206,42,98,187,243,117,152,175,106,72,84,207,65,247,43,4, 184,131,65,5,120,118,65,189,241,88,24,214,157,239,84,40, 129,69,189,135,72,249,223,95,225,219,28,192,129,124,243,16, 71,64,118,24,155,177,25,167,99,25,46,52,54,26,71,57, 246,0,190,103,42,138,134,146,183,111,239,186,156,52,61,244, 38,242,84,234,186,170,228,153,184,159,110,178,6,251,127,62, 0,152,125,154,115,35,49,120,106,117,127,119,180,208,32,58, 190,0,72,134,68,48,153,1,110,175,230,114,1,142,214,187, 104,186,63,57,195,64,82,174,66,254,126,178,23,177,7,111, 98,35,206,198,18,156,199,244,5,212,142,128,108,143,197,251, 23,37,77,59,166,94,30,45,222,63,64,158,137,47,253,23, 183,1,208,18,128,211,111,166,164,74,215,69,74,244,165,22, 253,162,230,11,208,233,194,252,248,2,76,142,122,121,124,1, 252,252,250,253,59,12,157,153,139,221,216,141,4,206,193,82, 44,100,30,86,45,8,200,222,176,120,255,252,79,201,231,102, 93,28,57,222,223,95,158,142,214,225,175,117,8,80,242,1, 18,173,77,206,103,120,69,81,73,189,192,68,240,149,241,5, 4,165,231,204,113,190,126,135,168,146,47,128,79,15,6,221, 95,110,100,139,237,65,139,113,182,198,45,68,120,21,175,226, 46,44,194,50,156,31,3,1,217,30,88,188,191,0,32,94, 158,189,36,122,188,191,191,156,50,113,198,255,181,40,3,22, 67,128,249,191,39,206,208,1,53,92,98,141,180,60,57,42, 228,11,144,11,123,0,207,23,0,22,234,41,164,59,116,165, 136,62,136,47,64,206,71,232,168,67,29,173,72,90,244,169, 63,233,92,123,208,20,44,194,210,26,18,144,157,194,16,44, 222,159,224,188,62,247,76,36,163,199,251,251,203,61,57,128, 9,134,5,144,146,127,112,224,52,253,62,177,249,123,176,231, 251,232,110,189,238,70,123,83,107,48,240,5,8,152,188,14, 94,46,20,168,112,33,139,32,124,106,253,38,95,128,187,191, 32,15,198,127,125,8,2,35,120,22,207,162,13,75,124,8, 200,166,96,105,100,4,100,7,217,190,120,146,82,155,13,32, 63,56,103,154,104,243,86,227,132,210,117,66,74,181,46,34, 121,38,222,12,64,109,170,0,14,156,51,206,194,53,130,217, 227,202,193,227,235,251,58,39,15,230,11,16,204,252,250,30, 29,204,23,64,76,62,129,51,51,38,190,0,61,203,193,213, 62,244,124,1,64,24,198,147,120,18,93,6,2,178,81,60, 133,167,208,142,11,35,32,32,59,212,240,120,127,1,113,98, 14,57,211,128,232,241,254,65,242,108,86,137,246,38,96,18, 208,129,3,167,227,58,8,29,111,239,106,148,222,96,18,126, 58,37,56,7,185,241,254,174,96,144,1,28,95,0,2,248, 2,200,168,210,50,50,160,26,190,0,46,3,160,95,159,243, 33,4,78,226,81,108,195,76,44,197,98,76,215,30,192,80, 36,4,100,7,149,80,171,241,248,0,156,225,233,199,147,11, 41,30,188,127,128,92,168,85,128,200,107,2,201,216,246,126, 137,5,56,113,35,119,8,167,140,215,55,239,233,196,36,231, 76,81,53,192,161,183,252,146,59,128,137,47,0,62,37,70, 225,177,113,186,121,48,243,5,168,161,140,126,127,65,235,163, 206,127,12,91,241,16,230,98,41,75,64,118,10,15,227,97, 204,196,50,44,195,172,138,60,0,61,101,213,80,124,0,153, 105,111,182,44,142,13,239,31,32,167,108,156,9,192,184,61, 128,130,250,39,22,44,194,82,185,188,194,197,194,92,14,64, 142,165,85,246,64,111,161,15,129,120,124,50,148,239,212,220, 189,63,49,40,49,124,69,64,117,124,1,96,238,207,204,110, 44,148,76,67,238,154,253,232,199,253,56,195,64,64,118,20, 15,224,129,138,8,200,142,1,13,139,247,7,8,93,47,78, 185,40,62,188,127,32,31,192,132,109,4,146,104,192,218,174, 21,76,190,157,203,134,131,173,154,123,205,128,170,80,122,87, 189,153,47,128,140,181,123,93,229,116,31,65,55,13,100,100, 10,16,172,243,111,226,11,0,204,96,99,253,254,248,174,130, 220,127,75,4,100,139,152,211,11,75,4,100,23,50,141,198, 252,56,142,70,197,251,19,4,218,159,238,184,36,86,188,127, 48,31,64,172,25,128,120,12,128,144,227,127,56,226,70,174, 225,213,132,215,231,178,245,102,190,0,97,144,243,124,1,208, 42,14,97,248,2,244,251,227,130,9,151,85,97,84,196,23, 80,254,250,120,77,200,235,120,3,27,176,48,18,2,178,163, 64,99,226,253,33,208,242,228,212,203,226,197,251,7,202,179, 152,160,148,96,30,20,192,194,11,156,115,213,186,62,192,119, 224,123,187,254,205,124,1,170,195,207,49,248,122,85,220,97, 202,140,229,240,5,240,117,139,168,249,2,132,167,66,224,191, 62,65,124,10,2,185,230,161,28,1,217,18,44,96,250,2, 194,18,144,157,64,35,226,253,1,32,241,244,204,203,98,199, 251,35,36,31,0,63,168,94,13,128,28,0,188,223,175,134, 78,140,83,31,196,23,224,165,244,244,227,11,144,247,72,19, 95,0,16,230,254,120,250,175,106,249,2,252,238,95,149,59, 161,215,167,244,247,25,188,132,23,49,5,231,97,105,133,4, 100,39,27,17,239,15,32,241,252,220,139,227,199,251,7,202, 179,90,147,64,196,225,64,50,22,229,151,122,0,157,171,133, 86,252,144,129,15,130,113,202,43,225,11,224,26,106,85,80, 145,218,133,37,12,39,13,233,46,63,199,23,128,64,190,128, 48,247,199,55,12,87,190,62,106,250,116,12,207,227,57,180, 85,68,64,150,110,60,188,63,8,206,171,115,206,35,39,126, 188,127,144,60,155,141,55,0,64,12,135,214,122,207,255,77, 32,121,218,244,246,175,8,33,55,225,58,69,21,113,180,228, 153,55,53,163,214,207,213,223,21,134,136,88,20,101,66,73, 238,20,96,55,65,13,32,66,185,126,216,249,189,127,167,58, 151,66,251,45,174,246,31,221,250,192,179,171,229,232,72,15, 224,121,60,139,33,180,50,41,192,44,14,225,89,60,142,227, 104,193,84,207,149,79,96,42,250,49,162,52,73,11,230,250, 147,71,158,216,59,119,166,104,83,145,34,38,118,136,56,229, 238,227,199,95,69,198,243,79,225,68,32,23,20,205,41,1, 241,230,0,28,136,233,239,116,28,82,212,76,119,138,101,21, 168,158,47,0,17,241,5,16,51,191,244,128,24,24,18,87, 71,224,201,78,116,86,3,19,48,89,24,252,147,224,245,81, 141,220,32,30,199,118,76,199,98,31,2,178,78,92,88,36, 32,203,96,17,206,196,111,138,213,128,6,224,3,56,50,107, 138,232,4,83,127,137,1,239,31,32,215,192,64,52,17,60, 0,39,87,253,207,121,0,243,254,84,92,44,43,129,227,217, 189,28,99,235,142,195,112,179,20,98,97,161,236,168,96,142, 3,209,119,121,82,246,222,210,44,250,254,43,216,4,33,183, 215,251,205,47,36,111,130,164,226,147,48,222,159,227,185,162, 163,197,85,193,243,115,235,163,26,179,81,236,197,83,216,133, 49,116,48,48,226,20,246,226,73,236,192,16,218,241,24,8, 73,116,224,229,6,225,3,192,192,236,161,166,211,184,66,48, 124,136,227,227,146,103,31,57,241,6,210,200,32,131,180,180, 255,215,173,7,32,212,28,64,226,106,50,118,203,235,251,63, 199,23,96,94,52,142,47,128,107,248,209,201,61,56,190,0, 61,218,6,162,225,11,16,161,249,2,132,34,87,27,141,195, 241,41,248,245,20,120,61,134,126,28,246,33,32,59,142,45, 216,146,255,249,108,246,239,39,33,31,192,216,140,67,205,231, 213,16,239,31,32,167,172,230,84,70,164,248,113,247,1,228, 3,128,115,22,224,116,192,31,15,47,124,206,228,81,187,226, 73,115,210,203,225,11,16,76,47,34,52,199,90,48,109,69, 124,158,29,8,230,11,208,187,189,128,234,249,2,212,217,73, 201,5,240,124,10,124,177,244,0,14,96,51,206,52,16,144, 121,189,207,73,207,7,224,78,221,213,114,97,45,241,254,129, 242,44,98,30,241,97,1,4,4,156,214,183,4,227,225,229, 54,90,104,177,189,96,189,2,50,240,5,16,83,228,9,131, 199,215,177,138,2,225,249,2,136,117,42,57,149,172,158,47, 0,33,248,20,130,214,71,199,187,191,233,75,64,182,71,73, 85,78,78,62,128,142,29,109,151,212,22,239,31,40,143,189, 10,144,140,77,249,5,4,68,243,165,96,89,120,136,41,134, 113,231,165,235,251,61,135,215,142,134,47,192,212,119,31,134, 47,192,191,214,15,246,51,127,190,128,114,214,71,231,83,8, 90,31,146,230,47,172,87,22,187,241,26,154,176,16,75,112, 142,231,213,24,197,131,152,252,124,0,83,182,119,94,94,115, 188,127,144,124,194,193,129,133,148,10,116,112,185,25,143,175, 246,181,85,198,23,96,102,21,4,163,180,113,242,5,200,175, 151,31,95,128,206,103,224,176,243,115,124,1,81,172,143,222, 48,228,253,14,25,188,130,157,104,198,121,88,140,249,16,120, 3,91,60,253,128,147,149,15,160,233,233,233,151,215,30,239, 31,36,119,221,137,21,2,8,217,3,152,213,230,44,115,193, 225,225,121,60,190,186,175,185,30,185,107,228,11,128,198,23, 192,67,130,228,91,12,198,227,3,225,249,2,132,150,151,32, 109,126,142,142,84,53,86,196,242,1,112,128,227,40,249,20, 184,130,231,24,94,192,243,13,129,247,23,0,18,59,102,93, 50,30,120,255,64,121,198,243,232,40,14,111,32,214,36,224, 236,11,40,193,57,209,92,62,95,48,251,113,244,124,1,164, 213,131,77,120,124,93,49,245,61,85,221,211,77,124,1,252, 201,131,102,190,0,104,69,81,98,65,79,209,175,79,3,226, 253,1,136,151,230,92,48,62,120,255,64,62,0,87,81,246, 200,131,128,120,142,158,205,155,128,150,183,232,213,65,50,244, 209,9,229,168,78,117,143,212,123,250,132,210,124,0,173,96, 167,30,207,165,30,55,174,171,143,183,60,39,251,13,124,207, 159,249,250,194,80,207,240,38,162,132,114,255,254,235,3,168, 124,1,254,235,35,171,122,184,245,129,214,76,76,158,245,161, 73,41,23,175,205,61,43,183,17,122,115,35,208,0,105,227, 33,119,39,92,21,64,120,217,128,18,139,213,54,20,40,206, 167,31,63,111,16,95,128,222,95,232,199,23,0,35,95,0, 202,220,163,9,48,242,21,112,25,126,157,41,128,235,13,8, 94,159,32,190,128,234,248,20,248,59,154,236,120,127,1,236, 159,51,61,215,246,59,46,120,255,32,185,27,119,21,192,137, 107,247,135,128,131,197,38,87,159,235,149,135,161,67,14,74, 182,28,204,174,204,53,212,240,68,157,130,113,146,121,149,54, 247,2,154,91,147,100,101,21,134,25,201,72,78,198,175,15, 5,174,15,42,94,31,48,249,7,175,137,146,177,235,164,4, 76,19,93,46,78,204,18,137,233,114,142,68,199,235,143,159, 156,241,0,34,14,7,146,49,236,254,249,159,90,133,88,76, 70,192,138,105,55,54,225,241,161,117,253,19,203,205,99,230, 11,224,207,22,168,5,95,128,218,137,46,148,61,93,4,172, 143,131,104,249,20,188,158,150,195,28,145,38,88,163,3,76, 54,62,0,103,104,198,241,166,133,227,138,247,15,146,199,94, 6,140,207,3,112,230,158,46,218,245,250,186,96,236,69,24, 185,142,129,227,94,127,53,174,53,97,4,244,29,50,215,67, 79,21,223,159,222,175,175,223,159,8,248,123,170,106,125,28, 205,87,10,243,247,164,173,143,96,182,28,181,150,161,38,64, 39,164,60,51,109,111,203,66,126,91,53,157,241,92,107,57, 98,103,4,138,49,9,216,121,142,215,30,168,16,219,82,231, 147,80,98,53,46,110,211,19,111,66,122,141,29,165,10,201, 27,2,161,213,131,213,207,212,123,33,6,224,43,180,224,196, 171,124,188,156,55,100,164,205,169,222,95,208,250,16,187,62, 80,186,43,252,214,199,60,191,252,153,236,79,120,43,83,19, 84,78,93,47,76,89,172,215,66,248,132,220,120,201,93,87, 177,10,145,155,2,39,150,221,31,16,16,205,231,153,240,248, 66,82,21,53,75,94,194,179,233,177,124,206,106,58,74,145, 48,28,94,95,206,153,171,138,37,159,41,236,63,191,48,242, 5,232,237,16,220,254,171,255,61,143,244,11,94,31,39,96, 125,202,227,51,8,94,31,181,57,91,207,173,79,20,121,235, 83,237,23,11,37,226,230,0,57,227,43,55,54,2,81,61, 123,0,249,119,199,89,168,38,172,120,167,221,49,152,4,181, 53,71,223,11,85,151,91,176,103,240,10,102,127,135,214,96, 163,123,14,92,194,77,72,217,125,191,249,73,137,56,193,124, 63,189,112,194,237,242,186,119,17,180,62,84,209,250,56,33, 215,135,52,87,21,76,145,178,190,229,45,79,76,187,76,174, 239,240,71,189,143,183,220,117,217,221,159,234,213,3,144,223, 183,249,96,219,97,4,203,233,43,223,146,144,246,58,179,217, 112,124,98,109,190,138,160,242,5,8,166,45,84,128,195,227, 243,254,9,12,247,199,243,29,200,243,11,198,55,50,175,15, 133,94,31,33,173,15,66,173,15,12,235,195,241,41,128,73, 99,201,25,246,250,150,55,61,61,227,173,128,78,198,193,37, 116,199,87,46,98,111,5,118,34,85,126,217,3,152,47,91, 93,199,64,210,5,169,243,79,48,138,168,159,192,163,55,180, 8,45,154,229,103,18,198,18,159,96,73,58,132,199,177,246, 107,248,17,218,245,85,98,47,193,86,164,249,214,38,254,250, 194,167,33,73,48,161,0,180,128,197,188,62,194,176,62,94, 99,168,115,43,233,61,13,58,134,178,222,228,137,231,102,93, 172,183,62,233,127,87,15,242,172,27,103,15,64,180,6,64, 127,147,206,208,147,26,250,139,174,190,182,80,114,211,38,69, 33,246,37,37,134,139,87,24,210,91,92,170,11,76,2,15, 128,17,65,38,152,196,141,240,217,111,137,149,130,157,211,111, 125,192,38,5,121,67,66,33,214,7,33,214,135,152,245,81, 51,8,42,178,177,254,228,206,206,217,139,224,120,9,184,132, 82,52,149,91,239,199,87,238,102,165,66,198,132,65,3,10, 136,211,218,69,151,252,154,56,138,147,22,204,143,203,227,241, 97,32,246,80,155,89,121,60,60,216,14,60,98,92,125,32, 28,95,128,233,128,208,112,124,1,168,154,47,96,188,249,20, 188,134,165,222,249,0,18,111,206,61,13,45,117,132,247,15, 144,75,57,128,88,202,129,177,37,1,167,206,5,56,15,128, 12,205,154,130,141,193,133,49,87,192,33,2,136,137,81,29, 197,65,33,182,122,110,58,241,71,24,88,134,76,30,132,206, 1,232,40,75,163,183,228,242,223,172,240,125,156,144,235,195, 177,21,113,103,19,240,235,35,24,148,4,25,215,71,24,214, 71,205,183,203,12,6,117,32,63,60,171,21,157,240,152,85, 239,90,123,53,173,94,228,152,64,33,128,18,90,183,157,6, 198,85,55,101,188,5,27,131,171,241,170,78,180,41,43,133, 195,254,149,96,11,107,126,81,60,25,138,114,142,241,90,124, 64,160,255,189,222,133,31,221,250,8,105,125,68,197,235,227, 45,213,250,175,15,12,85,30,242,185,223,241,147,59,167,230, 140,38,103,113,199,198,242,231,73,215,131,60,235,22,173,3, 21,217,0,235,182,15,64,122,147,156,217,194,16,129,195,23, 185,38,12,145,177,185,244,7,232,167,238,241,78,176,92,116, 241,75,191,233,157,243,196,230,233,85,18,79,4,94,95,141, 168,5,91,13,40,119,125,248,210,40,119,164,72,148,235,195, 183,110,201,17,57,87,191,26,23,249,216,204,67,201,51,9, 60,248,25,90,167,67,125,200,221,108,212,36,160,241,135,0, 249,55,196,153,234,223,246,2,128,201,75,11,214,137,85,241, 109,252,17,27,126,123,62,41,157,6,92,93,64,176,115,8, 38,65,232,77,232,9,207,158,40,88,213,209,123,238,56,245, 50,103,232,195,172,15,124,238,93,190,63,211,250,40,92,110, 17,172,143,25,143,143,218,203,221,169,59,155,22,169,121,12, 210,76,69,189,201,243,244,223,156,15,64,245,106,0,242,223, 194,233,130,161,169,68,72,138,173,186,203,97,248,2,252,171, 239,60,95,128,142,199,175,142,47,0,101,242,5,56,198,202, 133,223,250,160,162,245,169,7,62,5,53,46,31,95,62,128, 206,29,237,203,234,17,239,31,36,207,102,20,213,71,212,245, 128,168,41,193,138,239,97,98,26,135,55,231,28,118,24,210, 96,60,95,0,2,168,179,253,0,66,193,120,120,254,254,130, 249,2,76,159,155,154,149,193,144,125,249,175,207,68,230,83, 24,111,62,128,246,237,157,151,215,41,222,223,95,254,120,54, 167,248,178,31,80,199,125,0,158,231,64,211,56,32,166,31, 30,93,127,205,205,232,124,110,55,162,128,124,189,26,213,59, 204,203,227,143,199,135,241,142,200,55,155,175,215,166,245,170, 186,96,206,167,215,151,119,252,248,20,116,154,177,242,248,20, 198,143,15,160,249,169,105,151,215,43,222,223,95,62,178,182, 168,252,234,63,117,217,7,32,61,123,103,26,177,47,101,73, 85,92,165,232,22,39,95,128,172,120,114,205,85,77,195,152, 240,248,106,153,208,204,23,224,61,196,132,187,107,149,171,128, 227,11,32,237,254,163,95,31,206,176,152,249,20,244,35,53, 194,175,15,129,231,67,136,159,15,32,249,204,204,75,168,126, 241,254,62,114,113,112,255,43,112,65,197,195,192,92,37,12, 168,227,36,32,32,48,205,212,147,231,117,208,204,48,224,241, 224,11,144,23,164,82,190,128,48,120,124,1,19,95,129,126, 253,248,248,20,196,56,243,41,200,158,65,28,124,0,226,197, 57,23,66,212,47,222,223,79,158,234,203,202,39,1,171,30, 64,221,49,2,73,137,99,103,170,107,140,66,137,137,44,245, 254,58,248,68,233,28,115,47,119,126,47,207,219,11,132,63, 128,203,68,25,46,24,47,67,102,236,37,246,239,189,123,85, 16,235,32,41,6,83,63,37,152,187,39,193,246,39,122,239, 143,0,195,253,17,115,138,178,121,125,168,194,245,145,251,11, 133,49,164,168,86,142,93,115,206,70,82,254,214,242,251,161, 51,241,214,141,124,172,255,9,184,112,181,227,64,35,246,1, 146,17,239,251,165,255,223,162,22,104,200,200,171,175,159,215, 194,15,98,14,215,86,51,222,220,249,64,122,210,141,207,23, 240,109,176,250,253,57,218,177,166,188,10,201,13,31,252,217, 124,196,228,8,4,155,244,227,13,9,180,53,149,239,143,59, 100,68,63,45,193,63,169,231,151,91,169,124,125,160,29,133, 66,26,128,186,42,249,254,121,51,209,38,88,140,128,154,140, 171,55,57,144,189,107,104,164,168,254,186,17,168,107,62,128, 220,99,105,81,59,227,28,31,167,208,204,23,32,2,248,2, 132,47,95,0,25,92,222,120,249,2,194,240,21,16,184,206, 193,232,215,39,58,190,128,120,248,20,226,226,3,192,241,89, 66,76,175,119,188,191,122,240,111,105,28,127,160,120,24,120, 54,175,250,110,244,41,192,248,56,1,145,104,81,31,190,63, 222,220,132,135,215,29,125,25,15,111,42,253,153,248,2,68, 40,190,0,81,21,95,0,24,229,246,203,5,192,24,107,87, 191,62,193,124,1,227,207,167,16,61,31,128,24,154,121,162, 105,94,253,227,253,161,117,76,228,255,247,232,209,163,200,230, 255,41,24,129,88,76,64,244,132,32,249,255,186,45,58,30, 222,220,137,31,134,47,64,84,192,23,0,159,238,125,190,245, 198,212,208,83,46,95,128,96,248,10,120,190,0,138,104,125, 252,88,16,234,159,79,33,82,62,128,244,212,61,45,11,235, 21,239,15,207,113,178,196,164,52,5,8,67,247,32,11,183, 104,2,178,69,19,64,81,99,2,99,11,1,68,139,254,114, 8,230,68,28,97,216,97,244,221,136,202,226,11,224,43,210, 254,124,1,220,252,186,163,204,243,5,8,246,254,204,124,1, 130,157,223,143,47,160,124,62,5,63,190,0,98,101,227,203, 167,16,17,31,0,77,125,161,117,105,253,224,253,245,214,61, 53,199,68,42,146,113,239,161,221,200,34,35,249,0,106,34, 176,142,207,6,4,68,139,3,199,140,135,247,99,179,231,249, 2,0,30,143,174,95,31,74,6,92,111,81,33,6,66,28, 134,47,64,176,32,78,249,254,195,243,5,160,108,190,0,71, 129,138,148,199,23,0,35,95,64,37,124,10,48,28,37,22, 21,159,66,53,124,0,173,79,117,92,86,31,120,126,0,82, 198,131,216,247,22,90,22,32,181,42,155,241,236,254,37,35, 64,209,195,131,227,160,4,3,208,222,162,226,225,249,44,118, 181,124,1,38,110,193,210,239,59,154,237,213,123,224,121,15, 66,149,147,49,94,134,177,94,31,37,95,0,127,150,112,88, 190,0,83,254,190,50,62,5,193,174,95,116,124,10,64,165, 124,0,45,219,167,93,86,15,120,126,206,59,17,158,142,94, 161,105,114,241,47,71,14,61,99,80,255,186,110,5,150,190, 113,83,66,69,241,195,128,135,231,89,239,170,229,11,0,84, 0,16,23,139,115,243,131,117,239,117,232,113,16,95,0,2, 208,125,229,243,5,68,185,62,213,240,5,192,55,203,16,23, 159,66,56,62,128,230,167,102,92,62,158,120,126,176,105,77, 72,221,15,94,117,7,19,28,184,119,141,142,228,213,62,147, 15,3,220,137,88,6,52,114,240,132,193,155,115,233,33,160, 26,190,0,176,138,165,238,131,164,237,187,96,243,9,229,240, 5,144,239,253,241,223,159,227,11,136,103,125,160,244,84,168, 124,1,230,245,169,5,159,2,175,82,38,62,128,196,179,51, 223,50,62,120,126,249,40,18,111,168,71,10,58,18,82,216, 160,182,76,17,128,163,91,242,170,159,201,27,1,213,7,136, 116,36,99,209,125,38,174,52,31,100,169,118,224,235,9,41, 189,115,80,63,235,69,62,143,143,24,69,215,27,107,228,199, 4,99,5,93,109,139,225,122,207,244,249,245,96,134,124,219, 89,249,251,227,214,71,13,21,184,245,145,103,224,186,21,1, 24,83,159,250,250,192,167,195,160,146,245,49,5,123,250,250, 16,155,99,208,225,90,206,203,179,207,23,142,220,235,40,148, 242,154,218,11,89,141,92,53,74,228,9,236,184,191,87,131, 2,181,171,33,255,211,214,19,199,24,247,63,134,244,95,244, 30,0,233,177,0,49,120,120,71,113,49,117,60,60,25,43, 3,114,167,157,126,206,29,127,42,176,95,173,128,11,23,56, 160,79,180,124,1,20,17,95,64,181,124,10,20,122,125,162, 230,11,16,101,174,79,16,31,128,243,198,236,211,157,150,120, 241,250,185,255,239,26,61,19,48,127,79,154,211,239,245,22, 244,36,224,208,125,197,253,63,163,213,1,98,96,7,142,167, 10,64,34,171,186,254,250,203,237,74,81,99,88,60,62,80, 14,30,223,140,77,7,83,123,230,94,83,26,119,190,128,56, 249,20,120,116,70,208,250,212,3,159,130,98,86,250,231,180, 57,157,113,226,245,193,4,52,58,253,9,95,223,215,17,44, 106,96,80,12,149,247,28,222,45,237,254,25,79,35,240,132, 73,2,2,200,100,85,188,182,78,58,45,180,82,24,191,255, 65,91,42,62,174,5,147,237,231,58,217,244,72,95,117,91, 73,75,137,5,241,5,136,64,60,188,31,15,48,192,195,90, 253,195,130,241,230,83,224,210,99,84,33,95,64,249,124,10, 146,114,158,156,61,230,204,86,19,110,64,52,120,125,121,125, 73,241,63,72,241,101,84,126,2,185,92,168,115,37,201,149, 140,209,85,217,92,212,239,221,255,99,13,1,162,246,0,242, 55,151,206,248,225,181,1,238,252,121,30,57,47,180,28,170, 218,97,197,227,225,69,76,124,1,240,225,11,16,40,151,175, 0,202,222,192,227,233,249,245,169,37,159,66,24,190,0,3, 32,183,2,62,5,238,254,132,97,51,16,192,232,204,254,166, 69,209,226,241,161,189,25,92,11,178,30,249,154,0,190,166, 138,191,178,146,195,71,158,145,18,127,153,120,75,128,241,120, 0,4,2,141,101,195,224,181,253,248,2,248,61,154,231,19, 224,228,113,241,5,152,239,223,97,190,95,101,124,1,20,98, 125,38,51,159,66,176,220,115,111,217,169,187,154,23,69,131, 199,247,70,232,130,141,218,193,236,228,94,185,156,244,227,102, 245,178,24,170,191,153,93,51,58,86,204,253,103,20,48,208, 132,8,1,138,126,208,40,29,187,126,244,72,214,231,124,118, 211,249,244,126,7,95,9,173,185,148,12,21,102,176,24,59, 14,47,200,85,196,133,65,81,249,99,59,121,52,129,80,202, 114,60,91,46,7,61,18,80,79,140,23,6,22,229,240,235, 35,124,214,199,148,194,227,238,175,190,214,7,64,145,238,83, 207,82,240,76,187,186,92,125,121,85,135,222,152,227,134,222, 213,72,70,40,59,159,80,148,175,127,236,33,73,249,99,46, 0,198,227,1,20,227,148,29,47,29,123,244,48,78,96,204, 120,126,187,255,249,244,166,138,183,74,96,173,166,81,248,58, 183,154,238,50,19,149,3,60,184,86,199,11,234,247,71,26, 108,71,135,186,168,199,117,130,113,56,245,86,153,112,235,67, 198,245,129,18,104,84,122,127,194,144,207,24,175,245,105,123, 188,243,82,25,35,192,3,133,84,57,180,159,188,177,186,92, 26,230,243,252,42,73,47,159,3,40,169,132,124,125,110,126, 122,240,212,9,79,242,79,133,0,213,249,217,128,164,244,100, 18,136,246,17,70,48,2,7,237,104,69,146,77,180,145,22, 121,66,33,206,224,146,133,92,247,188,78,73,13,99,81,79, 205,241,171,204,55,178,147,168,86,173,73,170,236,251,151,214, 228,244,38,25,26,123,73,235,48,227,238,207,127,125,72,59, 88,68,93,31,213,140,192,135,132,84,142,132,131,238,143,195, 31,168,184,15,157,105,200,180,62,164,193,169,76,243,55,63, 49,245,10,21,114,43,207,16,180,135,147,84,131,210,217,6, 204,249,13,63,191,192,36,37,86,94,152,121,208,91,0,204, 42,61,128,177,29,15,234,68,184,247,67,62,184,128,14,20, 150,113,0,253,56,140,161,60,146,129,195,115,151,119,62,189, 96,91,52,189,95,201,175,7,94,71,237,115,145,167,238,136, 251,241,5,232,153,109,51,30,94,24,102,69,136,239,111,150, 155,248,2,252,215,103,34,243,41,36,158,158,113,153,172,252, 102,60,62,95,139,247,150,229,248,40,95,72,167,114,8,237, 175,244,43,234,153,0,255,249,139,123,230,238,35,123,52,4, 64,172,209,127,124,125,0,249,219,205,28,104,246,56,7,25, 156,196,41,76,65,43,90,160,50,196,10,246,161,169,110,156, 163,164,18,5,147,91,54,213,164,185,174,64,190,160,165,243, 224,241,28,251,28,187,175,90,176,35,105,111,211,115,234,252, 157,22,86,172,244,125,157,178,214,135,128,200,214,135,12,228, 29,65,235,163,238,239,220,185,4,230,245,225,153,16,229,251, 115,94,152,189,140,132,247,119,84,186,60,189,17,151,171,68, 112,16,99,185,7,213,116,125,176,253,0,20,106,126,249,56, 85,0,24,203,21,0,189,45,64,110,188,5,192,232,13,128, 236,3,184,217,131,122,54,117,20,35,112,208,134,41,104,214, 106,210,164,193,84,185,131,64,76,14,180,186,180,34,212,139, 200,183,167,146,79,238,155,175,148,251,29,91,78,158,198,39, 19,123,45,49,205,51,220,119,114,67,172,143,9,158,75,12, 23,96,24,69,197,56,174,143,48,173,207,171,51,22,80,82, 125,191,244,83,137,248,208,17,108,81,142,152,146,172,220,211, 199,151,108,133,198,172,200,205,175,190,185,202,29,12,246,63, 107,216,255,99,220,253,163,247,0,188,30,14,13,29,106,99, 241,210,46,134,48,132,4,218,49,5,9,95,188,183,252,66, 5,241,5,128,121,121,204,124,1,66,67,1,112,205,198,65, 93,115,97,249,2,0,127,190,0,189,67,159,208,72,124,10, 65,235,163,144,200,238,155,61,3,109,106,180,31,84,125,23, 74,150,64,104,73,97,174,119,146,24,15,205,188,187,155,231, 231,178,20,165,111,152,189,51,157,98,26,128,106,16,2,196, 209,9,152,191,221,161,227,72,153,241,210,89,156,196,33,28, 207,87,9,0,14,239,93,41,95,0,183,247,192,240,95,14, 143,111,190,190,254,179,163,205,195,243,1,112,4,150,38,90, 51,239,157,56,6,247,17,90,213,188,81,248,20,112,108,166, 192,116,193,152,34,57,163,239,135,215,87,211,182,196,100,1, 84,195,17,26,207,31,114,126,207,147,163,163,91,181,6,32, 55,238,2,96,156,101,192,92,51,144,75,123,130,240,212,35, 56,142,131,56,137,52,194,225,189,133,129,162,42,24,15,207, 53,247,170,77,55,102,190,0,32,152,47,192,143,229,142,175, 166,251,179,238,169,40,254,202,249,20,194,172,15,48,49,248, 20,156,193,105,39,156,211,73,81,55,157,167,200,68,157,39, 148,215,20,44,209,58,49,5,66,37,105,7,176,152,192,74, 230,167,251,7,79,41,13,64,217,248,11,128,209,87,1,72, 45,4,138,189,97,240,212,192,16,142,162,31,131,200,26,240, 222,194,135,14,147,139,70,57,60,60,213,144,47,0,33,240, 240,196,30,234,237,205,169,151,203,167,16,180,62,240,93,159, 9,194,167,144,238,120,179,233,28,245,47,72,217,169,117,69, 86,19,84,58,176,136,199,243,67,9,107,92,45,111,19,197, 252,67,235,180,6,32,55,254,2,96,60,85,0,233,0,35, 218,27,30,111,157,193,169,98,149,192,1,127,254,187,8,224, 11,144,79,228,33,192,216,87,30,158,47,64,173,7,155,58, 20,170,225,11,16,190,120,120,32,44,159,66,208,250,64,91, 159,234,249,20,106,177,62,197,122,61,117,60,223,114,9,148, 191,128,143,134,8,6,136,166,42,166,80,122,38,192,238,237, 250,42,250,207,79,26,166,212,56,255,206,99,123,149,238,255, 154,20,0,227,44,3,230,13,65,246,64,130,73,42,145,102, 45,75,242,81,140,194,201,153,1,42,156,233,166,150,199,252, 206,245,17,12,229,38,95,226,19,218,105,61,0,119,68,25, 41,7,90,240,93,98,250,252,50,67,172,80,204,20,31,247, 18,83,17,241,114,35,168,229,65,29,195,22,126,125,248,249, 77,40,195,74,214,135,180,118,162,106,215,167,229,137,150,203, 193,86,50,212,215,143,143,208,115,195,213,140,33,135,231,39, 205,15,32,229,157,49,181,6,151,59,255,216,157,217,172,39, 253,87,179,2,96,60,85,0,239,57,230,68,251,43,193,99, 187,24,193,48,154,196,20,180,33,193,58,170,225,248,2,128, 32,188,57,1,134,102,87,176,9,41,140,59,95,64,45,249, 20,76,39,3,194,88,4,140,151,79,161,105,123,251,229,124, 25,79,119,214,229,122,6,143,231,7,84,54,127,98,86,67, 176,254,147,105,254,114,248,4,242,127,113,242,240,243,76,1, 208,123,16,72,172,35,198,42,0,40,125,160,114,60,118,26, 3,56,132,163,24,102,187,180,56,62,1,192,124,20,152,9, 29,111,114,171,213,108,54,212,66,20,27,77,151,203,23,32, 124,249,2,28,95,214,188,70,227,83,104,122,162,227,114,221, 53,215,153,129,121,60,63,49,201,59,120,122,244,245,180,159, 183,250,207,147,128,153,241,254,97,230,47,200,211,185,2,160, 202,1,20,19,5,104,188,30,128,198,214,148,62,216,138,234, 206,79,31,195,24,78,162,181,24,18,168,231,191,115,177,175, 185,167,143,235,123,19,62,254,129,137,47,192,27,247,6,243, 5,168,78,45,223,168,74,12,30,159,79,56,249,241,41,148, 90,133,130,248,20,130,248,2,234,139,79,1,59,58,46,229, 43,238,65,71,136,65,58,109,129,195,243,155,14,237,128,143, 171,143,0,30,160,160,249,75,241,192,241,109,134,246,223,216, 11,128,113,123,0,0,13,30,22,196,47,82,57,120,109,194, 48,142,226,160,24,68,154,221,193,72,250,42,58,94,158,140, 175,95,88,60,188,154,107,55,225,245,121,15,194,123,127,38, 60,60,161,28,190,0,24,230,55,177,242,155,255,126,98,240, 41,56,47,119,45,134,163,166,238,132,230,146,243,204,193,170, 115,79,62,217,121,147,156,175,244,83,40,230,98,149,35,64, 186,151,77,249,2,160,190,255,199,30,253,199,149,3,240,24, 129,193,20,14,98,94,52,231,167,187,24,192,0,90,208,138, 41,12,194,155,103,169,149,203,110,122,58,17,224,89,245,136, 201,180,19,227,160,154,162,100,191,254,59,115,127,155,158,133, 55,93,211,235,118,114,117,2,253,206,5,211,95,23,229,250, 192,216,159,24,102,125,248,231,87,244,90,94,239,152,39,90, 76,237,187,96,195,27,242,193,251,235,85,9,174,167,80,15, 68,200,144,208,54,93,159,124,19,149,185,107,14,110,80,114, 255,37,231,191,70,38,32,250,42,128,55,17,8,218,43,230, 69,121,126,250,24,198,0,180,162,13,45,4,193,159,15,15, 22,180,41,164,166,78,189,14,32,36,24,43,152,58,2,1, 236,245,137,173,101,115,17,55,105,176,29,97,84,84,61,79, 78,76,32,194,191,194,66,203,88,144,33,29,24,245,250,232, 53,5,63,132,128,122,127,236,250,28,236,104,67,87,80,167, 191,186,90,208,126,147,175,36,233,138,76,198,74,82,12,243, 191,124,116,159,167,1,72,143,255,49,49,67,0,175,167,181, 55,142,243,213,71,112,20,7,197,0,50,134,158,56,249,255, 59,224,58,213,0,48,61,110,60,207,142,57,41,88,72,214, 113,7,91,169,231,33,9,214,105,246,59,227,24,204,253,233, 117,118,33,221,135,185,53,74,24,234,26,81,174,143,94,85, 16,210,250,152,169,224,133,22,98,1,2,56,209,150,22,115, 204,209,56,41,101,72,213,103,33,205,179,240,143,219,245,218, 75,16,159,64,53,243,143,173,102,186,255,106,86,0,140,211, 0,20,125,0,119,95,92,231,171,187,24,64,63,142,96,48, 223,44,5,101,135,50,227,221,29,150,24,76,85,155,240,124, 1,106,150,92,109,251,229,10,153,165,89,184,115,124,195,241, 5,8,45,247,46,203,213,108,251,196,225,83,40,94,117,164, 173,63,113,38,152,252,58,177,173,185,58,95,143,220,120,43, 216,134,29,78,78,140,113,224,209,255,85,204,127,252,232,75, 76,3,80,205,10,128,241,24,0,82,206,61,216,31,239,249, 235,105,156,194,33,156,196,40,136,4,19,123,146,114,44,57, 223,223,46,159,153,167,43,170,254,178,242,61,1,230,254,126, 98,20,67,151,251,157,204,7,230,254,161,220,191,255,253,57, 62,13,195,196,48,10,134,95,31,193,172,143,40,107,125,216, 179,8,179,205,175,58,231,131,9,27,228,226,177,137,103,199, 155,48,4,91,148,211,187,66,205,254,1,63,63,66,205,15, 118,254,204,234,116,74,105,0,170,105,1,48,174,28,128,212, 9,144,57,216,2,176,172,108,106,228,84,141,124,24,195,112, 68,27,90,145,100,234,205,168,154,47,0,134,115,105,194,225, 241,185,84,34,65,199,200,187,158,25,77,217,3,2,140,77, 183,149,242,5,84,191,62,241,240,5,52,61,211,116,25,152, 40,156,88,215,93,48,29,1,106,118,94,48,249,20,248,164, 235,72,163,137,171,108,126,48,199,138,35,123,252,81,100,198, 179,0,24,159,1,240,152,188,2,43,80,252,231,179,19,134, 48,136,36,218,209,66,73,193,31,238,72,101,243,5,128,205, 199,151,195,23,0,31,60,188,144,220,99,199,192,192,167,115, 10,154,248,2,80,21,95,128,122,229,202,248,20,84,131,66, 21,174,79,226,177,166,43,229,115,247,244,221,84,104,247,71, 108,223,189,233,201,201,201,60,142,169,128,180,223,54,203,121, 142,0,245,205,41,252,174,187,126,120,144,57,4,172,134,5, 192,56,66,0,149,8,149,6,250,249,40,41,174,243,217,51, 56,137,126,113,12,35,32,34,246,33,153,248,2,76,49,111, 181,124,1,130,73,200,169,243,146,225,20,97,46,211,160,122, 19,142,81,117,213,249,107,195,167,160,238,123,78,69,235,227, 108,111,185,146,59,109,7,172,31,196,115,247,171,94,131,140, 231,215,13,144,25,239,207,117,249,169,70,210,4,0,22,202, 113,96,133,223,29,218,104,216,255,169,150,53,128,120,14,6, 241,104,228,201,1,103,144,79,130,197,121,126,251,40,142,227, 144,56,133,52,155,168,82,51,210,142,47,30,30,145,224,225, 29,95,35,80,30,95,128,122,125,71,202,231,35,38,190,128, 154,242,41,60,213,242,86,243,241,92,82,150,217,112,20,44, 49,77,59,96,15,2,225,229,64,216,249,3,178,224,208,219, 147,1,128,158,63,118,192,176,255,187,62,211,78,136,36,160, 2,9,114,247,212,226,124,118,93,238,230,89,6,6,144,37, 213,96,232,39,225,153,241,240,48,168,19,25,32,71,102,54, 91,61,207,173,215,221,199,147,47,160,110,248,20,158,107,189, 40,247,129,218,230,203,17,120,104,47,28,184,195,216,11,170, 234,42,120,127,210,120,252,192,210,150,171,29,125,130,121,233, 195,204,95,144,167,214,132,96,0,158,144,85,0,205,20,208, 155,164,40,46,199,33,23,151,60,139,1,28,202,135,4,122, 141,160,180,187,145,178,23,234,187,25,12,123,166,240,40,33, 159,225,23,108,149,93,63,203,71,61,25,199,155,153,39,109, 255,14,202,188,243,101,69,24,238,159,235,52,144,215,71,132, 88,31,17,98,125,68,208,250,236,154,178,144,146,28,179,174, 94,148,147,43,241,114,158,129,39,248,22,134,180,28,105,70, 90,39,249,240,155,159,202,155,255,216,145,151,138,229,191,140, 114,8,136,91,187,4,96,124,85,0,175,65,220,95,14,31, 64,60,242,49,140,194,17,57,150,1,18,222,93,179,82,190, 0,189,26,224,199,23,16,196,87,224,143,135,39,248,241,5, 148,44,121,20,124,1,136,136,79,161,226,245,217,219,60,211, 109,247,107,219,17,6,133,47,253,215,101,73,75,133,33,155, 160,31,70,235,199,55,96,154,223,187,210,46,147,192,149,231, 207,222,145,77,43,13,64,227,80,0,140,203,0,200,254,16, 185,251,18,145,156,207,94,173,156,48,140,97,36,69,43,218, 40,33,160,185,206,38,28,122,16,95,128,124,98,1,143,135, 39,197,141,22,90,25,206,212,6,197,243,231,114,24,123,50, 204,95,57,95,0,16,5,159,2,25,131,15,87,1,244,8, 208,209,102,135,166,235,165,58,158,57,154,59,139,136,175,253, 32,160,190,79,12,222,223,52,63,149,49,191,224,231,207,156, 120,220,115,4,88,102,188,10,128,241,133,0,146,153,165,3, 213,158,207,30,165,60,131,1,244,139,163,24,133,235,10,54, 120,128,164,66,225,248,2,248,3,56,56,190,0,132,192,195, 147,33,105,166,215,39,252,249,2,128,114,248,2,120,100,126, 156,124,10,90,74,113,32,113,146,78,231,170,62,194,128,167, 39,6,32,36,227,238,72,243,245,184,120,159,171,238,155,230, 71,232,249,97,152,159,238,243,20,0,185,51,0,48,25,66, 128,98,70,36,117,48,137,202,249,0,226,145,143,33,5,56, 83,208,134,22,18,194,196,118,23,196,23,0,13,111,47,27, 13,215,112,34,160,222,225,40,140,120,120,10,228,11,224,15, 171,168,132,47,64,55,27,250,247,143,143,79,65,164,176,215, 89,170,23,147,120,159,14,12,20,87,63,114,78,24,16,131, 122,226,207,204,240,95,206,252,60,223,128,58,255,192,38,109, 255,207,142,71,1,48,190,42,128,68,13,154,58,196,57,95, 149,157,223,30,165,156,48,138,163,232,23,3,72,187,48,156, 216,99,198,195,243,61,242,2,28,31,1,216,138,58,133,192, 195,243,89,122,57,101,73,134,235,155,248,2,28,237,142,193, 118,68,248,243,41,4,173,143,185,231,193,192,167,64,120,193, 89,42,119,209,235,189,133,106,151,61,25,123,0,248,157,90, 199,230,131,217,217,189,121,124,19,7,16,63,63,2,231,7, 104,199,137,67,218,254,95,58,3,0,181,221,255,227,11,1, 138,94,192,137,163,34,91,221,249,237,113,202,51,24,192,17, 231,40,70,224,186,124,145,79,104,217,109,238,136,108,193,86, 183,73,233,129,55,29,219,105,190,62,105,7,105,243,85,122, 135,253,123,98,238,159,111,125,210,209,122,178,25,112,148,110, 130,234,214,71,104,134,204,121,194,185,68,101,15,224,232,56, 212,228,37,247,25,239,23,64,171,244,243,175,173,96,35,89, 24,126,246,199,10,114,220,131,99,107,152,250,127,118,60,10, 128,113,25,0,37,92,75,101,176,175,252,243,219,107,43,31, 195,9,28,116,142,99,20,185,106,161,96,27,123,212,120,153, 147,203,138,32,180,179,126,132,166,6,106,50,74,134,237,232, 187,40,177,133,69,50,150,24,193,168,136,122,164,186,206,74, 32,24,127,134,63,223,152,95,31,17,122,125,0,0,143,225, 114,210,138,110,122,220,175,246,244,9,166,118,79,134,79,244, 174,66,181,109,53,236,252,38,102,104,125,126,230,254,14,31, 219,41,69,254,25,41,0,168,113,2,48,190,62,0,9,17, 72,123,162,230,3,136,71,62,130,99,56,148,15,9,100,112, 113,9,128,107,198,195,139,154,243,5,16,179,167,154,249,2, 132,145,47,128,235,33,168,29,159,130,128,216,46,174,52,237, 225,50,231,147,222,201,71,172,129,67,192,190,92,14,222,223, 107,26,185,64,178,188,249,179,171,178,41,205,3,144,25,128, 38,65,21,0,74,30,96,95,28,124,0,241,200,179,24,196, 97,231,8,134,64,89,181,69,52,8,111,47,171,82,229,124, 1,94,137,99,216,63,101,229,162,0,190,0,21,86,21,204, 23,16,63,159,66,81,246,140,184,204,20,53,151,90,128,76, 81,190,96,241,252,130,221,149,77,120,126,97,168,97,203,87, 226,243,5,193,243,75,30,65,250,248,99,1,12,192,53,31, 241,49,2,21,215,223,221,31,31,31,64,60,242,20,78,162, 63,113,18,163,68,46,135,215,39,69,133,28,69,245,203,231, 11,0,219,35,39,7,15,149,243,5,232,156,59,126,124,1, 66,171,144,87,203,167,224,211,181,248,146,88,66,14,152,74, 129,80,210,112,130,221,151,195,224,249,73,11,102,244,52,30, 215,187,32,140,121,0,53,188,84,231,119,153,210,167,0,144, 253,221,232,16,19,255,103,149,67,192,106,58,146,49,237,253, 158,102,32,218,31,55,31,64,28,114,23,195,24,22,9,209, 138,182,108,50,97,102,28,228,248,2,160,57,134,234,223,19, 179,115,242,240,226,104,249,2,248,35,196,160,245,237,149,199, 23,0,4,241,5,200,202,88,84,212,221,226,116,106,209,139, 104,38,2,80,1,238,119,133,22,115,243,223,86,101,8,214, 251,17,131,240,252,220,153,128,130,137,249,77,243,15,110,68, 26,25,164,37,35,224,142,95,2,48,222,62,128,162,103,148, 57,144,64,252,124,0,241,200,179,24,196,96,162,9,173,104, 203,56,73,104,199,68,149,203,23,192,243,1,148,199,23,0, 102,71,227,19,138,48,220,31,215,90,28,204,23,96,230,83, 208,147,156,97,214,135,14,160,221,237,2,19,101,171,41,54, 17,208,124,11,109,229,1,142,72,21,62,213,127,33,149,248, 72,121,50,130,49,188,164,152,17,255,249,129,236,79,78,29, 204,171,125,90,9,1,198,73,249,227,14,1,242,235,147,62, 4,212,138,15,32,30,121,26,167,112,48,121,12,35,132,44, 49,213,123,110,191,149,99,115,97,196,14,134,225,11,208,73, 71,160,117,11,232,127,79,249,20,30,177,201,61,104,85,123, 63,62,2,17,200,167,0,67,78,192,200,167,112,194,77,211, 28,30,79,175,31,133,74,62,120,127,176,120,127,254,249,202, 120,62,243,243,87,87,134,42,226,27,240,254,158,123,235,193, 7,144,65,26,233,162,23,32,183,0,99,124,76,64,188,101, 64,2,64,39,15,213,154,15,32,30,249,40,142,139,67,137, 83,72,103,136,229,23,144,161,188,81,242,5,80,8,190,0, 85,125,133,199,115,80,43,8,230,251,3,120,92,96,48,159, 130,137,47,192,192,167,48,226,30,162,179,184,247,93,40,25, 100,222,240,17,83,160,211,73,66,57,76,158,174,238,241,204, 47,255,59,251,219,3,247,49,234,239,221,255,199,97,247,143, 203,3,80,142,7,25,26,21,71,213,104,59,126,62,128,120, 228,89,12,225,112,242,48,134,144,77,169,9,37,142,47,64, 221,119,253,248,2,116,117,137,143,47,64,79,207,153,210,143, 166,251,171,134,79,193,201,100,119,185,139,249,220,63,129,3, 22,17,115,236,23,23,98,184,76,94,68,48,208,94,46,163, 31,110,126,50,206,95,74,255,169,167,84,184,119,28,190,59, 175,252,5,19,192,103,0,38,85,8,224,73,5,150,56,1, 106,205,7,16,143,60,131,147,56,212,124,12,163,46,50,106, 150,95,175,7,152,216,239,161,33,228,57,188,125,53,124,1, 34,4,95,128,8,193,23,32,16,204,23,0,132,231,83,200, 62,227,94,4,41,218,39,246,180,62,249,132,68,57,73,200, 31,192,169,83,179,144,175,119,93,254,252,48,206,15,211,252, 119,245,175,206,166,145,70,10,41,201,4,200,167,0,97,242, 120,0,106,242,150,176,87,77,196,168,185,247,137,40,31,197, 113,231,96,242,56,82,41,34,174,252,38,88,186,77,146,8, 50,72,139,197,73,51,39,186,59,205,225,249,117,58,116,121, 118,71,43,95,10,131,207,35,148,110,124,249,106,114,55,35, 105,1,135,218,1,160,251,15,217,71,51,111,133,84,221,215, 75,119,0,79,192,65,90,213,130,175,184,235,215,7,187,155, 115,243,187,122,37,187,204,249,165,28,248,186,67,189,217,180, 71,245,75,234,95,58,3,0,227,23,2,36,107,160,254,68, 251,196,184,243,1,196,35,39,140,96,164,57,129,54,180,143, 57,45,124,6,129,124,249,2,228,156,180,202,23,192,157,30, 204,159,173,83,143,124,1,236,253,63,158,125,155,250,77,185, 14,60,21,30,45,211,119,153,170,26,124,25,81,15,22,184, 249,57,62,1,193,20,136,203,153,159,238,239,255,175,236,24, 82,249,253,63,231,3,100,20,22,0,26,15,16,80,188,30, 128,66,10,66,251,235,133,15,32,30,185,139,1,28,108,57, 138,145,172,155,134,20,233,171,144,36,50,240,1,4,241,5, 240,236,121,102,190,0,174,23,49,104,126,10,49,63,25,143, 2,11,199,167,224,62,153,186,92,24,220,119,253,196,29,210, 142,140,213,207,238,37,233,88,46,30,187,199,201,137,245,54, 252,230,215,255,150,130,230,223,218,127,107,118,12,233,162,250, 151,60,0,142,3,112,82,121,0,222,167,73,217,3,137,186, 226,3,136,94,14,0,99,24,75,136,196,20,180,143,53,55, 67,8,182,21,132,227,11,0,91,191,215,249,2,252,241,246, 42,95,128,8,228,11,40,4,4,178,135,82,14,95,0,80, 30,95,0,61,151,186,24,130,52,79,70,24,214,23,76,48, 32,140,33,3,95,140,242,67,230,155,231,231,82,133,130,241, 191,200,111,254,199,15,223,146,29,147,246,254,148,39,5,88, 170,0,96,252,246,255,184,115,0,121,67,48,118,176,254,248, 0,170,151,115,164,16,132,17,28,110,57,36,78,33,59,226, 207,241,207,227,237,205,124,4,60,23,113,48,95,0,23,133, 171,121,3,111,204,111,230,11,112,180,206,129,242,248,2,240, 202,200,66,36,161,53,226,138,80,120,122,111,207,61,23,121, 235,127,229,151,218,227,79,3,0,244,174,127,82,51,218,1, 243,23,229,79,31,249,105,122,180,168,252,37,15,128,11,0, 198,109,255,143,147,15,192,83,218,24,59,88,175,124,0,149, 203,97,216,39,0,228,184,136,91,143,96,56,227,166,130,248, 2,76,108,253,178,153,8,226,11,16,70,190,0,68,198,23, 128,50,248,2,24,6,128,55,71,102,137,118,93,181,192,38, 255,72,217,123,133,113,139,84,243,46,208,158,159,233,89,249, 245,221,132,229,27,208,231,207,127,239,231,143,252,91,122,164, 168,248,99,82,8,144,81,240,255,227,212,3,24,127,8,80, 220,22,7,79,204,28,67,11,124,206,155,83,143,96,170,119, 185,234,116,203,238,102,238,47,198,48,150,20,104,69,219,200, 148,22,215,81,251,199,185,23,137,88,210,48,210,250,251,156, 226,181,28,173,230,204,59,234,186,131,205,241,25,114,221,253, 106,82,76,38,65,133,20,46,232,180,92,197,21,59,60,146, 192,12,24,40,55,73,73,86,18,119,5,166,104,103,238,228, 135,15,243,48,152,147,125,249,249,57,67,18,102,126,241,202, 177,239,167,135,242,206,191,170,252,153,241,229,0,172,93,18, 208,147,83,25,35,103,79,253,243,1,248,201,77,97,26,5, 236,81,46,134,112,164,117,191,115,138,178,131,66,83,124,111, 39,29,25,249,0,28,152,241,250,196,34,252,204,124,1,208, 170,218,165,221,219,81,40,70,192,118,6,234,129,72,48,95, 128,56,53,58,64,167,19,179,190,114,27,21,249,212,225,1, 14,66,69,33,158,143,0,164,68,157,208,158,159,128,31,17, 57,111,172,125,230,223,125,244,187,163,195,158,216,95,14,0, 244,236,255,184,142,120,249,0,74,228,224,123,39,6,31,0, 39,15,194,147,115,117,98,185,163,44,139,1,113,160,163,31, 67,41,26,53,31,155,69,170,211,12,51,95,0,5,242,5, 240,109,185,94,57,111,48,116,190,0,181,183,192,204,23,32, 183,6,23,191,197,216,200,155,217,115,120,150,60,30,79,207, 214,146,153,90,59,49,175,91,57,207,71,109,252,141,98,126, 241,230,177,111,143,13,22,247,254,49,101,247,47,236,255,117, 225,254,199,159,4,44,173,222,222,137,195,7,32,227,185,213, 116,27,49,4,216,96,162,79,253,32,138,20,142,55,239,155, 114,12,99,131,148,13,199,23,160,119,5,242,120,1,135,225, 11,0,67,210,233,199,23,16,204,103,224,40,121,2,157,47, 0,220,245,221,177,23,51,23,234,175,133,10,17,230,34,111, 149,19,145,124,246,103,174,145,202,236,23,112,207,199,52,63, 149,51,255,193,19,223,24,59,165,237,253,41,5,0,68,245, 225,254,199,95,6,44,26,70,119,191,163,68,154,245,195,7, 64,44,158,155,139,242,201,248,154,232,81,168,9,79,62,140, 225,142,36,166,184,157,131,201,174,32,190,0,10,193,23,224, 245,32,4,3,114,229,206,3,50,93,51,14,190,128,177,39, 82,87,112,229,83,61,247,192,225,233,193,134,2,48,62,31, 193,118,213,248,205,175,94,141,35,9,55,207,175,60,235,35, 131,255,50,124,210,147,248,27,211,156,127,53,247,63,238,65, 64,156,231,2,120,52,198,61,144,64,61,225,253,185,100,145, 48,158,15,111,250,122,130,57,191,94,166,159,208,89,229,114, 39,22,14,58,131,93,77,232,24,109,115,69,91,53,124,1, 144,206,156,231,191,137,96,124,32,254,181,231,122,10,195,242, 5,168,240,152,220,39,99,143,142,190,13,10,145,9,177,81, 58,135,167,151,229,48,62,31,97,48,202,66,83,126,210,148, 159,180,140,138,126,20,137,223,252,30,179,120,252,212,87,135, 142,121,218,126,184,226,95,157,169,127,45,250,0,8,4,74, 31,172,47,188,191,158,127,247,59,31,94,207,4,16,115,62, 124,41,124,224,79,159,87,231,79,227,248,148,253,109,71,49, 114,18,25,84,200,23,128,0,190,0,97,228,11,64,145,47, 0,154,81,224,249,12,252,249,2,192,220,95,246,241,209,183, 193,248,253,161,249,96,130,61,118,59,248,249,112,189,3,130, 125,114,28,1,88,245,243,3,0,78,157,250,234,208,81,164, 153,189,191,142,213,63,222,42,128,167,31,32,125,144,175,154, 214,26,239,175,239,46,229,157,15,47,216,151,74,207,124,147, 118,208,52,63,191,139,17,28,158,186,47,121,50,155,62,81, 57,95,0,140,124,1,100,84,127,57,223,224,72,57,0,50, 38,42,1,51,95,128,122,63,238,211,3,111,53,175,63,161, 28,60,189,96,119,96,115,158,158,140,207,167,210,249,3,222, 143,161,161,175,12,245,51,145,127,74,65,254,213,153,250,215, 224,120,240,220,191,143,245,131,198,11,207,47,239,255,28,158, 155,12,123,137,249,124,120,190,92,197,225,201,195,204,159,197, 169,196,129,105,7,48,48,236,14,84,194,23,192,201,85,190, 0,98,229,186,90,9,214,111,16,128,49,61,201,207,159,125, 97,112,41,28,174,147,143,59,102,211,180,62,28,246,130,88, 98,47,249,249,8,99,66,202,127,126,87,155,95,63,70,140, 116,136,240,232,200,87,6,14,178,177,127,166,254,42,255,181, 49,0,202,154,165,210,226,64,173,241,252,234,222,173,226,185, 77,200,49,53,191,205,119,141,169,24,53,238,184,234,242,230, 79,225,120,219,222,206,67,24,61,78,169,202,248,2,100,213, 228,249,2,244,147,4,28,214,39,0,252,248,2,192,112,16, 120,175,64,175,13,204,119,91,100,163,226,143,167,23,108,218, 84,176,250,34,152,245,231,225,195,48,62,31,126,126,193,228, 255,245,231,175,84,148,82,35,95,57,185,207,16,251,167,235, 91,253,227,205,1,72,134,159,246,198,143,215,47,156,207,206, 187,113,106,132,200,119,157,11,95,60,185,138,48,227,90,87, 92,214,205,12,198,147,231,254,59,134,254,233,123,155,143,165, 51,71,205,124,1,128,137,47,64,45,115,170,124,1,220,201, 133,165,92,0,73,125,6,130,205,5,112,124,1,26,231,225, 254,83,29,238,84,189,195,94,72,235,3,159,231,99,218,237, 121,188,63,49,142,185,96,155,116,184,231,19,118,126,240,243, 103,70,191,118,114,143,33,246,175,123,245,143,63,9,88,90, 175,189,96,242,189,130,217,117,43,147,155,250,182,5,11,184, 10,62,31,158,127,73,189,153,226,224,249,185,92,58,87,56, 84,231,119,49,208,180,127,230,62,156,26,160,19,60,18,0, 74,174,64,231,1,226,146,116,106,27,15,41,181,123,253,10, 194,147,40,52,5,7,26,15,210,241,83,233,204,28,253,251, 19,219,105,25,238,249,64,123,62,194,240,124,76,235,175,63, 31,161,29,153,82,193,252,238,216,215,79,236,102,246,254,148, 145,247,175,174,212,63,254,70,160,226,6,232,238,87,99,54, 32,26,60,190,252,95,98,98,62,127,60,55,124,170,199,196, 156,15,79,101,205,15,148,143,39,71,161,74,208,185,103,218, 65,12,31,206,245,15,114,213,128,242,248,2,76,145,188,236, 214,155,174,31,142,47,192,25,62,209,159,62,219,124,106,46, 73,113,55,21,189,11,211,161,30,166,231,67,218,27,97,94, 127,61,249,7,223,249,41,236,252,148,250,214,137,157,76,236, 159,210,64,191,110,125,170,127,188,125,0,18,175,130,187,47, 81,37,30,31,82,172,72,108,188,230,135,39,47,27,207,13, 47,67,188,122,39,196,176,249,152,122,7,202,157,223,155,120, 26,197,232,108,129,142,84,231,241,166,57,66,84,203,23,224, 237,233,11,226,11,64,8,190,0,53,40,112,50,39,118,165, 47,230,215,83,40,207,143,71,236,169,77,64,166,142,12,19, 108,75,24,161,218,38,190,1,19,75,64,240,252,233,239,28, 127,193,208,245,231,45,252,213,177,250,215,172,15,0,228,30, 228,221,105,222,249,226,229,166,243,217,205,76,47,122,246,215, 127,255,214,75,67,193,243,251,131,135,42,193,147,235,202,56, 208,188,127,238,62,113,242,68,230,88,56,190,0,191,44,189, 12,4,230,178,248,48,4,83,58,95,128,242,42,209,192,211, 99,23,251,69,237,102,188,63,152,76,190,31,75,128,96,206, 109,52,189,31,38,190,1,248,206,111,186,223,194,172,217,239, 31,219,81,76,253,113,125,255,89,184,200,214,183,250,215,2, 11,144,255,105,236,64,165,120,124,127,213,50,203,185,184,143, 140,80,92,115,194,49,202,249,97,56,33,55,204,252,105,28, 155,182,119,198,1,26,62,64,195,42,30,191,60,190,0,160, 60,190,0,132,228,11,24,121,108,248,114,190,107,222,124,118, 174,128,76,77,34,216,245,52,253,127,130,137,68,180,156,249, 253,222,15,193,248,113,0,144,253,241,209,39,149,216,63,205, 80,126,213,185,250,199,137,5,80,78,88,24,233,239,44,11, 111,175,30,14,69,74,143,124,56,60,57,127,36,20,41,169, 63,104,225,69,84,243,235,134,194,85,152,129,195,207,159,251, 247,168,24,157,231,160,99,164,243,88,203,105,148,208,247,111, 158,47,0,33,248,2,184,64,64,197,76,8,246,212,194,220, 95,142,62,58,240,54,19,165,42,25,159,15,223,111,89,217, 243,49,83,186,6,205,95,238,251,225,254,223,35,219,12,177, 127,78,253,51,210,153,63,117,171,254,113,151,1,61,217,128, 19,131,98,160,50,188,189,80,178,229,225,241,228,193,251,8, 149,57,63,191,195,8,223,111,21,46,81,18,60,191,183,221, 248,84,235,190,249,111,36,78,28,205,246,171,12,254,60,95, 0,160,119,239,145,193,107,80,205,133,163,252,151,11,26,4, 210,219,79,94,169,135,108,100,8,39,224,131,213,83,241,246, 194,119,125,192,172,79,152,249,195,61,41,254,249,208,173,71, 30,98,98,255,148,135,240,67,62,242,171,110,213,31,72,196, 120,237,66,159,73,2,14,18,72,204,252,136,152,195,197,142, 122,170,70,24,218,111,57,231,145,152,4,161,233,138,194,208, 116,82,233,252,220,111,251,37,244,252,230,39,229,224,81,243, 252,165,28,250,72,219,169,246,97,55,177,47,41,156,150,146, 196,219,54,204,119,250,135,227,11,80,115,9,36,237,25,114, 174,193,221,113,116,25,37,194,62,31,97,196,74,6,125,127, 142,175,39,252,243,137,102,126,247,183,71,214,21,137,190,229, 216,95,239,250,67,61,43,127,45,146,128,30,143,141,246,82, 224,254,236,221,125,4,235,34,170,153,110,24,121,102,212,215, 0,33,252,3,121,126,147,143,194,133,23,166,235,151,137,39, 15,49,191,28,222,140,57,135,206,120,163,179,127,40,245,134, 200,200,105,189,146,162,58,224,58,2,84,191,64,231,35,144, 77,128,89,142,87,250,207,165,38,254,249,4,127,127,242,225, 91,224,52,72,40,132,170,230,247,67,40,215,231,249,30,100, 121,208,252,180,234,248,189,1,136,191,82,244,143,255,191,189, 107,253,141,227,186,238,231,204,46,41,89,116,244,178,252,210, 195,18,149,4,109,26,20,65,31,104,210,126,40,208,2,69, 63,245,67,210,127,40,145,21,59,78,226,6,6,2,23,133, 139,162,117,251,193,8,10,23,181,40,89,85,228,88,113,44, 201,142,236,88,177,34,217,145,108,73,182,68,145,162,100,138, 34,185,220,221,217,153,57,253,64,114,119,238,189,231,220,123, 103,184,164,246,113,47,97,154,246,238,236,111,102,118,238,185, 231,156,123,206,239,215,251,38,160,186,81,211,31,8,38,109, 172,122,200,236,184,3,51,145,248,142,119,96,202,105,116,253, 120,179,89,183,140,62,252,90,240,249,43,90,59,62,193,226, 216,226,88,4,91,111,111,205,42,79,34,112,68,38,54,190, 0,117,117,148,75,152,117,190,130,229,255,155,125,126,123,87, 54,134,222,215,79,2,251,128,217,151,33,63,31,36,248,89, 200,220,43,96,249,30,56,124,176,224,175,140,163,179,255,155, 198,108,222,191,213,143,211,127,35,148,129,58,180,96,43,149, 0,250,90,6,226,218,9,154,180,53,79,214,192,245,147,163, 208,254,73,130,115,110,215,143,71,35,13,86,22,95,53,123, 101,240,245,145,117,38,34,220,123,124,14,54,37,219,110,140, 237,192,237,106,38,223,206,23,160,39,5,81,75,252,89,249, 2,102,102,170,217,78,179,218,159,167,205,194,82,247,95,191, 167,168,9,134,228,107,250,144,201,3,72,124,3,40,236,44, 240,11,13,0,157,152,253,239,148,143,253,251,116,250,175,183, 7,160,108,178,38,211,21,45,99,75,12,241,2,41,169,43, 52,90,61,248,126,114,53,218,228,74,129,116,174,217,110,225, 147,128,239,202,68,184,241,249,125,4,243,177,85,223,217,168, 54,198,17,182,220,223,241,197,232,94,24,229,72,69,76,70, 31,222,23,88,157,90,102,220,156,123,239,253,219,139,203,124, 127,40,246,251,203,165,94,230,245,219,190,31,115,173,7,102, 97,40,242,124,216,50,1,12,254,169,217,101,153,47,158,239, 39,97,98,255,158,159,254,27,17,2,180,55,3,211,41,211, 133,51,247,94,209,99,111,0,196,154,65,20,100,164,245,199, 120,253,241,145,93,75,144,21,177,144,240,229,12,57,143,223, 57,143,197,109,181,109,17,108,189,185,149,170,251,208,216,184, 35,163,121,136,243,97,242,175,71,198,54,26,0,1,54,103, 110,198,95,151,210,106,32,220,31,179,188,70,231,107,4,139, 92,14,90,242,34,126,248,220,243,225,129,127,122,246,63,211, 88,140,253,245,150,159,62,153,254,235,205,7,160,200,131,196, 183,249,126,122,245,225,32,75,63,55,88,250,185,243,233,29, 42,164,15,191,254,248,80,24,31,156,248,122,13,28,25,248, 41,204,237,253,108,223,205,120,225,19,250,194,76,252,17,67, 64,14,192,241,5,128,196,23,144,125,113,169,241,117,117,187, 140,155,88,249,176,33,83,54,229,252,190,31,59,223,130,142, 79,34,62,172,5,255,220,236,191,165,13,69,232,163,217,142, 252,91,185,125,255,62,155,254,182,29,185,110,24,23,132,10, 84,160,10,35,48,2,155,70,183,28,184,64,85,41,1,136, 98,77,190,41,172,109,123,167,239,22,157,153,96,130,174,224, 147,70,193,217,77,124,255,59,165,143,177,217,237,119,54,239, 135,205,102,16,65,76,73,45,183,198,234,159,78,48,255,235, 185,191,240,197,183,95,191,107,29,177,109,246,18,35,163,210, 13,124,229,251,57,63,247,211,182,206,79,211,40,251,73,32, 129,172,183,123,254,30,84,18,16,243,102,57,78,96,146,246, 35,211,28,203,165,255,228,150,92,50,190,112,62,66,38,103, 182,184,200,163,193,117,16,216,241,213,20,148,13,159,172,248, 36,104,218,216,240,117,106,210,197,157,181,157,17,124,233,250, 142,172,114,48,111,74,50,53,201,149,35,225,208,85,117,84, 234,19,130,218,59,247,190,197,225,171,98,234,166,89,244,185, 255,246,231,35,99,146,168,221,194,207,147,138,183,95,191,56, 247,98,110,250,115,238,127,223,78,255,141,40,4,202,149,2, 237,252,27,220,175,231,163,205,7,31,193,20,216,230,106,232, 213,189,1,180,172,184,36,230,195,193,138,15,93,192,71,49, 34,229,214,78,142,89,87,54,82,232,133,223,113,191,27,219, 239,239,88,108,224,149,81,138,198,58,95,17,39,85,162,243, 9,232,90,2,141,95,223,249,38,48,20,226,92,157,160,30, 6,217,124,23,0,249,249,48,239,168,252,124,148,197,55,247, 75,224,242,253,159,180,101,190,98,33,245,215,67,66,31,189, 152,4,236,116,4,78,234,229,51,25,128,81,119,141,150,246, 14,189,225,83,183,230,196,56,235,40,164,222,212,213,6,141, 152,92,247,75,100,1,105,44,129,15,90,166,29,197,243,3, 203,234,238,135,159,191,63,241,230,153,175,205,192,150,153,29, 119,182,28,160,49,208,106,234,228,80,35,95,215,23,127,48, 253,231,8,192,114,251,187,240,185,176,198,254,253,240,187,40, 0,188,174,97,119,241,225,218,194,63,181,58,50,95,188,206, 95,79,9,125,244,158,1,200,221,20,186,165,139,94,32,184, 245,225,137,213,171,147,28,104,31,253,120,50,50,223,182,126, 126,98,214,18,185,44,165,12,190,125,19,145,75,41,174,21, 127,233,177,218,99,72,219,175,108,163,145,175,46,31,226,203, 23,0,144,94,156,250,35,140,214,130,111,26,47,137,25,0, 132,231,67,218,167,161,194,248,96,99,6,184,49,191,42,243, 21,43,101,63,177,40,242,221,103,211,127,253,235,0,148,4, 110,122,43,98,108,52,23,53,147,97,247,185,13,50,2,190, 6,140,239,218,147,18,122,104,176,152,35,179,238,184,240,129, 253,44,223,132,162,25,219,131,120,127,138,227,235,53,112,43, 127,225,189,175,206,65,165,182,227,234,182,157,184,199,84,22, 224,53,132,178,171,147,123,87,233,62,51,35,225,233,143,207, 191,31,141,165,129,191,126,254,251,225,250,247,117,124,178,164, 158,85,124,156,158,239,200,124,53,69,153,175,190,158,254,27, 90,7,0,148,78,141,240,174,1,111,127,5,39,143,143,133, 37,9,111,16,87,2,42,144,8,52,41,38,245,250,56,191, 124,182,148,208,84,39,126,17,252,78,84,44,95,15,90,174, 63,25,187,243,199,119,96,203,228,206,59,15,29,196,173,230, 158,128,230,113,76,78,62,156,110,211,207,175,12,62,58,191, 127,233,117,179,136,58,79,185,230,194,151,11,180,213,191,163, 187,139,207,52,251,76,230,171,119,67,128,21,35,208,186,189, 137,121,244,187,175,207,46,101,243,209,169,15,47,109,58,73, 248,84,10,159,55,77,40,172,76,228,168,105,52,9,70,125, 238,143,250,153,181,61,181,61,81,182,245,163,29,217,232,31, 174,38,134,25,1,177,217,201,36,222,211,45,124,211,200,21, 229,91,0,176,209,137,175,5,31,238,45,30,174,247,157,204, 87,47,110,3,42,245,27,139,51,15,51,143,33,122,247,243, 243,185,123,176,70,187,96,172,17,15,26,223,220,242,114,197, 173,40,68,184,124,89,17,79,132,98,43,125,93,217,37,136, 230,190,54,7,35,247,119,124,186,117,103,116,192,236,83,140, 106,159,221,105,254,129,11,95,53,29,54,124,0,142,108,197, 228,233,69,70,53,209,213,207,201,191,234,141,63,191,116,184, 222,135,50,95,229,182,234,214,247,211,163,149,82,160,42,140, 194,40,108,254,242,25,216,165,199,96,188,53,231,90,102,236, 253,244,224,89,254,35,119,127,151,197,47,199,55,96,63,91, 27,190,100,105,215,142,191,138,250,208,245,93,119,31,250,10, 108,207,225,183,166,46,45,126,195,78,96,90,156,239,64,194, 39,35,253,104,251,220,181,61,31,58,62,214,234,223,173,77, 91,184,126,123,156,233,191,216,168,172,179,1,200,87,2,84, 161,178,243,239,225,73,94,198,82,127,76,244,24,15,197,12, 49,104,239,145,119,134,1,84,153,203,110,225,35,139,15,125, 136,191,186,210,38,219,231,119,207,86,147,11,163,211,35,187, 32,66,0,186,243,193,253,63,149,241,209,185,146,96,238,189, 170,223,195,225,243,60,137,32,166,73,237,247,19,64,229,53, 176,225,71,141,250,161,218,84,127,202,124,245,110,18,176,157, 6,164,155,240,39,188,115,134,57,142,60,100,168,156,244,126, 110,46,202,150,26,57,124,251,237,81,236,183,151,250,201,193, 130,15,133,241,225,1,225,11,173,77,213,185,111,204,193,200, 236,246,43,219,31,153,187,123,239,91,110,124,87,42,149,10, 224,115,59,31,200,238,237,200,228,238,37,240,227,230,211,181, 73,35,242,143,251,69,231,167,119,119,1,86,141,192,36,106, 213,231,144,211,162,37,224,117,100,185,126,110,119,13,61,104, 27,84,96,52,120,10,153,110,47,124,96,180,230,237,248,28, 37,137,142,79,107,194,151,238,15,10,89,117,179,62,209,196, 111,237,156,249,230,29,128,175,128,192,171,68,162,204,168,220, 3,153,119,242,93,248,38,9,169,254,137,110,124,105,39,6, 53,93,64,74,154,223,95,252,92,40,251,25,200,233,191,17, 186,0,10,41,8,221,226,21,255,72,76,44,121,235,179,107, 74,124,186,116,152,93,31,30,172,248,212,21,252,229,145,129, 180,33,182,118,124,244,192,151,250,245,203,224,251,126,63,220, 253,69,79,124,236,2,62,136,248,202,243,145,37,207,46,246, 181,204,87,47,26,0,101,250,3,181,166,148,136,128,21,129, 80,87,23,79,125,118,54,255,91,84,31,94,194,199,46,226, 163,134,47,251,61,197,175,159,88,124,178,94,63,10,81,118, 49,124,100,241,165,189,1,93,50,213,247,251,33,81,221,23, 45,170,2,228,248,252,213,255,213,250,225,124,159,203,124,245, 166,1,80,238,123,235,54,41,78,191,222,207,206,247,115,75, 10,49,54,178,9,206,234,171,250,240,69,240,169,139,248,220, 214,35,121,242,13,216,241,193,202,55,160,78,40,40,136,79, 204,196,67,182,119,194,31,159,119,19,109,248,25,211,154,140, 130,206,143,105,28,108,248,173,231,23,250,94,230,171,247,12, 128,65,10,210,152,50,45,56,40,113,88,62,178,227,203,118, 212,126,48,137,127,73,215,143,151,245,225,125,240,1,248,246, 161,50,248,8,110,125,122,89,192,204,108,143,241,195,55,184, 252,24,124,178,224,203,5,55,100,73,60,234,248,234,10,172, 238,36,216,175,223,100,28,148,175,31,11,224,47,191,158,188, 176,48,0,50,95,189,27,2,180,239,245,253,251,80,207,196, 135,80,141,208,120,21,57,100,244,225,185,104,18,181,215,101, 125,120,242,196,135,117,194,7,107,4,238,210,175,231,215,81, 251,249,73,248,88,2,159,74,226,163,120,253,62,207,135,20, 100,21,197,95,254,59,125,113,254,55,108,205,95,159,201,124, 245,106,18,80,157,109,55,202,232,195,115,205,28,220,67,138, 10,3,32,207,176,103,210,102,119,11,159,12,124,112,226,3, 128,149,34,68,111,117,5,17,95,221,204,196,117,193,151,238, 15,24,247,39,143,159,41,40,18,190,126,126,184,46,248,164, 169,34,1,100,47,221,127,23,76,170,239,62,148,249,234,213, 28,0,41,251,0,55,205,152,15,129,87,144,37,139,62,59, 8,250,240,124,130,76,214,143,95,27,62,177,148,156,100,153, 162,146,126,61,25,52,161,156,126,189,29,31,4,159,198,31, 159,28,248,188,174,46,90,166,40,25,155,118,118,213,94,243, 254,35,203,57,100,122,91,28,62,89,159,15,2,2,122,249, 254,192,200,124,245,114,18,176,125,191,241,166,190,9,163,54, 195,152,189,255,228,208,116,211,163,92,206,76,35,35,23,97, 198,171,221,193,151,94,71,71,237,130,63,62,9,248,192,224, 163,39,62,105,117,132,60,62,151,106,36,145,97,223,134,239, 98,93,32,75,190,128,187,191,252,170,131,46,252,87,230,222, 24,28,153,175,222,205,1,228,22,151,244,22,111,137,1,36, 218,104,16,214,55,20,26,108,253,245,225,121,253,122,98,94, 151,240,209,3,223,69,182,9,202,134,40,143,175,171,226,174, 5,223,22,181,187,238,15,58,175,31,140,61,9,244,254,126, 248,173,77,247,243,193,227,163,27,255,127,230,7,74,230,171, 119,67,128,220,55,144,77,113,83,27,53,194,103,115,109,228, 202,135,192,217,251,239,251,58,42,15,6,24,25,234,50,248, 84,8,31,172,124,7,216,21,124,89,69,177,56,190,253,250, 108,175,23,199,215,205,153,43,219,12,162,249,86,222,123,116, 97,192,100,190,122,57,4,104,135,116,201,180,154,75,71,86, 159,93,143,91,213,88,143,140,141,28,200,169,185,112,26,115, 166,20,136,218,15,46,225,99,1,124,189,102,14,10,225,203, 18,164,101,241,185,215,221,248,252,231,19,35,184,237,251,253, 100,226,249,249,226,115,175,23,127,62,212,112,4,78,204,15, 156,204,87,239,238,2,228,178,58,233,52,64,145,126,110,87, 191,61,177,202,121,100,68,247,232,68,229,243,235,254,248,192, 58,215,190,248,216,117,124,247,245,147,144,77,151,207,71,194, 119,235,56,115,253,252,54,124,240,198,47,117,127,78,205,15, 160,204,87,111,135,0,43,191,239,207,80,134,204,52,33,45, 26,150,133,183,185,76,59,137,50,143,106,5,30,25,143,161, 249,137,174,242,22,23,190,201,103,168,227,155,216,110,124,90, 7,124,208,94,151,106,26,201,3,159,191,63,168,237,201,251, 227,203,185,31,253,252,108,207,7,42,94,66,251,61,167,23, 6,82,230,171,119,147,128,57,79,44,78,96,202,36,216,2, 102,223,26,64,214,135,231,121,116,116,231,25,133,199,36,51, 240,201,89,15,224,143,143,78,124,157,238,194,7,95,237,128, 239,54,62,88,183,250,16,76,50,118,96,75,143,59,59,46, 152,75,195,217,240,129,193,207,88,124,40,128,159,207,91,24, 207,199,185,133,1,149,249,42,51,54,142,20,180,99,232,111, 194,30,169,89,212,159,85,175,72,63,189,234,12,218,244,235, 209,66,61,185,17,248,0,38,119,48,95,150,163,175,112,118, 124,189,253,186,8,62,178,117,144,58,171,158,93,51,65,198, 151,248,30,138,226,251,126,63,112,126,225,95,180,233,111,150, 253,164,195,50,253,55,54,9,8,157,82,32,179,205,195,92, 123,72,41,215,68,230,117,213,181,67,54,75,76,44,161,148, 13,95,127,220,138,225,67,1,124,62,120,1,111,124,180,224, 171,142,23,130,189,227,81,158,52,62,248,122,148,173,174,230, 121,124,206,91,203,152,251,99,226,155,193,136,138,143,6,62, 25,215,15,0,23,107,47,166,3,43,243,213,235,57,128,229, 159,12,38,137,221,29,39,38,99,205,233,191,234,84,145,229, 250,233,65,196,199,130,248,246,126,118,178,224,131,136,79,30, 248,192,230,209,205,130,36,20,170,12,248,140,137,29,159,10, 227,251,240,61,20,227,91,64,79,124,134,111,226,114,237,133, 65,150,249,234,229,16,96,245,25,202,128,178,91,145,33,4, 129,22,125,118,98,20,231,200,145,39,22,72,174,180,90,241, 238,224,115,217,124,95,124,16,36,170,129,117,244,77,124,178, 224,155,247,2,12,55,31,172,45,205,102,93,61,138,180,107, 221,195,215,145,73,235,238,43,141,127,109,105,192,101,190,122, 57,4,200,121,0,201,20,21,210,103,87,31,78,18,40,45, 253,250,233,51,35,202,236,6,62,49,248,102,13,26,135,175, 79,5,18,13,153,138,15,30,248,32,248,15,50,62,228,220, 241,50,124,7,235,137,15,5,249,30,116,124,184,177,244,163, 214,128,203,124,245,174,1,160,246,250,159,65,70,183,212,252, 119,145,126,122,16,242,228,228,217,79,239,215,79,206,225,131, 5,159,227,27,0,246,19,145,21,177,4,225,221,50,62,24, 177,184,44,32,166,230,198,169,192,253,177,241,29,0,243,253, 216,180,143,186,141,239,243,124,228,142,154,110,252,160,53,240, 50,95,189,105,0,72,203,3,100,181,91,32,68,131,182,126, 110,169,2,77,126,228,136,73,117,21,99,199,179,183,1,149, 195,199,18,253,236,124,113,16,177,117,253,126,248,50,223,129, 207,245,219,191,159,140,77,168,114,25,12,110,13,247,233,231, 151,249,22,4,252,187,141,161,144,249,42,51,42,27,130,210, 209,7,64,136,226,116,203,27,81,4,95,134,17,115,227,9, 45,250,240,196,212,148,73,250,240,29,102,125,117,215,220,174, 15,15,214,142,57,59,62,247,94,244,192,71,15,124,46,99, 193,115,249,21,193,199,146,248,224,192,87,4,54,45,42,13, 166,230,178,235,251,41,250,124,172,232,21,220,107,60,221,252, 98,24,100,190,122,213,0,96,206,4,32,68,128,139,179,243, 191,106,253,108,211,84,180,155,30,145,39,17,0,71,192,5, 162,176,133,180,23,142,134,246,139,172,15,239,143,15,78,124, 48,82,103,197,240,213,215,237,248,100,109,120,46,138,207,189, 142,80,68,113,8,152,196,229,218,240,193,137,207,223,31,156, 111,28,106,222,29,14,153,175,222,53,0,168,120,1,17,32, 68,73,178,112,121,254,181,209,115,213,10,140,115,129,136,202, 134,111,235,103,39,102,45,145,213,111,92,175,75,159,234,194, 167,46,226,147,178,198,185,21,16,208,200,66,172,245,250,245, 106,66,249,174,3,251,253,116,15,223,245,253,128,229,249,64, 192,90,124,168,49,195,52,252,196,185,125,255,33,158,254,27, 103,0,58,63,144,251,55,44,221,157,63,155,28,217,116,55, 218,7,95,50,107,192,120,237,60,83,24,202,38,5,198,235, 215,219,30,42,115,141,130,117,198,215,107,224,244,60,248,131, 192,7,54,181,134,160,150,238,218,241,245,215,139,232,32,154, 66,36,18,190,229,251,108,180,14,213,135,72,230,171,151,67, 0,48,38,127,251,91,108,197,11,151,151,38,70,63,196,205, 248,20,34,128,94,183,134,236,250,128,90,132,97,123,164,209, 169,136,167,102,161,121,124,85,151,198,23,95,85,206,67,203, 13,50,203,103,164,137,239,139,79,57,251,219,125,124,178,124, 166,122,126,46,229,64,245,94,201,248,170,70,144,19,63,110, 29,106,220,28,38,153,175,222,78,2,170,197,123,218,38,113, 150,213,102,22,78,103,199,55,205,227,126,120,200,230,18,23, 213,135,71,65,31,222,222,105,224,143,175,103,233,93,248,156, 254,173,27,31,0,140,213,89,207,141,163,55,190,26,46,248, 222,31,27,126,249,239,167,8,62,22,193,79,210,195,245,235, 195,37,243,213,235,187,0,102,253,238,234,179,187,92,127,149, 181,150,22,46,213,38,54,125,92,217,6,79,130,182,42,112, 156,52,182,220,55,128,91,251,92,77,20,202,94,129,11,223, 212,234,117,173,118,220,228,180,225,163,195,35,2,205,57,118, 91,99,240,192,207,155,17,20,20,126,212,251,211,51,248,89, 250,204,210,167,195,38,243,213,203,6,64,158,179,212,41,17, 130,12,50,74,106,183,22,222,132,55,171,141,104,156,70,229, 96,128,44,250,241,54,125,120,119,54,89,231,220,43,142,47, 79,229,181,225,243,202,200,118,124,223,251,99,195,151,66,27, 51,88,42,143,159,79,181,162,32,183,230,141,79,233,115,75, 191,31,62,153,175,222,53,0,160,5,161,168,77,255,182,15, 176,154,149,141,231,23,63,108,30,217,116,29,119,193,46,62, 13,164,243,244,161,152,38,211,35,101,123,226,200,158,45,120, 208,248,80,10,31,149,214,37,151,255,228,131,207,243,36,202, 248,32,224,155,247,7,13,190,3,55,190,62,146,231,235,23, 135,81,230,171,215,13,0,178,146,176,171,235,127,231,247,242, 215,147,166,173,218,103,11,63,175,158,29,201,240,0,85,80, 43,4,49,215,7,50,36,42,77,193,11,176,196,216,62,89, 0,127,124,179,230,174,60,190,46,121,242,160,240,193,195,56, 154,185,124,112,236,222,119,27,63,123,97,233,252,112,202,124, 245,178,1,144,250,119,212,245,223,252,73,27,247,22,222,75, 38,70,167,163,39,113,27,177,43,9,25,250,241,8,224,153, 243,55,87,80,100,50,233,250,202,232,131,111,95,25,55,6, 31,10,224,131,144,115,39,205,122,251,225,99,151,240,201,88, 61,16,36,118,161,149,224,225,197,218,185,97,149,249,234,109, 3,0,2,137,61,57,140,64,10,89,210,172,125,186,116,172, 250,193,200,8,236,55,159,22,190,98,192,93,41,232,126,80, 125,92,118,179,84,104,109,248,100,76,228,110,224,147,67,26, 133,243,117,220,233,188,238,226,171,85,24,182,239,135,15,153, 8,16,232,165,197,179,204,218,63,36,50,95,189,110,0,204, 167,130,216,60,0,107,6,40,171,207,44,156,161,215,71,238, 225,62,24,211,235,210,209,194,234,159,143,51,253,31,107,51, 19,205,125,178,217,207,111,195,7,207,44,185,15,62,176,133, 207,104,205,88,216,107,22,36,124,40,129,47,243,61,216,240, 253,246,110,76,252,182,17,120,121,241,151,195,44,243,213,251, 6,64,215,149,80,141,64,150,203,5,228,18,130,249,159,86, 189,246,81,253,200,200,165,234,195,176,199,116,61,165,181,132, 196,42,52,18,42,218,80,72,172,169,2,147,118,167,216,196, 7,47,124,126,42,112,38,15,45,190,134,63,62,58,253,39, 149,172,131,199,39,131,102,84,151,42,69,33,73,232,131,143, 214,235,95,249,235,149,197,19,195,45,243,213,15,6,0,88, 217,90,61,20,32,205,20,116,204,65,10,25,165,245,169,197, 95,226,201,145,26,140,195,38,105,189,243,105,95,225,42,253, 165,21,202,116,140,109,61,115,54,163,227,194,183,191,211,21, 225,151,197,151,82,138,190,248,168,25,68,27,190,79,123,149, 185,225,199,221,255,220,185,188,90,63,74,67,46,243,213,31, 6,64,95,88,242,1,129,110,10,44,121,129,120,177,118,33, 62,82,253,52,218,137,143,233,143,189,43,194,213,59,208,208, 242,232,185,246,239,185,105,247,32,241,193,146,86,147,206,15, 153,181,22,157,237,209,27,131,15,30,247,23,129,142,54,94, 77,91,195,46,243,213,111,6,0,89,63,0,114,172,108,170, 17,72,117,131,144,38,245,207,107,39,43,167,43,73,101,124, 153,221,208,167,223,31,11,244,147,131,163,138,190,88,63,187, 137,15,37,241,201,64,244,235,156,115,5,14,114,77,31,106, 57,252,242,248,80,8,95,186,191,202,241,39,26,63,203,201, 124,53,135,85,230,171,223,12,128,110,4,244,128,32,211,140, 0,113,158,0,100,205,185,218,111,226,35,35,147,209,19,176, 67,94,109,125,28,78,233,117,158,175,158,231,27,176,225,115, 70,130,74,241,13,232,248,32,184,238,28,190,107,5,183,9, 152,75,124,11,221,199,71,35,207,47,142,83,245,255,82,100, 190,226,97,149,249,234,71,3,192,5,4,164,165,6,237,70, 32,131,20,178,52,94,186,218,56,94,121,175,90,129,3,203, 236,2,82,111,25,26,92,183,124,63,187,45,114,5,111,71, 31,156,248,60,223,128,187,147,222,117,150,190,189,125,18,223, 129,57,101,125,240,169,20,62,10,91,159,94,94,197,233,250, 127,164,205,92,234,111,136,101,190,250,219,0,152,130,125,121, 67,144,89,115,2,203,93,4,89,227,110,237,157,236,88,245, 110,180,143,30,246,239,39,247,235,103,183,9,88,19,112,60, 182,254,248,224,129,15,78,63,163,108,63,61,26,95,128,116, 127,138,225,3,64,193,251,79,214,140,49,119,124,116,174,254, 82,78,231,103,213,8,12,169,204,87,127,27,0,62,57,168, 87,10,216,54,10,83,200,32,75,26,75,191,111,28,29,185, 80,221,66,251,204,143,229,54,14,221,253,236,114,236,170,171, 210,32,211,162,34,225,83,33,124,123,46,31,161,27,253,244, 164,197,224,190,248,190,189,154,242,253,55,191,13,244,201,100, 156,175,255,51,35,243,213,26,86,153,175,65,49,0,200,78, 126,18,58,7,152,128,128,146,198,116,237,109,56,49,178,128, 251,97,51,31,183,250,244,179,163,48,37,245,13,71,91,116, 15,32,225,155,204,62,62,248,234,62,251,90,42,237,77,124, 219,166,164,15,223,0,88,119,51,80,216,116,117,237,134,72, 247,7,47,54,126,154,46,5,153,175,193,51,0,188,47,224, 191,81,184,226,23,180,106,75,191,107,76,140,94,169,236,128, 199,237,69,191,82,238,27,69,103,180,88,145,174,84,212,138, 78,124,213,65,55,249,14,186,131,15,90,237,4,191,234,150, 229,91,64,175,251,91,236,120,4,188,92,255,73,26,100,190, 6,220,0,240,190,128,180,81,152,42,33,66,10,25,100,212, 170,223,172,189,81,121,171,218,196,241,14,13,185,182,146,8, 89,127,46,15,224,179,31,111,235,103,231,222,105,39,22,247, 193,183,179,40,162,152,85,0,75,126,94,223,121,135,194,248, 28,159,130,31,165,139,171,223,31,129,174,197,63,78,106,65, 230,171,123,83,172,247,207,17,53,122,241,21,149,129,220,79, 5,42,43,191,151,127,170,249,223,163,91,190,244,173,232,219, 244,85,4,189,97,216,116,143,37,231,211,30,171,231,39,183, 172,21,92,14,223,69,167,169,198,217,196,132,25,27,133,207, 243,5,20,61,222,129,127,35,126,54,167,243,147,119,255,135, 92,231,103,208,60,0,213,8,152,181,2,192,214,11,176,1, 65,26,215,175,47,157,168,190,91,1,24,199,200,213,79,79, 138,3,74,150,21,220,22,101,115,185,114,190,159,159,186,134, 207,217,78,91,77,159,63,62,9,245,16,62,159,111,183,237, 124,107,145,168,154,48,21,63,219,186,31,100,190,134,203,0, 152,1,129,158,21,200,216,202,65,45,60,104,126,177,244,235, 100,98,244,54,238,129,173,102,38,91,239,103,71,199,137,216, 218,127,165,126,118,48,242,236,157,10,56,87,63,253,250,227, 251,87,251,3,248,245,235,187,246,4,120,190,1,139,98,192, 221,248,251,173,123,130,204,87,107,184,101,190,6,221,0,240, 201,193,34,69,67,41,100,144,165,141,250,39,205,163,213,223, 86,54,193,83,128,82,63,59,0,88,51,1,114,191,30,42, 194,100,146,211,139,70,170,203,165,88,176,62,248,84,10,223, 191,95,159,247,130,208,72,101,34,43,131,110,76,255,217,230, 211,45,89,230,43,29,118,157,159,97,49,0,254,69,67,66, 225,16,165,205,219,75,103,178,227,213,185,232,41,218,194,245, 179,155,106,124,18,159,0,89,187,237,101,167,215,54,253,125, 249,14,214,142,15,98,90,207,228,19,144,249,20,208,146,235, 32,99,109,71,241,250,209,154,33,192,249,214,161,56,200,124, 5,3,32,248,2,92,99,177,26,18,168,193,65,154,44,213, 47,53,39,70,62,142,114,52,228,8,102,65,171,220,90,236, 226,27,48,179,228,224,217,201,175,23,235,242,147,172,24,223, 65,113,124,123,191,62,122,156,185,121,188,233,1,200,198,74, 57,147,90,114,168,25,100,190,214,105,45,237,239,243,207,83, 202,174,238,15,168,59,4,250,30,65,85,253,189,101,247,230, 191,195,127,128,49,61,239,92,164,204,133,44,239,115,201,141, 129,97,118,124,156,114,0,63,194,79,155,84,7,199,96,204, 37,24,221,228,231,100,33,240,38,145,56,204,125,127,87,238, 74,163,245,221,120,82,140,253,131,208,199,144,122,0,114,94, 64,237,40,228,247,8,218,36,35,173,249,250,135,241,107,163, 215,225,17,124,84,173,129,35,102,31,221,79,98,140,172,73, 59,83,116,155,175,193,83,215,212,181,225,115,83,203,175,223, 222,110,176,212,21,91,230,59,176,19,143,168,166,71,75,73, 198,201,161,56,200,124,5,3,224,153,23,40,182,81,184,202, 58,152,52,62,171,159,172,156,137,50,28,239,220,23,4,153, 1,208,108,20,6,0,7,93,87,254,177,207,63,232,82,109, 159,29,159,159,138,118,124,61,174,55,251,237,253,206,95,55, 31,58,65,39,0,56,85,9,129,201,53,48,124,0,73,118, 184,25,100,190,130,1,40,156,28,180,177,11,176,156,131,144, 198,247,26,239,165,19,213,41,220,77,219,196,157,104,109,181, 197,194,253,252,38,217,6,151,47,176,227,23,229,59,48,249, 4,220,215,103,207,103,216,240,209,43,69,232,117,124,150,61, 211,8,50,95,193,0,148,12,8,252,217,5,218,1,65,218, 108,124,26,31,171,252,166,194,210,144,131,230,64,251,147,102, 169,253,112,62,93,255,224,48,37,190,248,122,202,205,29,72, 184,146,46,38,190,47,137,24,40,137,85,20,248,24,218,231, 71,217,115,141,32,243,21,12,192,26,125,129,162,236,2,41, 101,241,157,250,89,124,189,58,11,79,193,152,58,149,243,174, 43,89,146,97,46,190,1,0,147,109,151,60,18,98,249,64, 131,44,161,0,151,125,183,241,5,216,206,95,191,94,147,109, 24,29,30,131,148,16,117,220,159,231,27,65,230,43,24,128, 46,37,7,193,232,37,116,208,144,39,245,198,199,173,35,35, 151,162,49,216,235,203,113,99,239,119,215,167,116,71,185,79, 158,4,186,160,120,25,124,116,242,5,152,81,57,122,241,29, 184,171,22,245,243,55,123,2,69,95,231,133,122,144,249,10, 6,160,139,190,128,63,187,64,142,134,188,57,85,127,11,78, 86,23,163,113,218,36,217,24,96,250,229,213,215,249,85,85, 95,147,77,62,0,95,190,1,117,159,95,250,124,159,85,154, 20,115,193,29,47,155,31,50,116,19,184,162,31,254,147,140, 50,167,23,235,54,153,175,176,250,7,3,208,37,95,192,131, 134,60,89,108,92,136,143,140,124,18,237,132,199,204,36,32, 90,57,113,76,178,45,159,21,19,28,82,99,246,126,121,112, 36,226,244,51,178,243,13,32,248,234,25,153,231,95,166,83, 0,0,128,94,106,216,101,190,210,32,243,21,12,64,89,95, 160,20,13,57,37,205,27,141,147,209,233,74,139,14,66,149, 171,16,52,203,106,200,163,132,6,180,210,31,255,158,4,177, 95,30,236,169,203,34,253,254,110,81,113,189,204,71,13,15, 208,233,129,152,249,1,2,124,185,30,100,190,54,112,82,12, 215,245,234,149,131,38,187,64,165,253,15,207,46,48,246,208, 95,225,119,104,92,126,140,245,137,109,55,19,0,96,173,59, 44,246,249,69,142,71,177,13,24,188,205,136,249,249,246,240, 197,227,248,87,234,175,67,12,173,246,218,223,89,255,121,157, 159,48,253,131,7,80,58,32,144,54,10,45,187,4,105,220, 188,218,58,30,189,23,69,112,0,34,142,245,206,180,176,92, 46,220,62,209,212,64,195,187,95,222,178,78,115,220,197,174, 64,195,118,254,82,148,15,206,235,231,178,24,237,223,175,198, 69,101,190,194,8,6,96,77,201,65,190,118,208,66,60,10, 25,101,173,187,205,119,233,88,245,14,238,131,135,81,236,167, 231,248,6,124,166,155,173,223,30,189,236,155,158,187,183,241, 29,200,249,2,219,249,251,240,1,240,87,109,185,190,163,113, 144,249,10,6,224,1,37,7,57,193,114,43,13,121,218,104, 94,78,142,85,46,224,67,240,148,100,105,248,54,24,126,149, 54,251,237,249,13,55,55,81,55,87,158,107,235,231,147,45, 37,215,208,83,228,120,251,245,105,198,228,68,28,100,190,130, 1,120,128,190,64,41,26,242,214,116,243,109,60,81,153,199, 3,176,89,118,218,193,193,128,135,204,218,174,215,234,219,142, 231,214,87,27,190,59,87,160,135,54,107,225,3,224,143,87, 247,27,162,83,205,32,243,21,12,64,79,248,2,133,105,200, 147,90,243,98,50,81,189,28,237,160,39,208,144,203,230,167, 7,223,47,175,151,237,218,214,102,185,223,222,63,224,224,249, 2,212,98,93,40,204,7,80,226,250,78,55,131,204,87,48, 0,61,231,11,20,162,33,143,39,155,191,136,222,170,52,232, 160,74,67,46,113,236,115,165,59,254,107,180,237,120,155,36, 39,175,125,164,214,249,73,157,6,104,53,70,42,118,161,235, 59,23,7,153,175,96,0,122,200,23,176,119,20,230,119,11, 244,2,226,251,205,223,166,71,42,159,69,143,193,35,84,170, 223,222,205,71,224,219,111,239,99,58,138,157,31,49,77,208, 126,199,219,248,0,240,124,28,100,190,130,1,232,41,95,96, 77,52,228,20,183,174,55,79,84,222,169,0,140,83,36,247, 219,23,237,151,39,197,41,215,143,247,217,198,99,250,237,189, 76,8,31,82,152,199,243,124,8,246,235,139,46,54,131,204, 87,48,0,61,29,16,232,89,1,47,26,242,100,54,62,7, 19,149,219,176,7,182,250,113,231,119,163,223,94,74,199,153, 230,167,24,31,128,141,79,192,214,154,204,139,141,229,142,191, 28,7,153,175,96,0,250,36,57,88,130,134,60,254,36,61, 26,157,175,108,162,253,128,174,78,124,212,98,104,103,191,188, 199,122,111,238,225,163,209,91,232,54,38,246,227,73,51,18, 62,86,117,217,131,161,107,241,143,179,32,243,21,12,64,31, 249,2,37,104,200,147,153,248,12,28,143,230,240,41,216,2, 236,138,95,178,95,94,224,15,228,142,39,75,174,65,63,30, 64,230,3,40,194,228,47,95,223,202,255,191,145,62,151,46, 48,177,191,94,244,27,166,127,48,0,61,230,11,128,163,90, 192,160,33,79,151,90,151,210,137,202,71,209,86,218,109,118, 207,149,233,151,87,211,105,230,122,207,127,154,59,198,215,181, 6,202,242,1,144,227,250,112,42,9,50,95,193,0,244,173, 47,64,90,31,1,41,59,3,196,81,140,80,210,186,21,159, 170,188,137,75,112,16,71,1,124,251,237,229,181,95,173,174, 179,243,13,128,37,26,47,194,7,96,95,229,85,146,79,149, 67,40,255,73,209,221,36,200,124,5,3,48,32,121,129,66, 52,228,201,124,235,66,246,90,245,26,238,162,71,203,246,203, 219,248,0,220,107,51,199,0,64,74,155,164,36,66,134,236, 10,159,127,93,226,3,80,67,25,156,77,130,204,87,48,0, 3,225,11,148,164,33,111,125,22,159,140,206,84,82,88,161, 33,119,175,176,190,124,0,126,94,195,90,248,0,212,157,127, 52,132,201,157,225,203,124,122,168,21,100,190,130,1,24,168, 228,96,9,26,242,244,94,252,126,54,81,189,133,187,97,27, 239,156,235,245,125,166,123,95,196,111,80,85,1,242,66,28, 96,108,14,218,204,8,50,231,103,59,94,243,27,106,201,161, 36,200,124,5,3,48,128,1,65,9,26,114,106,182,174,166, 199,224,253,168,10,7,0,249,126,123,185,6,192,221,207,175, 211,124,20,231,3,176,237,85,128,245,120,174,166,17,27,217, 161,214,20,27,251,39,108,213,127,152,254,193,0,244,153,47, 80,130,134,60,189,211,58,139,175,71,95,68,109,26,114,87, 191,189,175,64,24,26,107,180,127,221,96,94,87,192,135,15, 64,78,54,182,143,143,179,32,243,21,12,192,80,36,7,139, 208,144,47,23,13,213,91,31,167,71,240,98,101,12,246,250, 17,127,232,171,116,103,71,192,135,15,64,158,248,104,164,26, 57,101,35,95,62,128,92,166,32,161,195,113,144,249,10,6, 96,72,124,129,50,236,2,105,50,29,191,5,39,163,5,108, 211,144,163,241,225,156,24,183,158,189,47,195,7,192,5,29, 88,56,219,64,140,230,31,0,2,102,244,76,28,100,190,130, 1,24,114,95,192,131,134,60,91,76,126,151,77,84,63,193, 157,248,152,188,74,251,242,1,160,115,162,114,20,32,122,89, 114,145,41,207,242,1,16,61,23,7,153,175,96,0,134,208, 23,40,73,67,222,186,209,58,25,189,29,197,112,16,170,110, 62,1,44,192,7,0,138,177,128,146,199,155,159,148,47,72, 210,253,15,122,62,14,50,95,193,0,12,117,114,208,182,81, 72,220,14,1,100,233,92,235,131,236,72,229,38,62,9,59, 138,243,1,144,67,150,12,60,250,249,165,227,11,242,1,188, 16,7,153,175,96,0,66,64,80,134,134,156,226,228,106,118, 28,207,69,8,227,24,201,124,0,114,10,174,40,31,128,61, 61,168,210,130,18,200,124,7,237,241,98,51,200,124,5,3, 16,124,129,181,208,144,167,119,91,239,102,199,170,51,176,15, 30,230,74,111,205,9,235,195,39,208,29,62,0,253,93,218, 165,255,107,243,76,144,249,10,6,32,12,61,57,200,209,144, 235,20,35,170,25,168,183,174,100,71,43,31,194,102,120,202, 167,15,143,231,232,241,119,244,117,11,166,31,175,115,253,177, 130,233,47,199,167,130,204,87,48,0,97,112,190,64,209,141, 194,101,118,129,233,228,116,244,127,209,10,13,57,23,149,251, 241,1,112,147,222,118,188,174,53,236,83,133,24,189,210,60, 177,178,222,235,177,191,89,245,23,166,127,48,0,67,234,11, 20,165,33,79,211,165,214,197,108,34,186,28,109,135,39,242, 73,61,27,31,64,81,206,192,188,250,177,196,7,96,251,44, 2,124,53,9,50,95,193,0,132,81,192,23,48,55,10,51, 134,134,124,217,23,104,37,147,173,95,224,47,177,1,7,113, 132,39,225,150,250,249,77,62,1,0,23,159,128,63,31,192, 138,185,152,72,130,204,87,48,0,97,120,249,2,242,70,33, 217,105,200,179,251,201,111,233,72,116,29,30,199,71,252,249, 0,108,124,2,190,133,190,38,159,64,190,219,0,79,36,65, 230,43,24,128,48,60,125,129,53,210,144,39,215,147,19,149, 119,34,162,131,24,217,38,44,177,27,129,249,21,94,230,3, 48,27,149,77,134,130,246,127,157,74,130,204,87,48,0,97, 148,12,8,244,172,128,15,13,121,154,206,38,231,104,2,167, 163,189,176,85,157,176,29,16,84,250,249,245,44,1,56,252, 7,23,159,192,106,182,32,58,157,252,123,26,7,153,175,96, 0,194,40,23,16,148,161,33,95,246,5,26,233,39,116,52, 58,143,163,176,31,81,141,242,185,54,161,34,124,0,58,159, 128,152,37,56,151,188,148,54,131,204,87,48,0,97,116,195, 23,240,167,33,79,219,236,2,51,201,217,232,56,220,195,253, 180,37,191,50,235,220,2,174,248,62,159,37,48,165,199,77, 15,2,1,0,207,167,65,230,43,24,128,48,186,236,11,128, 99,139,80,111,38,74,211,165,244,35,154,192,75,184,21,118, 251,177,248,235,132,224,28,159,128,147,15,224,98,26,100,190, 130,1,8,99,29,124,1,158,134,220,218,73,64,73,58,149, 156,138,222,140,106,112,16,70,209,58,229,59,128,106,115,176, 155,125,48,183,243,112,57,13,50,95,193,0,132,177,238,121, 129,66,52,228,217,124,114,1,94,195,171,176,11,30,213,39, 63,178,124,0,249,98,98,159,52,225,138,25,185,150,4,153, 175,96,0,194,88,103,95,160,36,13,121,250,121,250,243,232, 12,38,112,16,42,118,62,1,157,100,196,231,244,8,162,27, 201,115,89,144,249,10,6,32,140,13,9,8,120,95,192,78, 67,158,101,115,233,251,48,17,77,70,187,105,187,225,190,23, 144,251,98,104,191,166,146,103,179,32,243,21,12,64,24,27, 26,16,148,162,33,79,175,210,49,124,63,170,226,1,196,60, 91,96,145,94,1,77,5,224,78,242,253,44,200,124,5,3, 16,198,3,241,5,74,208,144,103,119,210,179,240,122,116,55, 218,191,74,67,110,155,246,0,86,62,128,217,228,112,22,100, 190,130,1,8,227,1,250,2,106,72,224,69,67,78,245,244, 247,116,4,47,194,24,236,149,215,123,48,124,4,141,79,96, 30,14,165,65,230,43,24,128,48,30,184,47,80,138,134,60, 157,78,223,138,78,226,60,141,227,166,162,124,0,0,80,163, 67,201,109,145,234,59,200,124,5,3,16,198,3,246,5,188, 104,200,211,223,225,68,116,5,118,194,227,58,159,0,42,202, 132,249,112,0,0,26,244,189,228,150,65,247,17,100,190,130, 1,8,227,1,250,2,190,52,228,122,209,208,141,244,141,202, 219,216,196,131,84,245,226,19,136,233,123,201,205,149,245,190, 25,100,190,250,247,161,9,99,144,190,207,124,107,222,234,79, 4,8,81,251,167,210,254,103,245,167,154,255,93,25,171,252, 37,252,35,140,59,102,107,66,223,75,62,133,196,104,249,137, 195,244,15,30,64,24,189,18,16,148,162,33,79,175,193,113, 60,135,0,227,16,241,31,143,25,61,157,92,89,153,238,171, 211,191,21,100,190,130,7,16,70,47,251,2,145,230,13,116, 252,128,104,101,253,95,245,4,170,80,129,74,101,123,244,215, 248,29,122,2,116,231,159,232,112,114,1,146,21,103,223,76, 253,5,153,175,224,1,132,209,99,190,0,104,173,68,153,43, 53,8,25,213,179,43,48,1,31,226,102,218,159,39,1,129, 31,36,239,231,166,126,75,216,248,11,211,63,120,0,97,244, 180,47,176,252,187,162,121,3,102,94,160,82,121,52,250,91, 248,54,108,39,64,192,31,198,191,130,20,82,72,218,62,64, 43,87,244,27,100,190,130,1,8,163,135,205,128,156,28,52, 141,192,170,9,136,160,2,81,101,20,255,12,191,67,167,146, 215,129,32,107,27,128,68,97,250,213,119,254,195,244,15,6, 32,140,30,246,5,162,149,223,170,25,224,12,65,212,46,10, 160,21,3,144,55,1,171,147,63,9,58,63,193,0,132,209, 251,102,192,190,81,168,250,2,171,65,66,100,24,128,180,45, 238,157,172,252,87,152,254,193,0,132,209,87,1,1,8,59, 4,29,35,176,252,55,106,6,32,107,111,246,173,54,252,228, 197,75,194,244,15,6,32,140,190,11,8,48,151,26,236,24, 129,40,183,254,175,110,6,100,138,9,200,152,233,31,162,255, 96,0,194,232,67,95,32,98,67,130,168,109,32,32,231,3, 100,109,146,207,44,76,255,96,0,194,24,36,95,32,210,54, 11,49,199,16,14,44,45,57,133,233,31,12,64,24,131,224, 11,232,233,193,78,252,223,241,0,72,227,31,162,118,234,47, 76,255,96,0,194,24,168,188,0,42,1,128,106,2,200,168, 248,15,211,63,24,128,48,6,200,23,128,220,191,59,6,192, 212,42,8,211,63,24,128,48,6,212,23,0,198,0,128,49, 249,195,244,15,6,32,140,1,123,58,16,100,218,96,117,213, 15,147,63,24,128,48,6,216,12,116,254,34,205,8,132,233, 31,12,64,24,67,251,164,132,169,31,70,24,97,132,17,70, 24,97,132,17,70,24,97,132,17,70,24,97,132,17,70,24, 97,132,17,70,24,97,132,17,70,24,97,132,17,70,24,97, 132,17,70,24,97,132,17,70,24,97,132,17,70,24,97,132, 17,70,24,97,132,17,70,24,97,132,177,158,227,255,1,240, 190,117,10,245,58,9,76,0,0,0,0,73,69,78,68,174, 66,96,130, }; /* label-md.png */ static unsigned char label_md_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82, 0,0,0,128,0,0,0,128,8,4,0,0,0,105,55,169, 64,0,0,4,25,105,67,67,80,107,67,71,67,111,108,111, 114,83,112,97,99,101,71,101,110,101,114,105,99,82,71,66, 0,0,56,141,141,85,93,104,28,85,20,62,187,115,103,35, 36,206,83,108,52,133,116,168,63,13,37,13,147,86,52,161, 180,186,127,221,221,54,110,150,73,54,218,34,232,100,246,238, 206,152,201,206,56,51,187,253,161,79,69,80,124,49,234,155, 20,196,191,183,128,32,40,245,15,219,62,180,47,149,10,37, 218,212,32,40,62,180,248,131,80,232,139,166,235,153,59,51, 153,105,186,177,222,101,238,124,243,157,239,158,123,238,185,103, 239,5,232,185,170,88,150,145,20,1,22,154,174,45,23,50, 226,115,135,143,136,61,43,144,132,135,160,23,6,161,87,81, 29,43,93,169,76,2,54,79,11,119,181,91,223,67,194,123, 95,217,213,221,254,159,173,183,70,29,21,32,113,31,98,179, 230,168,11,136,143,1,240,167,85,203,118,1,122,250,145,31, 63,234,90,30,246,98,232,183,49,64,196,47,122,184,225,99, 215,195,115,62,126,141,105,102,228,44,226,211,136,5,85,83, 106,136,151,16,143,204,197,248,70,12,251,49,176,214,95,160, 77,106,235,170,232,229,162,98,155,117,221,160,177,112,239,97, 254,159,109,193,104,133,243,109,195,167,207,153,159,62,132,239, 97,92,251,43,53,37,231,225,81,196,75,170,146,159,70,252, 8,226,107,109,125,182,28,224,219,150,155,145,17,63,6,144, 220,222,154,175,166,17,239,68,92,172,219,7,170,190,159,164, 173,181,138,33,126,231,132,54,243,44,226,45,136,207,55,231, 202,83,193,216,171,170,147,197,156,193,118,196,183,53,90,242, 242,59,4,192,137,186,91,154,241,199,114,251,109,83,158,242, 231,229,234,53,154,203,123,121,68,252,250,188,121,72,246,125, 114,159,57,237,233,124,232,243,132,150,45,7,252,165,151,148, 131,21,196,131,136,127,161,70,65,246,231,226,254,177,220,74, 16,3,25,106,26,229,73,127,46,146,163,14,91,47,227,93, 109,166,232,207,75,12,23,55,212,31,75,22,235,250,129,82, 160,255,68,179,139,114,128,175,89,6,171,81,140,141,79,218, 45,185,234,235,249,81,197,206,23,124,159,124,133,54,171,129, 127,190,13,179,9,5,40,152,48,135,189,10,77,88,3,17, 100,40,64,6,223,22,216,104,169,131,14,6,50,20,173,20, 25,138,95,161,102,23,27,231,192,60,242,58,180,153,205,193, 190,194,148,254,200,200,95,131,249,184,206,180,42,100,135,62, 130,22,178,26,252,142,172,22,211,101,241,171,133,92,99,19, 63,126,44,55,2,63,38,25,32,18,217,131,207,94,50,73, 246,145,113,50,1,34,121,138,60,77,246,147,28,178,19,100, 239,250,216,74,108,69,94,60,55,214,253,188,140,51,82,166, 155,69,221,57,180,187,160,96,255,51,42,76,92,83,215,172, 44,14,182,134,35,203,41,251,5,93,189,252,198,95,177,92, 233,44,55,81,182,226,25,157,186,87,206,249,95,249,235,252, 50,246,43,252,106,164,224,127,228,87,241,183,114,199,90,204, 187,178,76,195,236,108,88,115,119,85,26,109,6,227,22,240, 209,153,197,137,197,29,243,113,241,228,87,15,70,126,150,201, 153,231,175,244,93,60,89,111,46,14,70,172,151,5,250,106, 249,86,25,78,141,68,172,244,131,244,135,180,44,189,39,125, 40,253,198,189,205,125,202,125,205,125,206,125,193,93,2,145, 59,203,157,227,190,225,46,112,31,115,95,198,246,106,243,26, 90,223,123,22,121,24,183,103,233,150,107,172,74,33,35,108, 21,30,22,114,194,54,225,81,97,50,242,39,12,8,99,66, 81,216,129,150,173,235,251,22,159,47,158,61,29,14,99,31, 230,167,251,92,190,46,86,1,137,251,177,2,244,77,254,85, 85,84,233,112,148,41,29,86,111,77,56,190,65,19,140,36, 67,100,140,148,54,84,247,184,87,243,161,34,149,79,229,82, 105,16,83,59,83,19,169,177,212,65,15,135,179,166,118,160, 109,2,251,252,29,213,169,110,178,82,234,210,99,222,125,2, 89,211,58,110,235,13,205,21,119,75,210,147,98,26,175,54, 42,150,154,234,232,136,168,24,134,200,76,142,104,83,135,218, 109,90,27,5,239,222,244,143,244,155,50,187,15,19,91,46, 71,156,251,12,192,190,63,241,236,251,46,226,142,180,0,150, 28,128,129,199,35,110,24,207,202,7,222,5,56,243,132,218, 178,219,193,29,145,72,124,11,224,212,247,236,246,191,250,50, 120,126,253,212,233,220,196,115,172,231,45,128,181,55,59,157, 191,223,239,116,214,62,64,255,171,0,103,141,127,1,160,159, 124,85,3,92,11,239,0,0,0,56,101,88,73,102,77,77, 0,42,0,0,0,8,0,1,135,105,0,4,0,0,0,1, 0,0,0,26,0,0,0,0,0,2,160,2,0,4,0,0, 0,1,0,0,0,128,160,3,0,4,0,0,0,1,0,0, 0,128,0,0,0,0,107,70,38,77,0,0,3,99,105,84, 88,116,88,77,76,58,99,111,109,46,97,100,111,98,101,46, 120,109,112,0,0,0,0,0,60,120,58,120,109,112,109,101, 116,97,32,120,109,108,110,115,58,120,61,34,97,100,111,98, 101,58,110,115,58,109,101,116,97,47,34,32,120,58,120,109, 112,116,107,61,34,88,77,80,32,67,111,114,101,32,53,46, 52,46,48,34,62,10,32,32,32,60,114,100,102,58,82,68, 70,32,120,109,108,110,115,58,114,100,102,61,34,104,116,116, 112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,49, 57,57,57,47,48,50,47,50,50,45,114,100,102,45,115,121, 110,116,97,120,45,110,115,35,34,62,10,32,32,32,32,32, 32,60,114,100,102,58,68,101,115,99,114,105,112,116,105,111, 110,32,114,100,102,58,97,98,111,117,116,61,34,34,10,32, 32,32,32,32,32,32,32,32,32,32,32,120,109,108,110,115, 58,100,99,61,34,104,116,116,112,58,47,47,112,117,114,108, 46,111,114,103,47,100,99,47,101,108,101,109,101,110,116,115, 47,49,46,49,47,34,10,32,32,32,32,32,32,32,32,32, 32,32,32,120,109,108,110,115,58,73,112,116,99,52,120,109, 112,69,120,116,61,34,104,116,116,112,58,47,47,105,112,116, 99,46,111,114,103,47,115,116,100,47,73,112,116,99,52,120, 109,112,69,120,116,47,50,48,48,56,45,48,50,45,50,57, 47,34,10,32,32,32,32,32,32,32,32,32,32,32,32,120, 109,108,110,115,58,112,104,111,116,111,115,104,111,112,61,34, 104,116,116,112,58,47,47,110,115,46,97,100,111,98,101,46, 99,111,109,47,112,104,111,116,111,115,104,111,112,47,49,46, 48,47,34,62,10,32,32,32,32,32,32,32,32,32,60,100, 99,58,99,114,101,97,116,111,114,62,10,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,83,101,113,62, 10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 60,114,100,102,58,108,105,62,77,105,99,104,97,101,108,32, 83,119,101,101,116,60,47,114,100,102,58,108,105,62,10,32, 32,32,32,32,32,32,32,32,32,32,32,60,47,114,100,102, 58,83,101,113,62,10,32,32,32,32,32,32,32,32,32,60, 47,100,99,58,99,114,101,97,116,111,114,62,10,32,32,32, 32,32,32,32,32,32,60,100,99,58,114,105,103,104,116,115, 62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,114, 100,102,58,65,108,116,62,10,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,108,105,32,120, 109,108,58,108,97,110,103,61,34,120,45,100,101,102,97,117, 108,116,34,62,67,111,112,121,114,105,103,104,116,32,50,48, 49,57,32,77,105,99,104,97,101,108,32,83,119,101,101,116, 60,47,114,100,102,58,108,105,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,60,47,114,100,102,58,65,108,116,62, 10,32,32,32,32,32,32,32,32,32,60,47,100,99,58,114, 105,103,104,116,115,62,10,32,32,32,32,32,32,32,32,32, 60,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,78,101,119,32,73,99,111,110,60,47, 73,112,116,99,52,120,109,112,69,120,116,58,72,101,97,100, 108,105,110,101,62,10,32,32,32,32,32,32,32,32,32,60, 112,104,111,116,111,115,104,111,112,58,72,101,97,100,108,105, 110,101,62,78,101,119,32,73,99,111,110,60,47,112,104,111, 116,111,115,104,111,112,58,72,101,97,100,108,105,110,101,62, 10,32,32,32,32,32,32,60,47,114,100,102,58,68,101,115, 99,114,105,112,116,105,111,110,62,10,32,32,32,60,47,114, 100,102,58,82,68,70,62,10,60,47,120,58,120,109,112,109, 101,116,97,62,10,221,242,56,27,0,0,21,107,73,68,65, 84,120,218,237,93,9,116,83,215,153,190,88,150,100,73,239, 73,178,44,91,222,100,89,146,45,25,99,27,140,177,241,138, 9,73,147,144,182,89,200,116,154,134,66,104,39,91,83,90, 38,33,13,157,38,109,40,217,90,114,210,180,205,100,50,39, 211,54,237,180,51,157,78,50,233,153,78,82,206,36,167,211, 14,167,7,3,3,33,33,11,148,132,18,6,74,10,12,1, 18,54,227,229,206,127,223,114,223,189,239,93,201,218,76,216, 222,61,54,70,207,177,248,190,255,255,191,127,185,247,57,8, 93,188,46,94,23,175,139,215,197,235,12,95,83,80,145,178, 166,92,168,224,29,200,133,60,200,141,74,144,29,217,224,149, 11,138,8,27,42,145,159,150,183,203,223,113,207,119,87,163, 50,228,5,50,28,240,234,5,226,15,54,228,242,126,83,198, 218,218,39,255,80,186,161,36,130,130,200,7,254,224,64,197, 231,59,13,69,96,253,175,73,88,82,224,235,127,122,143,202, 207,201,183,58,147,168,2,249,33,48,156,231,47,13,4,254, 93,212,250,218,146,180,15,121,88,122,73,190,219,51,3,133, 80,41,146,168,58,156,95,240,189,75,229,113,137,194,214,63, 115,107,92,94,239,93,85,50,128,42,81,0,201,231,147,72, 2,124,233,54,29,62,79,129,144,146,109,210,227,238,171,206, 31,145,36,206,127,179,60,38,115,32,205,100,152,255,38,105, 34,233,170,59,215,69,178,8,57,229,37,210,168,25,186,36, 214,2,203,125,239,145,115,91,36,1,190,103,145,60,154,206, 218,146,192,254,150,251,195,222,115,82,36,1,190,119,161,52, 162,39,61,54,242,121,21,200,240,254,152,119,189,180,210,213, 171,136,228,57,64,3,129,127,163,60,34,11,99,60,213,202, 232,254,27,242,163,158,43,80,21,136,164,124,246,138,36,113, 254,207,200,167,101,220,137,95,198,219,241,183,113,143,37,226, 83,169,66,102,247,189,187,165,167,164,5,174,48,136,164,154, 43,206,42,117,80,173,127,58,136,191,139,71,177,126,253,17, 63,142,231,9,108,42,165,181,255,4,247,15,72,63,243,44, 46,137,162,242,179,41,87,40,240,165,211,157,96,121,235,245, 39,252,20,190,18,251,184,184,183,214,4,114,86,247,189,31, 74,191,148,110,87,114,133,239,163,207,21,196,249,23,202,35, 11,241,49,10,186,21,47,192,207,226,19,12,13,7,241,51, 248,90,92,154,34,202,165,9,84,32,197,253,83,222,53,158, 101,142,150,143,54,101,18,235,47,146,71,238,194,227,12,220, 35,248,71,248,99,216,143,23,225,95,227,17,230,245,195,248, 159,240,167,113,80,104,95,217,146,25,50,188,63,34,255,86, 94,225,110,231,82,230,25,35,130,84,125,75,228,209,251,176, 232,218,143,159,192,189,184,28,223,142,255,155,163,231,67,252, 111,120,49,174,76,19,235,82,218,234,81,120,127,28,82,230, 55,92,221,76,95,81,116,102,224,255,149,60,186,148,1,215, 2,241,254,119,120,15,243,202,187,248,91,120,58,174,197,203, 241,38,142,158,147,248,5,124,11,188,158,190,66,148,179,188, 239,221,226,121,88,126,196,85,3,36,56,38,219,11,8,252, 91,165,177,107,24,221,199,248,3,136,253,69,224,228,51,241, 74,252,10,243,250,91,248,235,184,17,214,55,240,54,142,134, 211,144,52,151,226,250,148,218,111,158,39,100,118,223,181,0, 213,64,56,216,38,187,227,187,67,30,107,193,135,4,210,55, 10,78,191,28,39,192,190,119,224,53,248,20,253,142,255,193, 119,225,26,60,3,170,132,221,28,13,163,120,45,190,27,190, 95,20,229,98,15,72,127,223,243,21,52,21,202,166,73,244, 1,152,245,121,151,65,220,225,13,105,165,111,59,94,141,251, 33,5,94,143,127,130,255,79,251,174,113,252,59,124,27,248, 72,31,254,91,80,9,246,26,135,159,118,47,208,40,142,117, 41,101,29,41,232,49,30,68,179,160,114,44,153,44,2,200, 168,115,57,121,203,149,25,73,223,65,252,99,240,13,31,30, 192,143,226,63,80,215,127,17,127,22,200,186,28,210,227,17, 211,79,120,13,175,194,29,2,17,76,57,79,176,220,247,124, 27,245,162,90,168,23,167,76,14,124,151,252,55,228,109,218, 24,231,54,103,126,171,244,157,130,96,248,2,188,146,0,103, 95,171,233,198,9,252,175,248,58,248,111,205,215,171,248,106, 156,245,60,129,249,10,8,232,71,117,80,41,22,77,10,124, 239,253,42,215,47,78,152,249,69,210,183,25,223,143,219,33, 4,22,227,231,32,33,90,175,163,248,75,105,59,3,121,130, 206,129,44,247,195,147,69,0,192,151,30,84,223,228,242,140, 51,191,72,250,246,224,39,33,97,182,88,126,194,90,156,204, 100,94,48,193,125,247,42,212,135,194,133,39,128,56,255,106, 189,95,95,155,133,251,167,147,62,246,250,158,210,51,228,63, 79,40,185,79,35,160,128,26,48,69,113,254,199,117,182,47, 205,201,253,211,73,223,24,254,114,174,243,2,203,114,173,64, 61,133,21,65,5,190,244,132,241,166,207,229,225,254,34,233, 27,197,75,242,156,23,176,247,93,119,162,110,40,133,10,150, 6,9,124,183,247,41,163,246,138,114,45,14,158,160,240,205, 196,253,191,88,168,121,129,114,223,121,7,154,93,184,58,0, 224,87,187,229,167,217,184,91,145,166,241,205,197,253,31,206, 114,30,48,209,125,251,205,168,19,218,34,103,33,8,80,225, 255,128,103,121,243,132,141,111,54,238,255,98,174,243,128,148, 247,237,55,161,14,104,143,157,5,81,126,21,62,27,103,137, 140,27,223,76,220,255,79,74,71,152,199,60,64,112,223,246, 25,52,19,198,36,142,66,36,62,112,126,51,195,203,178,136, 255,137,221,255,218,194,204,3,184,101,91,128,102,192,240,212, 94,136,170,239,73,171,187,61,143,211,93,169,26,95,177,251, 255,188,192,243,0,242,81,182,14,93,131,90,161,27,180,231, 95,246,60,38,234,183,15,224,137,175,84,141,47,127,125,136, 27,114,234,247,211,221,247,111,74,140,163,79,162,22,152,12, 21,231,217,241,73,15,136,24,158,134,51,189,38,142,255,71, 114,236,247,83,223,247,189,158,56,149,196,232,19,249,18,80, 164,55,188,214,136,187,17,103,119,165,142,255,35,224,35,185, 245,251,169,238,123,119,54,28,73,128,72,231,235,1,100,135, 247,115,169,246,247,31,198,185,92,162,248,127,44,231,126,63, 197,253,63,199,247,37,177,66,192,213,160,1,57,19,48,5, 57,74,63,238,27,246,98,178,172,146,243,47,184,48,215,40, 110,206,185,223,23,221,247,30,141,189,147,192,132,0,8,129, 107,81,27,136,96,113,110,240,237,190,153,165,135,75,161,47, 243,41,20,152,223,118,125,129,8,248,117,30,253,190,224,254, 112,221,214,4,214,87,238,4,76,65,197,129,112,240,157,50, 92,6,187,56,1,160,192,207,208,160,190,213,187,5,34,96, 73,62,231,7,204,247,199,107,54,38,176,106,127,133,128,235, 114,37,192,86,46,85,188,92,14,53,93,25,86,73,32,139, 144,32,107,1,113,11,30,46,8,252,227,184,34,255,243,3, 244,181,138,161,36,181,126,50,15,2,64,251,67,15,86,225, 16,172,114,101,5,53,79,80,73,32,190,112,43,164,180,223, 224,247,242,38,224,197,2,158,31,40,27,210,161,231,231,1, 16,253,213,151,87,143,85,193,166,21,33,161,66,163,129,245, 5,66,194,84,80,243,21,176,13,254,18,254,95,83,221,159, 205,245,215,121,245,251,236,125,255,230,196,24,111,255,100,142, 4,216,66,21,117,59,107,113,53,172,26,172,210,80,65,125, 161,12,188,65,245,5,66,66,0,106,254,27,96,134,255,24, 72,217,31,97,150,147,253,213,145,87,191,207,108,133,189,217, 120,50,201,68,127,34,103,2,192,253,195,143,214,225,58,104, 102,194,184,86,33,161,82,161,161,130,134,132,238,11,1,133, 4,63,124,213,133,111,130,154,127,53,254,21,222,145,114,68, 34,186,14,229,217,239,211,83,35,187,26,14,155,173,159,107, 8,20,215,182,69,79,213,195,46,93,29,214,105,32,36,84, 83,79,48,124,161,76,241,4,53,71,248,225,222,0,72,227, 253,80,214,62,143,223,204,80,34,127,83,152,243,1,7,226, 123,147,38,232,185,106,64,17,114,53,252,42,138,99,48,236, 50,72,32,158,160,134,132,53,32,116,26,124,154,56,214,224, 203,96,172,181,18,63,4,21,223,22,203,132,200,124,61,81, 128,243,1,242,135,209,29,172,237,89,33,204,150,0,200,254, 177,206,198,241,6,76,86,12,86,61,165,33,172,5,132,74, 131,154,33,120,95,8,104,190,64,210,100,12,95,5,93,224, 74,252,0,180,185,27,132,219,30,234,181,52,207,126,31,254, 126,58,252,26,111,117,246,111,217,18,0,246,111,122,38,169, 116,241,13,56,14,43,102,242,133,90,133,8,221,23,42,77, 36,168,186,160,39,74,25,55,41,121,98,37,236,240,253,35, 76,136,222,23,16,112,109,94,253,62,249,170,106,163,14,56, 137,173,42,144,45,1,197,209,200,212,83,77,244,71,53,42, 158,160,210,16,161,52,212,42,171,70,209,5,222,23,12,18, 74,21,85,32,69,147,31,242,196,167,33,79,172,196,223,132, 169,225,75,166,118,120,86,158,251,255,108,225,195,59,127,46, 30,0,205,79,243,237,211,32,191,147,149,84,150,238,11,106, 64,196,56,95,168,85,40,208,117,193,234,11,165,140,47,144, 60,177,24,242,196,74,88,79,65,174,208,79,142,196,242,218, 255,15,172,55,172,157,52,57,191,250,90,118,4,20,33,119, 235,11,173,208,155,77,195,58,13,77,202,143,36,52,16,18, 162,22,95,8,83,26,248,28,17,228,124,193,167,30,105,131, 215,251,149,60,65,104,248,46,254,5,126,7,190,39,247,253, 127,255,150,196,168,53,242,121,143,200,142,0,40,128,218,78, 78,135,94,189,5,171,52,24,190,208,164,5,132,234,11,68, 21,34,38,95,168,97,124,161,66,161,160,156,235,34,252,154, 47,200,240,125,243,64,252,86,42,235,150,220,250,125,229,217, 130,198,227,162,196,199,19,145,29,1,246,228,37,237,48,189, 107,131,168,109,83,104,152,70,125,65,15,136,132,230,11,134, 56,26,186,16,86,104,168,164,1,81,65,43,71,149,134,128, 166,11,178,66,68,20,207,199,119,226,155,115,232,247,149,159, 176,59,113,200,236,240,252,231,236,75,225,41,168,100,198,178, 153,112,168,105,6,236,218,183,41,75,228,11,42,13,113,77, 28,117,95,136,80,26,170,105,237,24,98,124,33,200,117,17, 126,141,4,57,215,253,255,131,177,61,214,170,79,228,13,217, 16,0,10,208,241,131,89,160,203,58,9,134,47,180,78,224, 11,102,113,172,213,124,193,154,35,130,90,209,164,231,8,111, 14,251,255,222,99,245,219,147,130,194,71,84,9,100,71,128, 212,245,159,157,120,22,214,73,32,52,176,36,180,48,226,200, 147,96,78,148,17,205,23,210,21,208,165,154,39,200,116,220, 150,225,60,96,164,118,75,106,167,55,83,146,13,1,54,228, 237,217,210,13,233,138,44,131,134,118,133,6,66,66,139,208, 23,18,52,85,166,247,5,107,209,196,251,130,156,225,60,160, 106,3,159,233,173,214,231,230,1,89,140,196,138,145,191,119, 95,47,158,141,187,225,67,37,161,131,243,133,233,138,55,232, 170,48,77,73,146,83,105,181,160,231,136,168,160,104,170,166, 245,66,136,233,35,130,220,164,201,155,129,42,144,194,199,26, 251,73,97,18,212,68,240,154,204,167,194,197,168,180,255,84, 31,60,222,208,11,31,221,176,58,97,117,81,79,232,80,72, 80,67,162,85,89,205,156,47,52,105,213,66,42,93,8,211, 128,168,18,250,130,159,14,220,82,207,3,2,27,68,202,159, 192,137,20,34,216,184,14,93,5,7,37,253,153,157,20,45, 70,129,1,104,104,251,49,33,161,71,35,161,155,146,160,251, 130,42,142,109,84,28,155,133,186,16,215,138,38,163,94,168, 229,116,65,52,93,224,19,165,117,30,224,219,146,28,17,41, 127,50,5,29,141,184,226,30,52,15,197,225,180,112,102,4, 200,193,65,60,7,19,18,250,96,245,43,158,160,134,132,161, 11,51,105,142,104,167,213,66,171,73,28,85,26,26,82,234, 66,141,54,105,178,234,66,144,6,132,159,201,16,154,246,111, 79,28,179,218,56,105,130,156,208,194,81,233,98,214,161,79, 161,46,56,32,147,225,9,33,240,128,185,152,80,48,168,144, 48,160,144,160,251,194,108,170,11,70,162,228,235,133,102,75, 142,208,235,5,117,186,16,97,26,107,235,116,33,100,10,136, 82,218,69,104,190,176,167,225,160,168,223,103,193,83,224,218, 42,95,129,174,128,0,8,192,222,112,134,4,148,182,108,232, 249,253,37,135,8,13,131,26,13,125,202,226,3,66,165,161, 195,68,131,161,11,70,23,97,110,166,216,2,58,44,108,166, 140,2,58,192,248,130,124,40,190,91,172,250,141,38,224,113, 250,110,241,117,232,47,225,128,84,109,230,103,4,33,11,52, 129,198,54,13,183,111,234,127,117,238,40,239,11,172,46,16, 10,58,181,28,209,161,145,96,46,154,154,53,10,154,184,122, 33,74,3,34,130,205,83,199,202,20,141,53,80,112,34,186, 205,128,109,184,121,130,177,118,92,251,76,130,78,13,188,178, 44,237,175,212,1,141,63,211,157,171,249,207,157,67,131,187, 116,79,152,67,117,129,144,96,36,202,78,45,32,58,152,68, 201,214,11,122,64,52,97,118,200,18,229,38,77,117,52,67, 84,107,163,87,126,224,22,24,173,123,197,108,109,195,222,122, 25,22,167,227,27,77,117,178,182,63,33,64,110,124,128,23, 152,214,55,123,54,12,126,96,4,132,154,35,122,169,46,24, 36,176,5,180,94,52,181,80,95,208,73,72,208,158,50,42, 152,52,169,69,83,21,157,64,171,205,84,205,250,70,205,234, 4,114,35,3,155,181,183,218,156,25,63,53,123,251,147,82, 216,19,251,188,53,153,52,29,111,223,216,255,250,224,248,32, 23,16,253,140,46,16,26,58,76,5,244,116,70,23,120,113, 76,152,6,110,245,41,134,44,106,64,84,15,53,152,28,61, 110,178,118,148,89,245,122,135,154,131,253,73,55,232,170,157, 107,173,169,212,196,51,109,79,215,208,220,189,134,46,244,155, 114,4,235,11,29,90,181,208,78,3,130,111,172,245,33,139, 17,16,186,47,68,184,33,75,21,174,217,104,64,143,153,96, 199,76,176,35,154,182,144,85,154,131,253,9,1,78,127,125, 114,156,239,167,57,229,29,111,219,218,187,113,240,4,239,11, 125,90,229,168,215,11,179,76,245,130,104,186,96,78,148,113, 193,116,1,196,241,181,134,211,102,224,186,163,215,51,192,35, 20,120,68,21,215,156,236,175,12,68,80,48,177,39,93,121, 73,254,108,58,58,115,253,156,109,230,68,217,75,229,177,211, 82,52,25,205,84,139,165,128,78,210,113,91,131,6,141,38, 202,29,13,31,196,82,56,122,68,35,139,131,173,37,215,186, 28,237,175,85,2,201,223,38,44,1,32,170,185,166,237,154, 61,52,103,255,160,169,104,234,133,101,20,77,29,2,95,152, 110,234,34,12,93,72,48,93,4,64,218,27,63,16,165,142, 94,207,89,187,206,2,58,172,0,87,11,172,218,156,237,79, 242,128,175,241,135,201,180,67,38,142,146,209,233,175,244,110, 158,51,204,22,208,106,51,213,77,125,161,211,212,81,234,205, 148,120,210,68,155,169,195,209,93,81,26,20,169,172,93,167, 1,175,213,54,109,212,85,122,15,186,60,55,251,147,60,32, 199,239,21,149,153,201,52,147,247,230,67,29,67,115,222,214, 73,24,160,36,168,57,98,54,87,57,118,8,139,38,182,94, 80,178,252,201,216,155,81,45,178,213,146,201,108,111,35,113, 170,115,72,53,129,146,21,122,26,70,32,179,160,254,207,233, 25,17,146,8,111,204,174,223,214,191,106,217,209,61,52,248, 190,85,23,140,128,232,202,104,210,4,25,98,44,182,153,183, 118,68,139,237,48,93,181,116,233,192,213,46,179,226,25,112, 255,126,232,255,252,185,216,95,73,132,53,61,217,244,219,73, 166,11,35,69,244,140,77,125,91,72,17,109,248,66,47,165, 161,75,208,76,205,80,84,129,207,17,13,67,117,38,39,15, 83,107,135,53,107,179,192,153,221,202,159,162,27,208,92,148, 132,9,144,51,183,103,132,32,17,186,106,72,207,61,81,191, 157,76,211,144,36,247,119,12,13,236,226,19,165,218,86,207, 166,125,68,7,55,100,209,39,77,68,23,18,67,134,154,135, 181,65,74,88,179,181,1,91,183,56,129,173,111,202,132,126, 14,240,47,129,232,47,135,246,55,231,71,101,33,17,38,119, 136,125,32,105,178,185,168,33,137,211,10,175,249,173,238,13, 131,71,205,205,84,175,41,32,204,83,199,230,141,117,227,134, 139,215,88,220,156,181,119,149,54,93,34,21,35,148,206,207, 218,110,4,248,205,112,52,222,149,207,147,194,144,8,19,107, 18,194,189,86,115,215,109,52,186,169,42,243,248,241,233,27, 251,183,14,142,15,8,117,161,147,118,148,186,46,180,109,173, 27,14,11,97,215,80,224,213,12,240,74,109,65,201,252,75, 128,63,79,129,239,206,239,65,105,232,8,19,79,154,230,106, 38,39,87,63,226,76,77,110,174,211,140,110,143,172,198,189, 51,135,230,236,29,52,5,132,222,88,119,210,225,107,251,59, 225,163,86,123,215,104,96,171,181,134,153,131,173,111,189,252, 7,192,191,20,77,131,39,67,220,249,62,39,14,123,3,241, 229,137,20,209,221,56,65,67,18,227,27,18,38,119,215,141, 79,221,218,13,69,180,209,81,246,48,141,181,66,194,190,200, 123,188,155,235,174,94,73,221,156,133,29,210,250,69,128,191, 198,182,144,129,63,37,95,2,220,145,235,18,92,207,221,200, 181,159,34,216,245,12,104,163,37,169,179,36,177,250,163,45, 27,250,223,26,164,149,163,161,11,221,71,234,119,26,144,245, 195,23,86,216,21,218,180,64,157,23,40,237,242,75,182,207, 162,203,224,68,120,101,33,224,43,251,131,229,51,116,216,13, 66,216,230,186,220,128,93,103,169,212,216,52,166,151,44,241, 93,80,56,237,103,197,177,239,84,236,141,106,206,201,173,192, 245,125,198,10,109,71,65,219,89,248,47,219,34,244,177,194, 193,39,151,195,19,106,56,222,64,99,91,212,128,214,51,91, 162,108,165,22,17,230,110,93,205,217,20,86,61,154,220,210, 189,121,112,88,33,97,60,185,169,74,219,59,178,218,219,128, 93,161,129,38,240,213,21,88,107,91,12,240,91,225,185,64, 79,225,126,181,150,29,149,197,95,139,153,96,199,152,168,54, 215,229,17,6,52,15,92,156,196,170,104,26,11,191,223,54, 212,247,118,235,144,213,218,21,218,18,193,14,106,243,194,178, 117,182,155,160,234,111,83,224,23,23,238,209,88,24,141,198, 158,143,113,125,152,24,56,223,137,241,5,139,24,54,43,106, 149,154,163,155,157,92,5,205,58,58,15,156,236,46,43,147, 194,13,182,37,208,244,22,28,190,146,8,163,143,233,227,6, 115,223,205,118,221,214,62,172,38,13,108,54,129,89,237,29, 74,233,230,101,12,236,128,118,24,175,20,151,109,182,125,14, 224,79,71,213,133,134,175,36,194,240,23,69,214,142,112,78, 158,14,182,225,230,149,116,230,47,118,115,29,118,208,2,59, 168,109,145,168,31,234,182,89,64,57,148,11,187,5,175,218, 62,143,174,84,224,75,133,134,79,242,128,187,114,190,145,192, 248,174,91,92,153,87,167,112,116,93,205,67,2,53,47,167, 103,71,172,78,174,218,89,223,23,8,104,59,69,250,73,163, 210,55,224,121,224,43,225,113,200,154,201,128,175,52,68,193, 132,97,111,125,103,183,54,37,236,42,70,214,210,105,185,97, 113,43,236,32,221,20,211,79,31,27,160,85,216,126,109,247, 216,183,205,126,11,154,63,121,240,213,60,80,26,250,100,237, 179,117,167,106,211,56,122,21,167,231,134,168,89,107,181,244, 162,86,166,139,154,5,184,1,218,79,207,27,122,223,182,223, 10,27,222,237,48,240,146,39,11,190,82,13,66,97,49,205, 61,191,234,241,154,221,213,194,232,54,138,22,49,104,125,103, 167,34,165,181,3,154,155,167,181,182,1,92,249,236,219,105, 191,13,224,207,84,224,219,39,239,87,35,193,129,105,112,175, 16,204,85,218,139,231,6,151,87,253,174,102,188,202,34,108, 98,45,55,162,91,172,229,6,240,128,0,184,126,178,212,71, 255,244,210,5,187,196,239,218,111,71,31,7,248,225,201,133, 175,83,80,2,191,160,48,132,162,80,103,245,72,55,84,254, 184,234,176,85,212,172,149,90,102,209,173,238,251,26,160,13, 107,123,41,112,243,169,33,105,143,253,14,128,223,113,38,224, 171,20,20,1,9,78,120,179,32,188,101,19,154,85,124,69, 112,85,232,245,144,182,121,89,97,74,100,34,123,179,192,89, 123,251,45,110,158,26,54,61,25,182,207,190,20,158,2,38, 240,189,103,2,62,251,27,67,28,160,8,165,80,111,53,64, 222,237,247,221,86,241,239,161,225,114,161,168,89,65,235,135, 94,172,209,205,199,182,225,230,28,104,99,237,183,127,9,158, 1,158,5,191,16,233,140,194,55,124,193,14,1,225,133,105, 75,4,186,238,217,142,107,202,190,95,190,215,90,169,5,152, 71,104,82,171,185,95,23,52,33,108,217,12,157,172,131,246, 101,0,191,19,222,221,119,230,225,179,190,224,4,105,12,64, 254,77,160,153,197,115,125,95,9,254,222,234,226,98,216,62, 129,189,83,89,219,67,255,84,151,244,190,243,78,120,0,90, 133,239,248,104,127,107,44,33,193,14,35,71,34,142,49,16, 199,94,207,194,210,159,6,142,6,82,194,54,100,205,107,58, 242,196,158,254,100,193,155,151,116,184,120,57,192,239,66,245, 31,61,124,86,28,75,168,56,118,22,95,225,123,168,116,155, 176,104,161,143,217,90,207,0,179,246,102,224,210,175,220,202, 242,124,80,124,55,28,116,60,139,224,243,226,232,209,196,113, 6,26,144,190,224,127,193,55,226,101,108,157,86,212,52,71, 231,97,187,153,229,34,159,143,57,238,129,131,174,179,33,21, 251,225,221,138,208,89,118,169,226,232,82,196,177,30,196,177, 219,113,157,247,73,239,123,86,71,151,4,22,151,52,200,44, 112,15,5,79,224,187,78,56,190,10,240,187,207,86,248,108, 201,68,196,177,12,202,211,4,234,40,158,231,249,170,60,36, 165,177,55,11,156,183,186,102,119,133,0,215,73,199,215,96, 147,179,27,180,230,172,134,207,138,163,27,254,169,149,240,15, 110,67,189,37,139,165,127,150,143,121,4,178,230,22,44,151, 14,90,89,37,100,13,59,238,3,248,61,240,211,74,207,126, 248,86,113,172,131,253,185,78,219,124,215,183,228,63,184,25, 155,179,22,119,177,214,166,208,85,248,174,211,142,251,209,2, 248,77,160,113,128,239,60,55,224,155,197,49,0,179,26,69, 28,157,75,221,107,220,163,38,247,22,128,214,151,19,151,140, 56,86,161,235,21,248,129,115,13,62,47,142,62,42,142,215, 187,255,222,115,192,236,232,46,43,116,248,112,142,57,30,2, 248,125,64,223,57,10,223,152,41,24,226,152,4,113,188,180, 228,94,215,70,30,180,147,5,174,174,49,199,35,232,47,224, 128,195,57,15,95,44,142,125,246,37,37,191,40,57,238,100, 40,96,151,99,220,177,90,129,223,152,251,1,135,179,89,28, 203,21,113,236,178,125,194,185,186,100,39,192,197,234,7,249, 172,46,251,119,224,116,255,128,2,191,228,124,129,47,18,199, 70,152,51,205,177,127,217,249,178,115,204,0,15,240,191,167, 192,79,156,143,240,205,226,24,2,113,108,65,61,182,79,57, 255,193,126,8,160,147,245,4,192,159,3,240,131,231,43,124, 177,56,206,42,190,172,248,235,246,87,0,254,213,16,251,23, 0,124,126,206,228,135,86,42,14,226,216,5,53,95,59,124, 69,224,219,208,5,114,233,226,232,5,216,213,224,13,149,64, 199,5,4,159,247,5,151,246,191,97,45,66,23,175,139,215, 197,235,226,117,134,174,255,7,18,156,48,32,143,188,77,137, 0,0,0,0,73,69,78,68,174,66,96,130, }; /* label-sm.png */ static unsigned char label_sm_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82, 0,0,0,48,0,0,0,48,8,4,0,0,0,253,11,49, 12,0,0,4,25,105,67,67,80,107,67,71,67,111,108,111, 114,83,112,97,99,101,71,101,110,101,114,105,99,82,71,66, 0,0,56,141,141,85,93,104,28,85,20,62,187,115,103,35, 36,206,83,108,52,133,116,168,63,13,37,13,147,86,52,161, 180,186,127,221,221,54,110,150,73,54,218,34,232,100,246,238, 206,152,201,206,56,51,187,253,161,79,69,80,124,49,234,155, 20,196,191,183,128,32,40,245,15,219,62,180,47,149,10,37, 218,212,32,40,62,180,248,131,80,232,139,166,235,153,59,51, 153,105,186,177,222,101,238,124,243,157,239,158,123,238,185,103, 239,5,232,185,170,88,150,145,20,1,22,154,174,45,23,50, 226,115,135,143,136,61,43,144,132,135,160,23,6,161,87,81, 29,43,93,169,76,2,54,79,11,119,181,91,223,67,194,123, 95,217,213,221,254,159,173,183,70,29,21,32,113,31,98,179, 230,168,11,136,143,1,240,167,85,203,118,1,122,250,145,31, 63,234,90,30,246,98,232,183,49,64,196,47,122,184,225,99, 215,195,115,62,126,141,105,102,228,44,226,211,136,5,85,83, 106,136,151,16,143,204,197,248,70,12,251,49,176,214,95,160, 77,106,235,170,232,229,162,98,155,117,221,160,177,112,239,97, 254,159,109,193,104,133,243,109,195,167,207,153,159,62,132,239, 97,92,251,43,53,37,231,225,81,196,75,170,146,159,70,252, 8,226,107,109,125,182,28,224,219,150,155,145,17,63,6,144, 220,222,154,175,166,17,239,68,92,172,219,7,170,190,159,164, 173,181,138,33,126,231,132,54,243,44,226,45,136,207,55,231, 202,83,193,216,171,170,147,197,156,193,118,196,183,53,90,242, 242,59,4,192,137,186,91,154,241,199,114,251,109,83,158,242, 231,229,234,53,154,203,123,121,68,252,250,188,121,72,246,125, 114,159,57,237,233,124,232,243,132,150,45,7,252,165,151,148, 131,21,196,131,136,127,161,70,65,246,231,226,254,177,220,74, 16,3,25,106,26,229,73,127,46,146,163,14,91,47,227,93, 109,166,232,207,75,12,23,55,212,31,75,22,235,250,129,82, 160,255,68,179,139,114,128,175,89,6,171,81,140,141,79,218, 45,185,234,235,249,81,197,206,23,124,159,124,133,54,171,129, 127,190,13,179,9,5,40,152,48,135,189,10,77,88,3,17, 100,40,64,6,223,22,216,104,169,131,14,6,50,20,173,20, 25,138,95,161,102,23,27,231,192,60,242,58,180,153,205,193, 190,194,148,254,200,200,95,131,249,184,206,180,42,100,135,62, 130,22,178,26,252,142,172,22,211,101,241,171,133,92,99,19, 63,126,44,55,2,63,38,25,32,18,217,131,207,94,50,73, 246,145,113,50,1,34,121,138,60,77,246,147,28,178,19,100, 239,250,216,74,108,69,94,60,55,214,253,188,140,51,82,166, 155,69,221,57,180,187,160,96,255,51,42,76,92,83,215,172, 44,14,182,134,35,203,41,251,5,93,189,252,198,95,177,92, 233,44,55,81,182,226,25,157,186,87,206,249,95,249,235,252, 50,246,43,252,106,164,224,127,228,87,241,183,114,199,90,204, 187,178,76,195,236,108,88,115,119,85,26,109,6,227,22,240, 209,153,197,137,197,29,243,113,241,228,87,15,70,126,150,201, 153,231,175,244,93,60,89,111,46,14,70,172,151,5,250,106, 249,86,25,78,141,68,172,244,131,244,135,180,44,189,39,125, 40,253,198,189,205,125,202,125,205,125,206,125,193,93,2,145, 59,203,157,227,190,225,46,112,31,115,95,198,246,106,243,26, 90,223,123,22,121,24,183,103,233,150,107,172,74,33,35,108, 21,30,22,114,194,54,225,81,97,50,242,39,12,8,99,66, 81,216,129,150,173,235,251,22,159,47,158,61,29,14,99,31, 230,167,251,92,190,46,86,1,137,251,177,2,244,77,254,85, 85,84,233,112,148,41,29,86,111,77,56,190,65,19,140,36, 67,100,140,148,54,84,247,184,87,243,161,34,149,79,229,82, 105,16,83,59,83,19,169,177,212,65,15,135,179,166,118,160, 109,2,251,252,29,213,169,110,178,82,234,210,99,222,125,2, 89,211,58,110,235,13,205,21,119,75,210,147,98,26,175,54, 42,150,154,234,232,136,168,24,134,200,76,142,104,83,135,218, 109,90,27,5,239,222,244,143,244,155,50,187,15,19,91,46, 71,156,251,12,192,190,63,241,236,251,46,226,142,180,0,150, 28,128,129,199,35,110,24,207,202,7,222,5,56,243,132,218, 178,219,193,29,145,72,124,11,224,212,247,236,246,191,250,50, 120,126,253,212,233,220,196,115,172,231,45,128,181,55,59,157, 191,223,239,116,214,62,64,255,171,0,103,141,127,1,160,159, 124,85,3,92,11,239,0,0,0,56,101,88,73,102,77,77, 0,42,0,0,0,8,0,1,135,105,0,4,0,0,0,1, 0,0,0,26,0,0,0,0,0,2,160,2,0,4,0,0, 0,1,0,0,0,48,160,3,0,4,0,0,0,1,0,0, 0,48,0,0,0,0,248,255,78,54,0,0,3,99,105,84, 88,116,88,77,76,58,99,111,109,46,97,100,111,98,101,46, 120,109,112,0,0,0,0,0,60,120,58,120,109,112,109,101, 116,97,32,120,109,108,110,115,58,120,61,34,97,100,111,98, 101,58,110,115,58,109,101,116,97,47,34,32,120,58,120,109, 112,116,107,61,34,88,77,80,32,67,111,114,101,32,53,46, 52,46,48,34,62,10,32,32,32,60,114,100,102,58,82,68, 70,32,120,109,108,110,115,58,114,100,102,61,34,104,116,116, 112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,49, 57,57,57,47,48,50,47,50,50,45,114,100,102,45,115,121, 110,116,97,120,45,110,115,35,34,62,10,32,32,32,32,32, 32,60,114,100,102,58,68,101,115,99,114,105,112,116,105,111, 110,32,114,100,102,58,97,98,111,117,116,61,34,34,10,32, 32,32,32,32,32,32,32,32,32,32,32,120,109,108,110,115, 58,100,99,61,34,104,116,116,112,58,47,47,112,117,114,108, 46,111,114,103,47,100,99,47,101,108,101,109,101,110,116,115, 47,49,46,49,47,34,10,32,32,32,32,32,32,32,32,32, 32,32,32,120,109,108,110,115,58,73,112,116,99,52,120,109, 112,69,120,116,61,34,104,116,116,112,58,47,47,105,112,116, 99,46,111,114,103,47,115,116,100,47,73,112,116,99,52,120, 109,112,69,120,116,47,50,48,48,56,45,48,50,45,50,57, 47,34,10,32,32,32,32,32,32,32,32,32,32,32,32,120, 109,108,110,115,58,112,104,111,116,111,115,104,111,112,61,34, 104,116,116,112,58,47,47,110,115,46,97,100,111,98,101,46, 99,111,109,47,112,104,111,116,111,115,104,111,112,47,49,46, 48,47,34,62,10,32,32,32,32,32,32,32,32,32,60,100, 99,58,99,114,101,97,116,111,114,62,10,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,83,101,113,62, 10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 60,114,100,102,58,108,105,62,77,105,99,104,97,101,108,32, 83,119,101,101,116,60,47,114,100,102,58,108,105,62,10,32, 32,32,32,32,32,32,32,32,32,32,32,60,47,114,100,102, 58,83,101,113,62,10,32,32,32,32,32,32,32,32,32,60, 47,100,99,58,99,114,101,97,116,111,114,62,10,32,32,32, 32,32,32,32,32,32,60,100,99,58,114,105,103,104,116,115, 62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,114, 100,102,58,65,108,116,62,10,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,60,114,100,102,58,108,105,32,120, 109,108,58,108,97,110,103,61,34,120,45,100,101,102,97,117, 108,116,34,62,67,111,112,121,114,105,103,104,116,32,50,48, 49,57,32,77,105,99,104,97,101,108,32,83,119,101,101,116, 60,47,114,100,102,58,108,105,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,60,47,114,100,102,58,65,108,116,62, 10,32,32,32,32,32,32,32,32,32,60,47,100,99,58,114, 105,103,104,116,115,62,10,32,32,32,32,32,32,32,32,32, 60,73,112,116,99,52,120,109,112,69,120,116,58,72,101,97, 100,108,105,110,101,62,78,101,119,32,73,99,111,110,60,47, 73,112,116,99,52,120,109,112,69,120,116,58,72,101,97,100, 108,105,110,101,62,10,32,32,32,32,32,32,32,32,32,60, 112,104,111,116,111,115,104,111,112,58,72,101,97,100,108,105, 110,101,62,78,101,119,32,73,99,111,110,60,47,112,104,111, 116,111,115,104,111,112,58,72,101,97,100,108,105,110,101,62, 10,32,32,32,32,32,32,60,47,114,100,102,58,68,101,115, 99,114,105,112,116,105,111,110,62,10,32,32,32,60,47,114, 100,102,58,82,68,70,62,10,60,47,120,58,120,109,112,109, 101,116,97,62,10,221,242,56,27,0,0,6,18,73,68,65, 84,88,195,205,88,107,108,83,101,24,254,214,181,167,235,118, 206,217,122,218,173,235,117,237,186,118,184,139,12,54,29,101, 23,131,65,162,81,103,32,138,9,94,98,212,95,4,141,18, 19,209,24,35,9,248,151,63,38,70,163,36,42,137,127,192, 72,212,31,234,140,63,20,52,138,36,154,152,200,85,2,136, 147,31,178,49,86,217,70,251,250,124,223,185,245,180,29,56, 24,137,223,147,179,45,237,250,60,239,251,62,239,119,43,99, 255,155,81,195,106,153,27,112,225,175,155,48,92,172,78,110, 97,17,214,202,252,172,129,73,16,115,45,45,125,61,139,43, 7,149,121,229,152,242,142,123,45,75,178,16,107,100,62,230, 89,154,124,56,125,155,252,173,66,10,201,128,66,234,140,242, 163,188,93,234,97,49,22,96,242,141,230,35,232,149,111,56, 189,9,89,60,106,81,57,87,191,215,183,158,165,144,143,202, 234,174,207,31,23,42,158,148,15,152,180,230,79,7,242,202, 15,245,47,75,203,224,144,31,193,240,124,106,22,67,159,26, 252,229,19,58,66,187,104,173,35,254,10,201,130,250,123,253, 110,247,40,75,176,102,148,205,243,95,68,56,125,251,189,199, 206,18,81,15,141,209,7,116,130,222,163,135,169,185,74,38, 178,253,251,130,252,69,221,227,172,133,121,175,37,33,232,87, 28,253,3,244,221,180,146,214,81,63,62,206,101,38,232,83, 218,76,41,71,46,21,5,60,192,52,148,234,170,244,178,183, 67,249,237,43,65,223,79,15,210,38,186,155,66,212,72,97, 242,210,16,189,75,23,232,59,122,5,194,213,179,81,207,195, 17,247,194,244,181,160,207,170,39,55,17,9,129,59,233,41, 90,15,234,48,68,30,161,7,168,13,20,61,120,231,20,61, 228,136,93,182,254,86,167,225,133,103,97,122,69,234,82,206, 40,116,24,36,189,160,222,66,183,83,132,94,164,113,250,144, 158,165,219,32,201,199,1,74,84,120,96,225,50,154,87,90, 152,190,71,57,167,208,106,97,238,61,244,4,74,20,65,14, 79,227,231,32,189,137,226,240,241,19,5,203,168,229,82,87, 174,176,246,234,2,46,65,63,193,255,109,39,104,166,96,234, 24,169,180,134,238,163,86,228,177,5,249,244,226,245,11,212, 81,70,93,142,234,2,176,86,186,69,249,83,255,200,56,153, 67,151,81,16,255,32,117,137,87,158,89,96,78,152,62,104, 127,99,189,242,84,89,20,188,105,245,148,25,195,89,114,14, 93,134,155,123,2,221,84,30,113,105,203,250,167,59,103,43, 77,174,193,106,18,215,14,169,196,193,255,245,31,90,104,60, 239,232,156,178,137,70,141,51,157,212,57,201,226,229,2,30, 214,28,216,221,74,1,1,21,213,126,141,62,171,42,50,103, 117,143,92,197,102,117,54,91,204,82,230,12,139,58,231,1, 204,13,141,197,139,113,88,25,2,2,120,250,105,35,109,167, 189,116,174,76,96,188,202,12,182,48,223,81,200,82,39,165, 127,198,214,228,118,198,31,77,158,76,97,1,136,9,180,2, 26,100,130,232,249,49,122,155,126,165,130,37,240,106,21,106, 99,138,21,218,231,59,73,8,140,99,201,171,45,173,191,28, 219,192,223,202,82,169,136,158,139,134,130,181,211,99,244,57, 93,18,2,247,151,213,222,20,82,139,169,217,44,233,72,191, 143,181,200,85,58,185,130,189,95,118,161,5,187,41,99,136, 112,153,56,4,184,140,102,248,146,164,29,232,160,109,85,59, 72,161,120,94,39,231,129,182,237,192,22,84,178,154,186,89, 180,255,175,62,90,14,116,67,38,107,201,216,5,11,24,185, 112,84,219,15,34,211,58,181,142,200,147,88,139,75,4,36, 150,204,205,13,16,7,23,233,50,100,236,92,116,17,221,23, 187,145,109,180,76,153,212,157,66,70,201,161,233,89,169,64, 122,148,86,81,14,157,99,138,84,207,37,68,118,35,171,150, 3,205,83,102,105,12,145,153,242,105,6,129,53,52,10,228, 128,85,150,72,31,85,250,162,139,148,230,226,159,214,233,51, 70,56,105,74,126,204,130,206,237,70,98,169,158,124,110,114, 180,48,34,100,120,46,3,37,185,152,34,25,33,18,183,100, 52,32,152,215,169,59,4,218,65,159,46,42,67,78,7,248, 44,72,118,206,241,20,251,242,171,167,121,46,35,142,92,250, 22,240,37,68,225,217,76,17,148,32,110,55,94,111,163,248, 81,236,102,21,203,68,34,59,105,214,112,25,173,188,56,156, 231,34,35,150,200,128,229,10,207,69,47,89,146,98,243,237, 133,180,69,204,17,199,50,210,242,2,90,180,236,32,134,54, 205,124,100,119,49,71,215,196,192,193,59,142,143,26,34,185, 18,95,186,1,94,174,76,33,53,111,210,198,141,194,69,41, 114,24,241,87,236,4,110,214,154,121,203,110,52,83,38,67, 61,199,7,15,14,79,112,25,167,47,200,4,243,150,199,155, 0,169,65,13,180,30,193,62,80,95,121,96,169,101,205,233, 157,102,23,103,4,210,134,105,64,161,59,159,155,30,17,230, 155,50,43,40,149,231,180,17,129,48,12,15,243,133,229,180, 55,11,123,93,213,118,50,45,241,28,143,184,67,16,139,94, 64,141,83,162,210,73,81,134,4,221,122,113,40,63,98,244, 88,118,58,42,72,5,173,64,51,181,156,151,186,112,166,115, 85,191,92,168,225,71,59,140,110,72,10,106,219,180,184,49, 201,34,40,67,162,176,124,106,104,174,119,42,108,211,226,9, 2,129,73,79,63,83,22,58,108,213,176,6,237,46,155,54, 97,153,230,44,67,171,65,203,73,117,218,160,88,161,48,233, 102,220,195,184,49,92,229,44,231,149,187,18,100,155,22,53, 77,19,196,78,218,128,120,116,226,38,242,227,105,188,220,176, 142,53,93,253,168,88,203,252,45,235,99,251,162,179,122,180, 54,109,105,25,130,130,84,3,169,160,21,192,218,58,239,219, 128,131,187,251,90,87,60,15,166,71,76,25,14,237,10,77, 148,210,6,172,50,52,149,208,242,83,133,190,108,203,87,112, 150,214,174,69,111,246,146,132,46,8,73,221,218,214,224,161, 128,88,206,204,104,253,118,188,214,62,214,192,233,139,222,205, 88,216,60,139,185,172,186,49,81,2,44,37,111,108,218,215, 52,91,22,173,129,6,160,158,63,69,105,27,246,94,207,98, 47,78,252,70,236,133,105,49,223,176,186,75,158,176,227,213, 137,57,124,226,169,123,29,183,51,233,122,239,153,86,193,124, 91,125,151,120,188,58,41,104,13,120,223,192,209,196,123,99, 215,88,171,96,13,47,249,78,215,217,212,128,180,7,139,154, 119,41,110,201,122,193,26,89,172,118,76,250,190,174,32,145, 135,36,146,246,227,228,86,183,148,95,42,24,5,243,244,121, 246,120,46,185,191,198,21,220,183,244,223,89,232,5,211,96, 172,134,232,93,236,166,140,26,16,215,46,230,170,253,47,120, 125,171,15,164,64,176,74,0,0,0,0,73,69,78,68,174, 66,96,130, }; pappl-1.0.3/testsuite/label-sm.png000066400000000000000000000036141403603036100170620ustar00rootroot00000000000000PNG  IHDR00 1 gAMA abKGD̿+IDATX͘[$gVU=]=tuu&;cf';ӲC$:2A#D b| h"b u_Q4%Ƀ*sypw2mf'?&#X}+gNxd IIL"W< vN;^ԾMJbJTB?z<[  G!%9nHXo4 CRSt 3<z;)7IFbE7N㑈3?A~ז$Ex$";_|hiC>ArJ%ི Qx$"_;()0oIY-ƭ@Vp#"ϒV]y[* <)-mGC`lsN"c1#4k3D_R#@U>6M 8Ot"ᖍ'MgtttT4ͬK#3Ј^S$q$㏦'ISIݣNY߷z$;<[^&&qLVx cg`7u(_˔ɑ#Gq4{K)VXԩV1'j:$v-:>Ϲ Gƴ1|y%q 49$er1Iab8x<84EaчwSdWK4i`4N-: gCۑD )_]eUX jIl_<>t:ں-V문$k14:K\_fTOЦ:sI&%gלt~*Fr~ݤ]P-}e]Ddž^dE*TTZbs lԲ<~WJ{ & ֥7q͍NM6]Uו&5GJ)—E|ds퐬ҤEZ|Ї;e<'ɓ'w֧ziN1_4{< ddH=/% O,^IVwq5i*fg}o6\)wrȐ!CiLWDͩ1 _H (SD"E MǬ,)6,&&IPNDOj"ː]=~nzK +$b|p[pM!.t`10_mHZ\@mFz6N 1O2Q+}; b6,Qh$vA/ 3 we9۶v^1%ET1@8q7 _Kb:%.e'Dzu"B0=AIecFyɅ6S _8LaB%( z t9L t"mI]sEz58&d-`Qy ~'%+|_?$t,l 1Őᾫ&jx}vW@8eXIfMM*i00N6%tEXtdate:create2020-12-03T15:47:23+00:00o%tEXtdate:modify2020-12-03T15:47:23+00:002putEXtexif:ExifOffset26SetEXtexif:PixelXDimension48'8tEXtexif:PixelYDimension48KIENDB`pappl-1.0.3/testsuite/landscape-color.jpg000066400000000000000000006051761403603036100204430ustar00rootroot00000000000000JFIF,,ExifMM*V^(ifUUUU+8Photoshop 3.08BIM8BIM%ُ B~C  C +"  :SAvqo]Avqo]Avqo]Avqo]Avqo]Avqo]ɏe{}Mqq#qӷ˰_LWeSm7QW۠U{Q9gaGL)D ͖as6dsޡʇ1o6$n:vv lmr*tڊj'4,((!9۬.x̐{;yP&={}5čNc.!}2_-M[\E_nT[QUD慝2d'6[uْzo*dDzoX8یv/@+岩q˛ j*г@ln2@9PCWAk7;qܻ|U6n9su}QmEWvtMmɏe{}Mq Jp&^h;,Fn1ۗ`} s,G;FQh9srN[m_(J@pyc]HkfTeUV9R_>OEmo7:!/@욀2q*Ade%сT[QUD慝2sYK!+[[1Vln2@Bé#0W;xㄕ_N9l=}=m`$g=3MvXčNc.!}2`__?#J,wuA!v)<CIhWeSm7QW۠}|* *TGRQmEWvt~P5Z\Pπډ ; :dt̴Z7:ј>і 2+[[1Vln2@H 5PN}']|QmEWvtOoJ!G@zT,[wJD8[?d'6[uْ? }x>͍˺~5unY{Gfyڎ~Cc^q\m&P`D/4q#qӷ˰_L A>%.W]4^_zTeUVҋhR>jѨQmEWvt@ "Qԕ f&tqR-lqOl-O_fi{sޡʇ1o6B\(q 8یv/@2=n_ B۬ɷY&d78u8il lmr*tVu(_=*?)+;WH ^'M@x8#c}My*ډ ; :d4TuK4nG[Q[^/Z)Ŗ7 n)D ͖as6dWXkgEj; pu{yȷҌ74a^ T?˪Ur&+̰{;yP&={}5Rk64BA`7;qܻMCo8D?mx= v/+岩q˛ s cmFxEU}T[QUD慝2VFZvَjd'6[uْSngu6-=T>ɏe{}MqW@[P6{0$n:vv #^Y+ /~,2 "+Ȱ,4'~;0ހWeSm7QW۠ %{TǬo_i`ڊj'4,(wg׵oAߨhR-lb+WvÞލ7<6=A WvL{+ kn]Bd[*lډ ; :dJ&Nl 9$T>ɏe{}Mqq#qӷ˰_LWeSm7QW۠U{Q9gaGL)D ͖as6dsޡʇ1o6$n:vv lmr*tڊj'4,((!9۬.x̐{;yP&={}5čNc.!}2_-M[\E_nT[QUD慝2d'6[uْzo*dDzoX8یv/@+岩q˛ j*г@ln2@9PCWAk7;qܻ|U6n9su}QmEWvtMm2"jjBP) jBP) jBP) jBP) jBP) jBP) 7MS)mTF.X)hb.X)hb.X)hb.X)hb.X)hb.X)hb{ C*W1 k) f`k) f`k) f`k) f`k) f`k) f`YͲ['V6!B9KwiLY$R4mB=H-z5O7|/k66\N_=):K!B9KwiLY$R4mB=H-z5O7|/k66\N_=):K!B9KwiLY$R4mB=H-z5O7|/k66\N_=):K!B9KwiLY$R4mB=H-z5O7|/k66\N_=):K!B9KwiLY$R4mB=H-z5O7|/k66\N_=):K!B9KwiLY$R4mB=H-z5O7|/k66\N_=):K!B9KwiLY$R4mB=H-z5O7|/k66\N_=):K!BVڮ}e{v]ބŒE/ZᄊZpZpZpZpZpZv~sF+{{HSjFs1 Nf|ڎw^10@XZaokj ?/JM('/eK{<?SC,K͍=V~KG3w0A񾽈NQcէd~*)r2['V}(]8>ΙUQӠA+/D2&,)zF\i`F9ъ# "捽Vo⧡+սLdnɵOwߗOXZaokj A9\Ng=69R@59za %l<4=Ľs>|dT~u0$}㣗^jfww\ Hf>/yOP u\mr VIպ_} NT4S$bLFyMU>Y#x:U6REwzIj#I/'BWugBG= t$sqiԿ~͗RT@hڅooi*z+G,}.Ak|[zm^![پ[+ VX[vչatߥ{g?y tWC4y5ٵ}0V!\qb)%f˞ie&4sΔP}㣗^j5q Y*sj=R]*["?\x:V6|W|/؅ Gt?HK})ФUv]ބŒE/ZRJW^wh`sF+{{HSm%<;Xœ4.QzXըgD͚nuT/2FH-z53Pz\VP&ϾUyiBUyo}JT.DF)P yh{{Y =`۾eg] +CZXS}㣗^j~Ϟ'Z= 7Ewb6N_=):K!B9lfމa?:WL[֖9׬#:Nj\[HBb"@7ʳ~{٫RzoW≬捽Vo⧡bw~tM/"OVJ Df>^eCVX[vt{{Q(b;y^)=H :\ ?x=pU/o[ty);nV'f}؎bpƉ*8`:?롼K"zVX[v{?qڍ1`ށ,O7|/k66\Az::elYs =@ZM {U/o[tuÔ Sc??2SڛuA܄U-_1h-ݤWw1dK֠"FKw雍kvA?4xhڅooi*zilЭ:9⣑)bd: _SZM$H-z5 9l݀WmS"5Mؾ9% L>|SC,K͍=u4{Ρ-/pҼtK=QU}ޅm'<׫>5&}q@>)r2['V}(]8YwW'Mc٢&,)z +&tEGQUUР6[E_F4iCvtPde5m#OPZ EakՅGh00weBs@[,pO7|/k66\Az:|P懾X.{4h>ԼUe6t{f-*<(.v޼Pi@W+\{+վRunlBӀr+ИHPhڅooi*zXZaokj ob^llW+\{+վRunlBӀr+ИHPhڅooi*zXZaokj ob^llW+\{+վRunlBӀr+ИHPhڅooi*zXZaokj ob^llW+\{+վRunlBӀr+ИHPhڅooi*zXZaokj ob^llW+\{+վRunlBӀr+ИHPhڅooi*zXZaokj ob^llW+\{+վRunlBӀr+ИHPhڅooi*zXZaokj ob^llW+\{+վRunlBӀr+ИHPhڅooi*zXZaokj ob^llW+\{!o~G3^6T/(8b/(8b/(8b/(8b/(8b/(8cy n"gcYXpV5gcYXpV5gcYXpV5gcYXpV5gcYXpV5gcYXpV57HS]*$************************7%|i%o////////////~^ْYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYlYl=lzqW|ҋ :s.V[5MZ!bif"A_?_5E^r Ru.LezgΙY/Pe٪fI B2M,o]Ѐ+樴c5[9] Pj[nɑ̯LϿ{~0x|9f-&- _=<[~~{>HF=ܟ!bif"|~z~?zV!d+a Ɏd^/߁|{d1~ odNpnB畐|1_^=>x { 1Dz/ Ru.LezgΙY/Px5op6jf'6496'p O5S$2]kOԛ*~xꫡnaOߡYon `|Za_\ʹ;023TϦ[v ` C,&Ev#8~xCGSBm7ً-l-Gn+(~?eی]o BTZy1@z+KGzژЛBz@. =);1ܙ;AYy9{hm,Iu6TZy1@7Vv)ú O) 'tb \GQ;48;= Zk{'8K!yl;D-"g9F?Wyt7]- 3< ۫=iB֐Uue_m?K ~癘ol5-RW|=uQkXt-7frm^|w#T+5YjeJ*%=e# Hm&G23gL,k^fUB٪;L0jf'64Ϟx5:Qv}߼w7Cm\BΖt)RsPs.V[5MZ4߄> \l%JXh%VmÛ}j;uOIHtlN=<ך2M,o]Ѐ,MonQ8YO<فp)HU6Ѐ2}K[͇[HcWvBD-PWQiGl.ufv>  2ЛBz@. 3ަi{HZ_`>/nPM'=Giր^r&,u+.{<::P+<.ޅx x+O*^'OF%ft :!z׶CK&Vjx3c@+n7)Jd&\˳UivBVRk( Ͱxspo/|CfIZdkR7 C,&E-  ?*Лh@4K|p>UeSzZ  -< Z}y~]17ڄ΀,\GmxZiqi#ɭ.܅(,7C9hi^yt,/E+#aǥU (@ԶK#^3xxK0 ֽr[5BU_5#wV!(O5S$2]kOԛ*B\)GthVmÛ}>>WQ:_C(z O%Sp\48IՀRf&a7.@hm@i'}W!VB):X4 O&|ެ}=^0yVӡ~jO&:?օ_^F߅uLhM=s W]r CQLJ]o$xkuwք[5%ېg< 0+<.ޅVfv :N>:?Yj?MTkcbyAioAb%j[nɑ̯Lft :s.V[5MZ!bif"A_?_5E^r Ru.LezgΙY/Pe٪fI B2M,o]Ѐ+樴c5[9] Pj[nɑ̯LS; B &; bS =Д$qc`y!ǟ\xYm߸]v(uQ1ݰ[JI'+H5>2>spP d+}c`/ = ImO}W>kZ+y} Gyǀ`)۝bW[![|TJKh{1Z[0Z<Ϯ<@ O. ??a01cdc~1kw31]loLwl?ϡy~{Ϸ}}}~TJKh{?O@ǟnE?#pA֊FAlj}~m<ʼn#MdbCbB}q"7BX,ZNk`$(E2>spP{gbPbg: loHus6? &4[+Vo  d(-m Fpk4Ա !#}KvY`i逘O*TOt%% =Exc0ÏL-4z6oC7sa@H+ul2N/26+H5>sXYv{qQ_" 2<EQ/+]n@S; /ȖީbsE%\"ᎩC{fbNVQW u@e;r4^)8V!Dvl_5]rܖ('b':BG-TaUaUU^WaEze1p7Eod*{'Z:-r ^iS|-fwov]]5 uDm|}ٽ@+H5>uWk; {qQcO?_\V?&R\̸ \=?>2{ϡZ Gyǀe{{uDN;gJOP#%ӑEvZ91ɳt rwPƐV}nw]trBcs}Dbh\Cvz[B!꛴71n~F??Ҽ{§R]A]loLwl&7]t] is9ㆆ3=K<нR9hP|f}ىTzPR{)-Ia'QU ' l:Wai{TqYq{/Pxk֡V|b3%Gߠ kEo#.I(AO/_:W˔2C]e儱5 p}r2*Ou:;]~pi$ y!ǟ\xV]hbTYҢ.d\ d!5TmqiYm߸]v(`_s U{^hX4: LTݡ٧>Ŵs4@fLcٖ57"ZEuüreM`8 d+}c`/!= Pj&K=RO,t/TZҔWFs`B͊ra4S =Д$Gi: Y M0CQZi0mGpblt"n^y L 5k kEo#v>ah"IJW[yl=+-Gΰ,%#r,Z<,Ҁ'y$=ZsiVf(vn *tWnQsu7@eg }~uؠ k(D13SvfבIsU#1PnD?4 Gw5&ߘ= d(-"^(UNz"ycz$rzhAocnAn_@TJKh{yrj 2 M3ަ ֱa.׼Q?YS2 Tqc`"}Yj=uya%ȏkz6&c'An<9f?8 `!hC> l[h TV QUq*M."Zy/I`)۝bffnVE uMݛ\UBd \7:9'W#9zW,R-5۩+Bؾ(zPDb':BG-<i -b,x*TOt%% =D d5^iS|?>uJWܠ>ߝFMYۨwD=Sz=nlv>@ kEo# 7mE{qQU\:y XR}K=u^$8C1?h*v kq enV.Oueg }~uؠ k(D13SvfT£ suB@j$QWCYW5 ӺaNܡ)݀W[![|$Q+ʡRO,t/TZYak1-ᓘ*{'"x^h\ 4Lo>h{ICB䄳Y ͋ O Ķ4u)AsCm;2 } ִV0 vޤ[y8=-Gΰ, dq /Qf-6}qڊ+=$87s? DjH!_|:V ݨeg }~uؠ k(D13Svfݴ?;1r}ֶV7'JMhֿ|b_5U/~z  d(-"^(UNz"ycz$rUyʱ4UɷAo JI'!x,+0=`ϑ?6n45h۽}^A֊FAnԋo'e}hUCڜ}jQ<'.Z<Ϯ<@}fp*y0ƉV}nw]Y!Q۶o@ɬ2`Z`_Ls=͸zc +BؾIh*,HT2G;[_ WͦZd儲pR{)-IHᬏd#g3hv85׏|J쨄$^A֊FA KERǺ 2>spP d+}c`/ = ImO}W>kZ+y} Gyǀ`)۝bW[![|TJKh{1Z[0Z<Ϯ<@ O. d(-Op$BR[@`A֊FAB}q"eg }~uؠV!Dvl_*{'2 } ִV0 $8+>S; B &; bS =Д$qc`y!ǟ\xYm߸]v(uQ1ݰ[JI'+H5>ZKm%ۢ[nm! c `֡Q=,NPq&[uuxyZ^LR4qg)H"Fч/C\㈿I{=n}Km,S/kw!p,z2_0}LJX{T_5{ZDC!5TBba9"׺xߚz+K{Xo؋q.?ڑַh r*~?!'p^SQɐ|p1*?O~G9=Bzj$a&p=g=>IE'#·{ZݢYm6O&Nա~K~?_rJ|.ED.8aY]|*kxC3RYV5{ZDߣ/o^Qv˼OL"lS3.D^YC[Kt+8 %]>|ʉQ\ÿ1}yJ;bUp1*?M/vD[5{ZD¸ߚz+K{ &֎$E#PEJi:#[jם.߻~U\ÿܫSbʼn( $xHhI\p1*?LOcuQu7=Bz\p=g=tJP {:LַhM'Q%\YiD+gaߏQbÁ ʹyK7]+%VpsG[gG 2J96 -va>w ) QkP"W;OYizXJF­]hD'{ZݢN+VIВrއj-׽7MsoE(P\p1*?R!Bt)|2"#qDP)_kP"W;OYizT#*&[M]H"ŋ/{ZݢM5chZ>kvlQH:V)m1.U+zo[:kw1GQ(c~?XF^-c!1o*XY+ Hr  >לѸO5tzxXTO_|+A(w'{ZݢM(T#c ̔EB =,H;%/t0KqĪh,_܋';YeqQцQQB¡nXW]6 {ZJܜD]k7'[!MbEH֤R'74B¡nXW]6 {ZJܜD]k7'[!MbEH֤R'74B¡nXW]6 {ZJܜD]k7'[!MbEH֤R'74B¡nXW]6 {ZJ-؆Ղ/brf)98kzqڙvn._ w9emd atu|5w,Fe_Ow=A-;RM@dn7zEr@4&/YeڄJr?᮵ @8n)\Sʪ]Co]r27^ʝĄ♍8E.߹~) 1OkC.yv?NWU.ŋ<^%SϞa-Zts*@"K<y㈑!‡Ț7(\y%$[Il %$[IlB a#!+-n^; Ŭq]J۷.z"B0O -U7^!-EDS& h!vā_ t#5P1ՇFQh -H o)ZĊ=HzKʗQ"D'f@h{No"hxߴV+qcr_M]k='Ϲ/]Zv?%Ϲkx : fWSqp\XL8NZEs:ou$6mx]zNTuԴn.dָлxRKTZH"!no"hx߲u!./nڰeYg᮵L}[v nN ,8=֡/ŀJ۰}7^C 5},q -#fJzXW]7mE<5(ǁROGl X2x)bIo83"kR)^!͹6آ(Hvxn(*XI&8"IJ&8äQ]k=7=OCrEI̎vϥ_@ud O)|B.3 Qޗ|[1-b_ t" )͵EP8_v-,_ш8m{ {ZJLa #9^؟mބC&o8^Ț7(Dј O%DR5ֽB¡zNdUm۶-: AqF!YC>$TjE+_ta:x[Ҹ>Ț7"XVy_pZJlc}Z7)~3:q:7uhSϗS+|B Xo&U6"]$9tKk1ۿs{^/?/}X7~o*@"x˗a]+a0/XȚ7^eۮKVJ'᮵V2[ U:8D Z:=Y(o!aPEbzZ+ą1pHSXJH"]c ^gN8sXEH֤Rlv<+`G=.lg;syCg/GJ^נzO#Y.^})>Y1!܋qēԸYmZk:<|B +-WZN+!Mb #z1(8v"kR)^ԎAźueaR5P n\IMkd}w u@o}\>Ӷ[ޓXT/it~,KzxW]748_X'Tv/7p./_)o"kR)^*%}nqb:z1 ,9}7742!_ tކ 8.ppCO~K b1" EH֤R%T ljGN9 䴁v-Yrbč"ZX&nb Ҿ۞ÆנzO;)`uN? NqK {ZJ+--5Qj`/syCG 7 j:ii= u@a+8L=o^@%7^imou|5z@rR6? {ZJ 8S_I&?ҳ*>ˆנzkh$ ( uN2PEXT/M\LBTV"ou|5z@Ed#{1$vl$TjE+PKOU3.(6m×UIJo%[zo"hxߩ ~<$4e]k=Kn߰A1 E e/ :Ik`^ tTOd8n1,e8+l:ZDElgu|5znzCf?Y3*ѕ7.}m5- vKĊ=HnNo"hx᮵XT-kܱ"kR)[Ț78kz r:,HԊV&N^<|Bܱm*@"9{נnO!aP,CᮛrĊ=HnNo"hx᮵XT-kܱ"kR)[Ț78kz r:,HԊV&N^<|Bܱm*@"9{נnO!aP,CᮛrĊ=HnNo"hx᮵XT-kܱ"kR)[Ț78kz r:,HԊV&N^<|Bܱm*@"9{נnO!aP,CᮛrĊ=HnNo"hx᮵XT-kܱ"kR)[Ț78kz r:,HԊV&N^<|Bܱm*@ݶ5qƿ3L!383L!383L!383L!383L!383L!383L!383L!383L!383L!383L!383L!383E[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[lsϖ>[M9|6L `\&0. pL `\&0. pL `\&0. pL `\&0. pL `\&0. pL `\&0. pL `\;x֧۱HI'{hI'{hI'{hI'{hI'{hI'{hI'{hI'{hI'{hI'{hI'{hI'{hI'EK\E(D%Q."qK\E(D%Q."qK\E(D%Q."qK\E(D%Q."qK\E(D%Q."qK\E(D%Q."qK\E pAQNN%*ODrWj]3rqW]bOܖ%>-ȑnJ0=KnN*r,UDŹ?_t^nQw%U?rZ#"G+.8.VJKUsH7%֥7'yEܖ*TjS܎|Ժf7(Y*-TJ|[ϑ#ܕ`{ZLܜUrX%S%Or9$kR雓ܢKd~Q)nG>DrWj]3rqW]bOܖ%>-ȑnJ0=KnN*r,UDŹ?_tG4Xf avv˶f a,,wUl/oć ehbj% 8~KU?ѻ=2FXȿ ,>dec%XrZ/\AQ~wUtrgL4.ҦY82g ]sH=#ij(he #ó*he 42PCC(he o\Ժg|ɒ:z5PQ&Y8_ǻ*r߽z˫z.% vx(1ot6hǕ-}ՊU?k/G.\s&zSϪKkXE6Γd D]PM:Q)z 4dC Ǣ1C ;>Di0,K|k>ù^UWj]3A ~vRVky^nQ{!ieVC#EmYb۷މ{"4B,UŌeI/Ca8BeH@쫣Z(eqjS&i@7ʡXs ._?8q"ia8])*i]֥;Wy4+)`x[,1nwUQ"8,C;l+lX%S  @/Eסj]]M UӉŪOn) Ir8ǢakSBrtȑwS,k3ntotWi]֥;t?.މ{]1"Cqkܢ]Ѣljtr8&5lD; #ܼg6?X%S&/K}f2sM@Yz$Z^;iT4\86N-TJ|]ƃ bYrXǢܪ.\sӟ"GmqPӐJubotaWWÜn'#~pNXq_tۭqJ' 9!, :21Î4)+Z=qW^ۑbȼaB"7(/őhjeO6 'p{m{~=U?iTvۗx^vԷD58ꆺ;o/2}ŪORk0\ ׮ܺtq=`3 (i",Xpa3tN_?!h q tqè{,`{ZLϮAo@-\X5kooO#$E(sqky^nQ{nT>eLK\|őǽe޶߲mbO1PA drp/(# ,㉺^h,EZl\fsMO,8Ln㰂beƛӟ"GooU$"D/ott?kRǻ_m4. M0;\X5ȷ'꠹x mWwUBCJJ@OFv)|PfջdqV!^q_0q7K۲mҏ7#ŪO>U}Ę&v ӎ1M[\jYeTtS+jPn9|ssH;UɄ]/3+ 8otD?2ҳ:Y`Ju֥;A3 ː X5`spmx{7(lB[ő:UP7Ŗ,U tm(.b\@ÄXa=..״8W;Utgo?q=Ry*[e\1-!Dϑ#6j<>gg|k a,Ŷ_m7LwU`{ZLKŁ]?ky^nQ}#Et/eqLOre*TC /C5qEДiaA  g MusC x|ssH= 2^8]H5t+_.s9f[m: wq:9{Ժg^,9_ǻ*r=Y.$QE1۷n.X%S 2/q:.qv\J\TJ|^k8 ?5x\#~sNXR9G8:R!"GaYmmi,^\Bݵ֥=`xiڿkR鞃xgmbO0StiRLZ q=l;>D/%㉾53{GԺg^,9_ǻ*r=Y.uUjxn †4l.g{U8Ǣrtȑzeq7ƺ!pCs:Wj]3sZ֫l$EH,0V?Z֫F'9+7(⹙%TI*IU ԓ $Q$%TI*IUJUD$Q$%T7P$GU=IJ?dJsy0 bn[.Po \V g`Tc'bw%>/ANkGCh?in7h~m͠m=ӟ"G3 7j7FS#-y]~* cj8k9[֋\3խ6*bjsQ{0=KnN*r,UDŹ?_t^nQw%U?rZ#"G+.8.VJKUsH7%֥7'yEܖ*TjS܎|Ժf7(Y*-TJ|[ϑ#ܕ`{ZLܜUrX%S%Or9$kR雓ܢKd~Q)nG>DrWj]3rqW]bOܖ%>-ȑnJ0=KnN*r,UDŹ?_t^nQw%U?rZ#"G+.8.VJKUsH7%֥7'yEܖ*TjS܎|Ժf7(Y*-TJ|[ϑ#ܕ`{ZLܜUrX%S%Or9$kR雓ܢKd~Q)nG>DrWyOg$S۠On=۠On=۠On=۠On=۠On=۠On=۠On=۠On=۠On=۠On=۠On=۠On=۠OndSf 8U@Ը&5. pMKj\RԸ&5. pMKj\RԸ&5. pMKj\RԸ&5. pMKj\RԸ&5. pMKj\RԸ&5. pMKj\RԸ#Fb,%C!ΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔG:QDsΔDh D%31f03fc a31f03fc a31f03fc a31f03fc a31f03fc a31f03fc a31f0~K`s'189Nbps'189Nbps'189Nbps'189Nbps'189Nbps'189Nbpsŏh=J.@Dr#G "9@Dr#G "9@Dr#G "9@Dr#G "9@Dr#G "9@Dr#G "9@Dr ;L7#+ ]0JWL`%t+ ]0JWL`%t+ ]0JWL`%t+ ]0JWL`%t+ ]0JWL`%t+ ]0JWL`%t+ ]0v"&KIZNp%-' i8KIZNp%-' i8KIZNp%-' i8KIZNp%-' i8KIZNp%-' i8KIZNp%-' i8KIZN ۷ހ]1e5E  xzE _G}Y*g RXHe xpFc֖r3a+䝄55pNRSp׻tnخ'CKIЭS4]J-/UpY [zjQ"Cp5oD(^$1Ơ_ez8r;1+K\sqN"wYz6b"U$>Y yP{^ -6M"]pacif띜ԏuG@Af7zQV9+m{絥tCfP[ ?ƾv wP]FFCKIЭS4]J-/,87Wbj1C/ IN2NCF"vz,Z9͖dJ$a@}Q]b]wr&b*c7) J)~24Tz0чc=۷n]?{Z[OmM^ Q0K:[E UR"8k:WuMXmç(Eii:}ƔmRK>\%3 7Z/Kv Ų#}N!ą{|u!ل&=8c!X@EgMou!r5Y##fRͤ0-4h*(_k<.,[ړb7qɬa#T( tVfW5x~ŢژJ<ЌAiS\0QϷ]bha$:g c{Z[Ojɦ|T­xGzۗ]Q3:Wmur=8Ç~5DYKI\;!źK6T"k1J޶o[-i7'qj',M!t/}9v}kV?iSvۗx^v458ÂߑO ^*Kb7lYJ#bjmɬ{{btN6gkqۖXZ|q v;_i-L-/L֖خ d/8ë>_IОRС܃!+M??Cvג3пJ9% muZZNh,qRK&Q~w(Q#IDH:mū -n-=~S0w8 ,kX+쇆6"a7+0l<ZJ81$”BD KIS9aVmJ'=T"Y?=0t0j."6-ęb<!uIV:-a/[)͐EqZ^5 \F%B5W ت~_4F2̞1qrwt4Ro߿ /$(x㉫>im?/$Ca8zfVۥD_gZJ--'aw.K!,ꇠREWu[,JRv;kV?]OJ |Z|$JwS+d2IIq&K6_{~bQ?WG(^p͑ 7qK^&:]k!ČODm58ⳃǵ f:# v-˶<\p;u|ZZN -B_KI:.-_a:=,[C uZJܕ*H~ާNMf{Z[O֡ҷ%J-/r_Y֖GtRKܗ~nj+rT"%z95im>ZJܕ*H~ާNMf{Z[O֡ҷ%J-/r_Y֖GtRKܗ~nj+rT"%z95im>ZJܕ*H~ާNMf{Z[O֡ҷ%J-/r_Y֖GtRKܗ~nj+rT"%z95im>ZJܕ*H~ާNMf{Z[O֡ҷ%J-/r_Y֖GtRKܗ~nC1111111111111111111111111%q&Zm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&bm6&`kLG*0PhM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&hM&W$șLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșLșL篟ݸ r>N !015@QSq 3AR"246BTar#c$%bCd?t4a|< P$?z{T=ǽć_Cj!C{ڡH~=P$?z{T=ǽć_Cj!C{ڡH~=P$?z{T=ǽć_Cj!C{ڡH~=P$?s>=q=|3T\pfj+ Rq\83wKnr.Wڥ7{T\pfj+ Rq\83wKnr.Wڥ7{T\pfj+ Rq\83wKnr.Wڥ7{T\pfdgúnm|fqyhm|fqyhm|fqyhm|fqyhm|fqyhm|fqyhmq.(Oi8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8[ɣ8YT&t*Ԋ_vv^2~'tU?/vke7]t}=}N_R*=}ӭxn{W Uz[w= A;A}Hk_Ne){wE_Wa־nSuO@7G"ݭ}:ݗ뾟茶0o ~ f{!9XTm Ϋ[HkXޟ@'X\xY+ c^BQGȪ9k:ݗ벜PyY(6=Fkm0L-0L-Ɇ A;AbKB;љD>A7Pd Ta@Q=R(W8(0 /&K_vW73!r`eV:g`-%{0sbu/Me1YʼnӲt ? ^X'tU?,TOCf:!*LaPQctgLkCD:.wec*Ō ECAEn_v]ICäy4 qb,C`*#`ϔ1wAAJ;,d JD ԄܕwXnSu#q@vJp#8ғN]؟cITɣ8X{7Gt5pmP  4Ǘ=:/|;igdGƱHkYM>taƾz'[v5) '5h@*>*6iI]'OP߮Q)CBݙwE_WVEږ?Ȣe ' &ⓠ{ME]ZÀ1șmhU1w.~c]T08I)udl5e7]TEwfrZ lP-=t˂t 4)$bz-abD A;AdYb&AQA.WYBy3U>-XUztQ=1`t6Z/^L hcu/MbEq:,Ne)+T'5h  €JwL:+(w:dJ D^JFQBqX թCMl,O|`t*%) '8DGAvP~r/~_.\&Ivk5/H *'[vϪ ]5 cb{W Q&_C8VH!JNw<,ւyaj?ZO֑4EJ~YE]ZylۢtNw7J( ag&;?ӐL?:(Kuҡ,Qu2v^2²%PCӟ3HE-:uZPWb,T ñ01?]؞ E+1"\o(&ljQA04ܒwE_~PN-vE]Z(\IQPz( &d| 0rζ!-xn,w'=t=Cc~?ws=wE_Ĥc;qm7b` V й4qQ (j)vk8N #2![Co4sR[dY&du/MeU"f#,Sfݐ gTǑA61)mwE_Wa־nSuO@7G"ݭ}:ݗ뾟茶0o ~ E]Zu/M}?]O|`t*Ԋ_vv^2~'tU?/vke7]t}=}N_R*=}ӭxn{W Uz[w= . o)yHDR&07L "`o)yHDR&07L "`o)yHDR&07L "`o)>Jfygj~ygj~ygj~ygj~ygj~ygj~ygj~ygj~ygj~ygj~ygj~ygj~yghܰ,1u` UgV5[lGsUgV5[lGsUgV5[lGsUgV5[lGsUgV5[lGsUgV5[lGsUgI9&p4Ǜ/_ Uךku橣5ZyhMV^j?SUךku橣5ZyhMV^j?SUךku橣5ZyhMV^j?SUךku橣5ZyhMV^j?SUךku橣5ZyhMV^j?SUךku橣5ZyhMV^j?SUךR0f\fkqşF֫[,6Zqgѵ>VYmjϣkU}Zn8kqşF֫[,6Zqgѵ>VYmjϣkU}Zn8kqşF֫[,6Zqgѵ>VYmjϣkU}Zn8kqşFփEG±N_RS7ZCTocöDo]k'hN_RS7ZCTocöDo]k'hN_RS7ZCTocöDo]k'hN_RS7ZCTocöDo]k'hN_RS7ZCTocöDo]k'hN_RS7ZCTocöDo]k'hN_RS7ZCTocöDo]k'hNYJ7p9eMn`o[9f5Mn`o[9D_HXrdt"a "_ş!.zb\5X0=[#.2 +#b ٧e:f(Yd\ְR(~Uȏ~"˻(*@~u f+:x9[f^@ =d P; u!-5/UO/`aPoW&^Q:塔5F] rp(t|#ق8N +n3<&77r` !5X0=[ ]¸I (t8{0SB HfrZK.BX$zH& [7 mI2 G;(ZT^hvӓO%vCprA0)$E?I|ZWIE/>>áw-'dwfPD1" !c^^, EA6W^immMkoךmZ#t+rWZCTlT$UGstRagb~Y{!DM0 4)ĝ9a!iQWxKFb>;dOVB]=eLlTNhe QP PƜk& +0Ԅ36~]l9lRهlءxGdP9g Gw\)l5*ʾynzg5*0F`آ!urfOpc&FtYWEeͺ0C~Em),;e+BMuS"@TXGs1GCZ'h,% 'P \ H5Yzy{=`h;;.^\O2e992Xrb{&"\8:}@d>G9UIwDp/,}vȞqCJnM4sŸ>g* [Y'tʊ!(Z T`?>E.BXhEr~Em)u.nCN,X~7 'hNXIn,Ӎ(jahlRS6I';ZzT\wtI^P4ú,}vȞ !j @ʅsJ1@y hnkzgE.BXhEr~Em'u5 mvv\UIhBv~v:#vY#C[ R]&ksOW/|S2rf"a܃i!r?|"%ܢ!a"z(,wyNP0Q]8mgjF&lZNЙ4 ZV)@`ic) s2(|lQc\{)Qz}N{">ru96tRT2Yϐ5گ2򴙡3ٗ)! rO8ʵf4~.G5M,9<*\g|;AbIA x)· XrS69).۱jCv;zNhe QHYg+F8vO tQA݌.Xa6)cöDlT)h%$r@p4~>x r|qc\{)Qz}NJEao`p XA]ӆM4H**iCSPwaóCZ'h,`31VG d^^~ h Ps-ZCTlnGSnɝ LcɘWsIS?nEY[M,cc>;dOV_&-ba0VQCZ@DeQ&Qq[8 aK8^]m(U69'hi_mѭZ8DuS»J?&t]0Se 2_&(2^Q?jZP; Yzy{uC(j,}vȞ8 }P; Yzy{uC(j,}vȞ8 }P; Yzy{uC(j,}vȞ8 }P; Yzy{uC(j,}vȞ8 }P; Yzy{uC(j,}vȞ8 }P; Yzy{uC(j,}vȞ8 }P; Yzy{uC(j,}vȞ8 }P;Bm E ğ1vYᘓ.֫<3|gbOZIkU>bj1']Vxf$ ğ1vYᘓ.֫<3|gbOZIkU>bj1']Vxf$ ğ1vYᘓ.֫<3|gbOZIkU>bj1']Vxf$ ğ1vbP:')P]T(n skU B1vP]T(n skU B1vP]T(n skU B1vP]T(n skU B1vP]T(n skU B1vP]T(n skU B1vP]w*URNQ;v#A[pF؍o-ñ [b4h+ynV;v#A[pF؍o-ñ [b4h+ynV;v#A[pF؍o-ñ [b4h+ynV;c"wrkV$3x7,HfnX ܱ!bC07rĆ`o $3x7,HfnX ܱ!bC07rĆ`o $3x7,HfnX ܱ!bC07rĆ`o $3;-npr6]ez W|Ysޫ,coU\.`wŗ0lm˘66]ez W|Ysޫ,coU\.`wŗ0lm˘66]ez W|Ysޫ,coU\.`wŗ0lm˘66]e/&;e7e-;۫}Gz=W~@꾠 QÈvn[\w_V\z:I<}AݗԶnu_Iy{-G!)/mq~[=s꾓$Uo0ZCSv_Rz}'I O`8l쾥uuoGOԓWݾ8j8qM}KkQ랏GU':7}>qpRmxXCc&` mq~[Fx#`a`dODMYX s"lX#=n8LƬP`<1LJ._I1aOԓV)X ~ ;NӴ70ZCSvX$dyv>j\ =@X=wu,R=}f8CzTw.æxz\?jTm~Kk$^ 69GfhD$rҖWMlϞipru> ^{tG̹+ҚLRaA?^z`Bz4ĵԝNtuRAPB򳄖E:áH S{"LZ53\4I<`Å L:!/ΔJpe@Yw(5Cq1x0Lxo`8ljya19 9pE&P"a09guuӓ2Q*w ܣ %d{%-9ٹGN6 mq~^yw;@L&,{)BO6|DDk ?Đu'\g&fI16N"0DmeǮz=\84:Vz@ tuSE|a)>d+?&4槧ҕ%wQ2cOԓ\7g1=sZb& o`\ƜJp X-&Ɯ;8)&s7g7Sӛ QÈvn ^ߢ#h VDuHWx6J])D"R g-qFQGn H:&Q|,ɀҔy+? m4}Q53)etNOUN'kDbkW4H؂35nz„W(ɢqdK9hC©lDiJo\7GU41}'76=*L@I ]A1wCI3҉[,͑@ ]8RL,O`8lEPmC ZEЗ_TzNEKaA(4qewk E j J6 mq~^xAh~]܌T⢃8HlG'hҖWMl)#3K>tLL\TSezp"| PsgJSz:7H  I06˥R`8(!JRƊ`t/ ?RO up}r0,Ņ'0i3ۂt4VÇxkͺb DxP.Ti 8{0N70ZCSvXdmu JYg| zOhRov [\w_V#nb9;iK+^ 6!8@ I8 opRxp= 2Ҕ޸n4׵b+[{qUnU iyŅ'0i;,Ӵ3Jp#RqtJ>DiMMpo ݖr]~RwY7_^(]`հۘNR魗ʠ!8I6=W0#~wGA9Gw^K|8G&L=t7yH1eS` mIȋ))':.qp#nK^R=}fKW{B}ElsJY]5RrsQRNai3&Rl{JUA/@*yFqYd&q-S'y3j?sɜKF!*9..za^^R JUx"g,$9P6vVO=`1htPLxf-y˝L<$Iqpվ=?RO u_Pov|;e7e-;۫}Gz=W~@꾠 QÈvn[\w_V\z:I<}AݗԶnu_Iy{-G!)/mq~[=s꾓$Uo0ZCSv_Rz}'I O`8l쾥uuoGOԓWݾ8dq_5D}8'j;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lj;|*_lhd̄(9j՚jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱ!-jbѱR1XsL&U5D1i:o Q7ZΆ-?CTM&O|ŧjbt5D1i:o Q7ZΆ-?CTM&O|ŧjbt5D1i:o Q7ZΆ-?CTM&O|ŧjbt5D1i:#_ HtIyQ<&~O35Fx 5LQgjD?sTj'Q<&~O35Fx 5LQgjD?sTj'Q<&~O35Fx 5LQgjD?sTj'Q<r/v?\ 70~sx70~sx70~sx70~sx70~sx70~sx0qi}P/uzm`_/9T־y}MN7Q| Ck"yʦ/n[Cjt_T1`gv^X/KU5{vrrS_ C;}Hj^r}K۰ nX/ݗ ERMk^݇܇辨cu:}Pd6_R/ڗk_R "^ī"")\.zr#S'7Q| Q+ݡmnhu x6JBܔ2v*.z?Wg$GC;`ұah>m(e+Io 5)P%k 9a,Q%Pmh5*"lX3SJ'nrf*\/P7*&lȿj^r`ah>m(;Qo 5?iPk 9g-7unU?D ;ʃ6Lb۴`u4 :way}MNvU'I9ú5poTK SqH.y)8Öx6Hs?&3C@cn矇CA"nya 3ʋTTg!nJ;Ftvpj0;T)ڥL/`.v]-ăJHElr~ CkY ' ݼt/坟_yTV\Әi%k 9Y3Ae` R q reP&q8cQ4$g7I ERMnB0|t/坟yTV\ۣ-:TaDR2Z(N= wm@rCNZ(iǦLҞ3xBtR49az:۰)N_R@{6kƋTNBZD# g]c*nVb^VssM4µ7Q|)j|IY Rdٶ|X$QT)H[NKbjӰ)H/D 4pid6pc'+?_0 c[i/$bR G iP%k 8ۨF0 R<ΏiVHg(URs {*k.KF0qgWs,R% إ&Hj^r•Ϫ(XyS km%dU]A1ٹh^pWpRҡmKTHyˈr)PW{E٭i  KH9\bK;ўp5Z$" Aq% a—a-!5:8g( 7%ѡVQD)DrPz3ґBEԴPh8ŭ[}/ᥜ" <$<:EpT6ux .쟕ݓDA0Kv_(u) rP<*$.Yd (r5\ T-}JnMɹ T,~*1rfDo1u+A0f Ԟ `uy}MN g4ި@,3HRг5u-RRDp?'+cu:aWY|JBܔ2vTFAG^LiaZMB7nA@(nCC;aeW T2}MdgLPiRЩOr^NFa;XG+9$*(_/9TְF-*Ta۪AE@P00BʺiݎL-((O<㞴RI"n9oaڹF40zxjزiܔ+1 ɭ4]NZ PEwnppcu:aWY|JBܔ2vq W2 b[; `Npj-Pd6XhvUuB 2_v1qii*ԉ2!۔Xv* Ćo*^ #&x6ґXZtJk ERMk nnRmKrPt(rC7ģ'H F!r8!q"#ݚu0wr3y}MN g4ި@*ݹ^\0ݴAǛJNg$T1`˫^!nJ;GGu2 qGy\'l3C܈NBk Cke_T)P%k )t(^\giPPOp5:E(2nA0(ҡٕ%3 ERMk nnRmK1v,%t |(RLalk^Gy\0ggd eH&K۰ϭiAQ,ItAB hYȪdl546V;O!*ݒ/tfUSs[E(p!m;" TÓCA R䡓xWN$p ݛsRLvߎX2vݗ .ʿnRKR-_ }+ӅPM+y3?7bX_5ʏ1d avCp_/9TְF-*TaP5]ɦm¦Ɖ?&qۡ~-,Hn9vR(15BV.^Q9{vrrSr(7%)8K ,F!' ğ.w `/u.{(dodA#,*[0&4nܡo/-we!D˲۪eq*(gBΐ׭pd5zPO {"yʦ7ioT)Pk :1B}ng%v3;;&`A1Bpy}MN ėtP⡀goAgt 21gedS&bH=8]Zl1`Ĥ =s<*7ͪa 9M薜Z8hy wX9k۱Pd6Xc /++6vqwd=Ed$-wJ%0 ̭GTc%A 0Й siN8FERMk f=<炳T7h:.uwCU`xTMZimQg\y;nZ(q ;l2/F|^_:0taz5|j!чCѫFW>/F|^_:0taz5|j!чCѫFW>/F|^_:0taz5|j!ч?RЦ&\7TIa T(3]R]S;<딛pRӁ%3]RУuJ?uLһ`Ro%uKNuK+Bs5(3JnI -8XC5, =T@;WT+1&\7TIa T(3]R]S;<딛pRӁ%3]RУuJ?uLһ`Ro%uKNuK+Bs5(3JnI # %ۄoqs2J-8XC5UFEGǔE?کY%GʒFPBG6!g$D 69GwtNTADTl[-QnFź)"I &3һ`Ro%b}iaC"|9iRnK(^j<\LE R(=$XP>Aa CbVk,)}m˯T}jV @;V)%Ov#%IXwi]y)7A"=YUJ@83Ĥ(m&Z,5PrN ` Ia )%:A Z))~ r//W0gYxLESҩ~PoYZ{>N A!vͫ@(K:_Z4(dSF@6a9WHJjav ^nl` ;,qgvry.L 4ATXh3Δb ,!#`q-ҡ~PC+(3X]NI "vH{P.F@;Pީԭ٤9GwtSJ*crstW]SCfݥv7\Kʊ&PB$&LcgǏjKNc}BCAqu3LEsLPq#E$F0YZ{s8>ȟoX>#|9{sag2ʍC"D#<)˵[H,rX"Z(eIP>C3V BFdC4SvO|ݻ+1&\6BDhBa`iND'mZp$k}K9t1 9ehQfH61'wW4/S(]ޱρUC}jV @;V)''V#Dj( pcQF uys˲k&d %ii'F;.Lһ`Ro%c)DF4"9lӢg$sY%Kf,bjX% ,,SiѡR>#I:YBʂ7 , =7CQOtM&Wtzţbc|c3CI v~Iu@)@ygJrppz1R2(P(QJ?c W:f49f2H4TOty@wtjv)WlcuMl@vdO4fHeKfH"PTgK<6)ehQf+d ن(a;J px GdPLһ`Ro%b0ݜ%) Ac2zL~fOV(Gb ,!Y,R 8vhcg9 l䛺@A((3X]AR05pb܅_rSW-}]M&%E$VRDyT]U  2&fIEv(x,RXĠRm)sx0yRcqp Q|T _;cAܮŠP4_fcDC-gvry.!vRz.Ia (+j;'Hj43lhmGezi4M#bVk,a/JݚAcHwJ2FœvlXNiy52 6cݥv7\KG^9W{{x緽^9Zp$k1FF#)_T{vK7Pǖv=DN#dVk+҈ 4 V1;~xLRYQQ +jLQ,4˸P#Y& ӇyP  M>> DG3JnI -8XC5, =T@;WT+1&\7TIa T(3]R]S;<딛pRӁ%3]RУuJ?uLһ`Ro%uKNuK+Bs5(3JnI -8XC5, =T@;WT+1&\7TIa T(3]R]S;<딛pRӁ%3]RУuJ?uLһ`QHe#Mp&5x/Gɭz>Mm^ kj8^[W|ڼp&5x/Gɭz>Mm^ kj8^[W|ڼp&5x/Gɭz>Mm^ kj8^[W|ڼp&GwGdV 'Qjï&]m\u؋urדb.\:lEˇ^Mڹpɱ[W.y6"jï&]m\u؋urדb.\:lEˇ^Mڹpɱ[W.y6"jï&]m\u؋urדb.\:lEˇ^Mڹpɱ[W.y6"jï&]m;uDExEu "ju]Coڮ]mWP.mq[U68xEu "ju]Coڮ]mWP.mq[U68xEu "ju]Coڮ]mWP.mq[U68xE>gR,bQ(ox8r{7q 7o9y @ox8r{7q 7o9y @ox8r{7q 7o9y @ox8r"DVD* gKq{\@t7/% gKq{\@t7/% gKq{\@t7/% gKq{\@t7/% gKq{\@t7/% gKq{\@t7/% gKq&H c]/CwDnij4nZ05 Ǽ+_ZnRջhF(`.ku͏yw>WTwѺjP\T'|}j#uKKV٣uԡO%6=]R;wtFꖖ߳F5Cs]PKl{˾v--[fkR溡<\|#uKPZZ~֥ uBx9..G}_mLl,&-R^"D `P J"4{6!ov<͈[ݏ7bc؅{6!ov<͈YgES *C--[f i0h,;0֥ dMc|45>ై 4 Ǽ+1CS˜}\pdrvODlr;wtF~S'--[fU`A 7W0L~QrҒCra@ܜC,F(`.k=!!3w"@EG'crX|RJO$T>釩SA2P(n3.Ɋ˚Ii]u#l~[=M]ewUChyB]_Zna kpZnp&05t9ɠ1 @(5(l|v)ij4lS8r(" ST#{2R SQ044G=B(uΖn`@kR@])PJRmed)L]9Jf}v*v(O%6T,Sc]e0E^D6sܨeb=;L:"@tT Wb~+H7؜ SmS)6zջhؠn9LYc(qP3 }CprH@58hcKJ FtsU9ѵ'3|S*baZK ks q̢=)(yQm8c MNHS&aKǫ/CwDlr* )v3<_NG_p0$n6)< $rfa -(&w-12aj:/?7rNCPI7w|kR;:SRqyrx 4 'A:n>&Bx9,#86TlSc] Y(ei5+bi$4 ^0{ o/r!ME]^lcKP fq:iяhW2lpe߳F$GM'[;=&DD6J_hV2ʎ؍jP\)?;6DF{{:ǧ=apMyw>V4:fh3Ң(MhXf&˸R vI}j#c,Piu5_ߴ(eT&,n678D t@p 3D~gcX( kՌa63Z05Ț~@cJh5pPj).G֡ۻ7Tn7QJ r]sc]/CwDnij4nZ05 Ǽ+_ZnRջhF(`.ku͏yw>WTwѺjP\T'|}j#uKKV٣uԡO%6=]R;wtFꖖ߳F5Cs]PKl{˾v--[fkR溡<[FP Ѫ>a1z5B&/FT 0ST*|bjOL^P Ѫ>a1z5B&/FT 0ST*|bjOL^P Ѫ>a1z5B&/FT 0ST*|bjOL^P ѡszr5l:ѫaL^[bjuVèDհ&/FQ1z5l:ѫaL^[bjuVèDհ&/FQ1z5l:ѫaL^[bjuVèDհ&/FQ1z5l:ѠHZ_s6jͭxk~^/Fߗѫ6jͭxk~^/Fߗѫ6jͭxk~^/Fߗѫ6jͭxk~^/Fߗѫ6jͭxk~^/Fߗѫ6jͭxk~^/Fߗѫ6hS n{m0ITTmH.C4Om^w>@g="j=3h @P+%0Fꗶ4uHuL/t?T<JtL&Ѻx &~cR/sS9 Һ>S n{m0ITTmH.C4Om^w>@g="j=3h @P+%0Fꗶ4aj`QHaoh^Ve"Υ2 ]ETl9 7[ڗhl\ A)ax/뒃@{6 C4&0{rrsdcn'+;7Omu9X <olNVolNVbQ|{F*DꚘ:UJBv'26:Mo`|5\_l܍TgI|nY!ncϑ5<:(I` mH,S;ÓTh8ӭ \pNO8|z* cF]~˼0υƋ\XHE8PP8zg ~xfUKHJT xPu/qUqygLѣ "B|Eۚt8+ 7OmԽoQ5i \ZO*?Wc=_9{m0I8p #0'&h\M>Mo`|5\g(%iPٹxՕXNq#>LstL -pQQIMsqUsR"Z" q *+Edñ= 1gA_䊇n mH6r]EL=*Ƣa3Tnq M}0:4C?8dO~V1X(n)7:x'@P-Z0.Ο ?jcZ4{Cܱ)~#|Aۛ pWnY);9a/į'a(ja2U)aW1S (R[V-\mZ!qϤc5<Р%=a4&!: 2DvV1K:* I m0G&`Hϲh;ytᶶ-(HL6`&x_9O>Ї98$OfP (( 6se3h AG3@ZNIy=ZYCs|:!yj3vMJh۞iZ+<&xg ~xfX?+Pҩ9 m֒r5,*A@n =M4:;RLѣœp?|{QS,1ŞfB}9'LC~ͳtL&JsPFS9N`WvLh snsX(]5i1 ;Ub#b441ZP φt;x& (y^ Ca@ җz.k|6 7^ڍ[$S(@@&x_=N8 ź8g3h jgF]]l HA?U;3NO89QAe(:S+4XYw#>tRI iJ;;{$N[9E2W{8Qg]JPi\ *hzh4˽hZEs_J ViDa@3l>S vrghD^u6@hksQj>ijSFJ]5wtݐL tGs[H=^D}J6ngGėLDJ<{9{m0I|30le.\3l U92+t x/ϩ*;@ш{C#QO%Ll[9EXfmTyj; qmWԠ: Qhv}'v=VŻ!:eVϚ챥u;EE#(`!Hi*XS$?9=|զ:1 6%0F5{k9(]47%UUH ‰(O!Flx &~cb;·opΓ[X$_ Wng%=b@T;E %DzrzL/t4;jf`3TnqD$L=c.~!Rf֍LɐTs=5! ,.^q{+$\ ;)C43UwtBtʭ=!-%A!NB^gPhvŦ^Hj /SUMv:g(MFvt% zT "!WU@'Om t@g=# vLK 5Ep{ &x_9oihuG(CpjKx9=l39 ک٘'L'ۜvrnSy0h@P+ m2F{HgMu󳘇 x!dMaZlʥ:ZZ9<$ iJm"$18>>|V@P+ m2F{HvSr{ΘxAfMaZN6PHDx[;nLǰ@{ݒLP(ql^܈P;{ݝ! 4I`|5\%PUPE6st" I"<$(-Qn<-ǐqE6A8ڢy88&$g6|q|\^3q*N4UD(f^e `ũR~2L>=V=<_&V 7d!uwCoe({TH/`? 5hxNgHY G@;7Om nD8D4ۛZO4 Bwwgd=m `#l1{CSVul@γ%hcP4.sݰKnLǺ_ W约sm:AuH?٥u:}dhRۼa?1U9E]Rꇏi]NY)7T|{Ep{g6|TWSJa6/m i3|5\ʹ_ ~xfMuKnLǺ_ W约sm:AuH?٥u:}dhRۼa?1U9E]Rꇏi]NY)7T|{Ep{g6|TWSJa6/m i;{}1hP۠Z7 Tf!j-Pqx3|Ao8j[UDO:ڪ"}$!Q'I?uTDIC'OmU>j[UDO:ڪ"}$!Q'I?uTDIC'OmU>j[<<(EMDGZevJpջsQ GYo;EG+VuD7fQ<m;tg [=τF]xҺ5nTC>VevJpջsQ GYo;EG+VuD7fQ<m;tg [=τFK;%(CpA%wV0{B -Evpn{  .8c7fQ<X- |JrkhUwEgQ1kHfJi"S5JQ>e<%l{9m;tJ~-Eb)IF#t82x|rC8jݹ Z&gӮjc΁bψĊ^zUog*Ȩ/ͳo61*eg+fThq0rFUSsb;>hr]46Pz΅AOHJ9U%^$iҘ9(l]xijGz(:ퟗ=YuΡ F#t T C1@9T C1@9*!8]\Hۼ}(g [>'*_6ʪc÷>> 9O-dhQHvBгšp7h 2|粗8 +#IJGd~U$@hNi((ۀhN-Ca" ,R. YIҨb&jd V1ʀu `LcA6Q F?L}_yՙé߅Gݥ$R@Ҥ/gI\C4jak8†=4Ug3PTL%r-_Gd.AA`:44wg"nC;4 ;ȠDL(4 $mr8 YpQ øp7Go; /sΓiMR6)q^T4{73>yZ!']B!:JLU~ V[lV:Hpyc@su:|XX"(h.<lR8jݹ1i> YRu(|5tS=KEP b!cAOfQ<2ؼ~:LZMD?agI ?84€*NQ!ˆ JH`9;Xl'IgVǚ6R.<VxATw-C>S(WvOl^?P&-s ӞFH p;wStL@`CH| 59nv<.eT2gWcROtvDpe"nVN6{ E\#:gpuLo=VapNbΉNP;"De_R Q1&Ԧ Єio61*e)Ůw!|o/C| &]qM@!<F~ruȻhۥal>SG>4A s#@ GR!LJ v8I@1gaLtͱA̠Ө}C>e aeaնr6st"  6H PyN|l\4Hs!dVaЙ.嶂{|`vJ#2 g";TS zoI * T ?qqEB@!bpջs^a(zsܩ()%cIĸ,Y6H'D7,7"n v红(WwTWT3۞o?̣}_yR.$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$H9>$K4ILSDTlFg+TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTrY8{i(I;}ǹ|Vq#ݭrc0ͫ&ڳ[vMVNZJNdoqD_HwkD3jɿv]C/Uo18{g?+'&=3 ڲoݫ8dhd$vNEYďvJɏ{{L6j!nu7$>rY8{i(I;}ǹ|Vq#ݭrc0ͫ&ڳ[vMVNZJNdoqD_HwkD3jɿv]C/Uo18{g?+'&=3 ڲoݫ8dhd$vNEYďvJɏ{{L6j!nu7$>rY8{i(I;}ǹ|Vq#ݭrc0ͫ&ڳ[vMVNZJNdoqD_HwkD3jɿv]C/Uo18{g?+'&=3 ڲoݫ8dhd$vNEYďvK|_O(Ifܮ\P\(3#`_Z|$7msoGW)`nT~cI7?mH{P5.dyu}@.nڳ[|e%,KL9WDz"Եl;HEbf#rLŲhΎ29s &*P+BEeMKInkx-% 'vc柎K454IR蝡\/&\/&\/&\/&\/&\/&\/&\/&\/& +7_Q%%TO>܈+8։ssZe)fa_r3h5(=|=c0ͮhP +fWJe2 &RRxyLR¢r3ޝ2M A2:d|g:2 kU" NI5k13[O YAvu`8xL, 9z̳~R?Qd_w5o1Hf!\?5}ǹ|Vq#ݭˏO 7MGL*\,z]응W>o&=3 ~l5 `[&XLL%I$~q+.}g:҂DJhT6΃+4L(˿?!󗫜GW|aUӲK}ZJN$k>ӎ+ Eџ>܈+8։s_OSeo&=3 B*Rр˰6q;ܞaNZDέs̍|g:sηЦr/?!󗫛CWpqB}m( %=Q%kx-% 'vc憟s8p'>܈+8։s54IhYJ2:FF?Fr6(Y@HMܤ}*&Iq#h8XJrE_D3kL2qݫ8#N/(9iLϋ?!󗫛.1t\=ip}縈f4IIwnGD"&Q3_+Pwm>i}p6֘"8Dwxr"GZ%AlR 9!PzFt⪎ݹ%'9"|L{fJr4^j'BnG4hڳ[|QQ S J XrnU'ZrϚ?!󗫚e))fgD Їkzwvw&2]*AN$=ഔ$yIE݃yU9YH]Q#4H[#UmdjOkn1Ӫ:SgCϛ>܈+8։s+{-"W>o&=3 D ? )IM-ף8]c9nlD^ho/z{UySkY3>=IJfD]&aVvanB߾Ӝ_+Pwm>hڌܑ:)-=)0̓?VR:i>;\EYďvKI[i䊾y1oif8D&^.QqΕÑp ۅQm'j!nufnŴN}< $t[*hjD^fTRϠ*&(޴=idZv_w5o19GsҧªG iLiQJ0G}$HK/ X?BZz/ԕSJe¦9$ 孾Tpnixr"GZ%$촊rE_D3k )h.oqwN$!Y2=D5g,g՜Bm3Rx?#P5C2QA㺕t|gxr"GZ%$촊rE_D3k" )^%ʣp7}Ogx-% 'vcqR5—d} +i \EYďvKI[i䊾y1oif5D$"=#!H*j.HdAJU3gVq sí4Ov-?3C/W2+3 ?$|&c+ G}%oqD_HwkDĕNH7msDBB uÔDCȧ /QwN.Eȅ~S gDϜv_;hQMص#uy 9z_h?֮gx-% 'vcqrIg+Р{,"Y 3$ IJ/3}ǹ|Vq#ݭVZE9"|L{f qQ g)ȷ ~E8䈑 Ko;~Y-ξgi'Te f~!LL8JIn,b*?'Wj J|\$ߒʗHrd9@Y?P,(OÔ 'an%Q$\ĩ}93?3՚5/(O$qJ4.gx-% 'vcpr-aS*r3'OHQ WG: Xi=e(8)h윲MjQNCM=z_W4}ǹ|Vq#ݭVZE9"|L{fَN"-K>QK.>Qs7՜Bp:&bZKX3e Ζ CC6ҶJ0%)֏|w=\_w5o18Oz-ntM BRPi(J0L%,hNcǾ;VTS**>RuK_4}ǹ|Vq#ݭVZE9"|L{fw\,}ޢoݫ8nusK<";4>hD^u ~NrI| IBIݷ'4I.ɠ嶃M~kxr"GZ%$촊rE_D3kwkDXE߻Vq s뾉VaI9d| .9D ^~KhS 5T 9z-9%kx-% 'vcmC92g*YLPl4s+aꬥi;T@Dw 0!]MV%%(>܈+8։s%DU9t%3bo1!$DR݇9i%RAsy1oif3f X40R${B *ث *ث *ث *ث *ث *ثȉǎYoݫ8?,۴t ]CBKW7y Oa1 ML2_uN9|\ыZڦ`Ȏ].1WGxd~WG~ n/GŗEa$|ưv-$\!C $%ضacUua1IwMB\d؆\b2m%˪$7}ǹ|Vq#ݭrc0ͫ&ڳ[vMVNZJNdoqD_HwkD3jɿv]C/Uo18{g?+'&=3 ڲoݫ8dhd$vNEYďvJɏ{{L6j!nu7$>rY8{i(I;}ǹ|Vq#ݭrc0ͫ&ڳ[vMVNZJNdoqD_HwkD3jɿv]C/Uo18{g?+'&=3 ڲoݫ8dhd$vNEYďvJɏ{{L6j!nu7$>rY8{i(I;}ǹ|Vq#ݭrc0ͫ&ڳ[vMVNZJNdoqD_HwkD3jɿv]C/Uo18{g?+'&=3 ڲoݫ8dhd$vNEYďvJɏ{{L6j!nu7$>rY8{i(I;}ǹ|Vq#ݭrc0ͫ&ڳ[vMVNZJNdoqD_HwkD3jɿv]C/Uo18{g?+'&=3 ڲoݫ8dhd$vN2-jRZ)(AJ"'(#"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,"9b/,q 7pJRYI:tU׌bcu]x*1W^1U׌bcu]x*1W^1U׌bcu]x*1W^1U׌bcu]x*1W^1U׌bcu]x*1W^1U׌bcu]x*1W^1U׌`ԣf \BHFD+qW㘯1^;bwx9s+qW㘯1^;bwx9s+qW㘯1^;bwx9s+qW㘯1^;bwx9s+qW㘯1^;bwx9s+qW㘯1^;bwx9s+qW7_Y:&= $[,ȇ(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?Pc}dRW)2:-b_Ѭ/XU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hXU+hKB%ȕ$Q|w"/!^r ZAU3+cх[G' OFm*9=Urz0haV­х[G' OFm*9=Urz0haV­х[G' OFm*9=Urz0haV­х[G' OFm*9=Urz0haV­х[G' OFm*9=Urz0haV­х[G' OFm*9=Urz0haV­х[G' ht6%%D q4N"F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=F+a=6̶JK4DD2VD8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8Ohӄ8OhӄQL̢'\)g.8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Oh8Ohe)QvT{I.] bN+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=N+=fE/r" މY9'. 5ZPFcpr" މY9'. 5ZPFcpr" މY9'. 5ZPFcpr" މY9'.neL%<%'9i;e `NE-I6?-. Rr˛4|jQ]?XW-'i>^)N-cqt ^ IB:1miDWc a 뒥I>މq- TrL>_!;rhA^"NV.,bj!\s[ݹ|VqyoDydDԣDĆkK/*Qq>7Z-A\I˺F n9(._34γZ'.f xO~ 2R,%KB著5hMz`&)&Ϩ sI9F@gg(zh5.2|ًi(G]#1 f!-T k9ft~Rp 2|)gN5<]+4-4'(ad׭nЗQ- )deHh[ݹ|VqyoDc(%%Q:xo kW5~|:䴋!s` J9ftρ&9G~@AJY4h?+K- J9f|bGH-PFc6)BAf]ߔS!z8uUAT%pN!ʉ z"/ -4v>1S 4R`TdIAPmOӝeobj%G 5Zi-v>9f}I5 j #JGn)ۤJ/4bZJH|7V.29l{RO=|dj[Ϊik9f}s(u*Lq'Rs&dX'NF>r݄pr" މsBsOa 5ZjbouNC+~]Ҩ7t[D݅v\S ыi(G]#1T^% J9f{"RJHHO^s)z"2+/)hiRi]ҨL2 Srr>T!3D­ z"/ -3(:r)۷jKI?ywH >tyAoKGH)iJ9f}An\O`T u3uEoU@u*+z yвF];Tnw'9!? |'CW3YYߴ,"QNƲT!}vYS GPa)|͋i(G]#1AE&Zz(] Ona7]" Qqrz9v"+-x \$)uڗFv0RrDɯ!o_vEYo _~g)SjNpۉ{>_o|A|AR]^~U`$!܅>~@`KJOI?ywH >tԈhݝ,q*oNe y u48"N]90[t=](jd.oL*CeO-p攪&g1KZv4Ri!Uģqe۞ʈOEA`AN@ECx-%f>}. z54rR z8bq\wq}"s/TrQ3dSV;ҰNFCmO!o_vEYq_y| [י:Ò"A2q3N,ΔLyL)L Wzyt@s/G=e&}S-Tj;g fഔ#k{5>RN^ڐ^rzT~ˈ=Smc!o_vEYN;$_h9עI9\QuI;: O\I˺F n9d3GO9ǤåAVݳm$/i;gU qt'R]6 |WˠU@&ў6hڍ">RRSK-1Ϫ l'?,jCE;J>fഔ#FϔSF;Vgb-HJpr" މs(v0$:F3xeY$Tgki2ꬦ  huҩ$C)ݣisI'.<x[i&qݦ.[lEb4NTt u4S3:D "eKc&rɈq݇M+5ha7.4prKOdxh6/t~j5|+s㷅i&v&zE8xH[ݹ|VqyoDgu=$6eH>4BdTӳr 6\iԙˣ$b8DhTUp9 A׌y u3U!hcgz=f⼊ىħ̭z֍gs9IТT/ aH\rZFh΋;>8DRBKČN)Gќ`PFc?5>RNNvSgA-H[ݹ|VqyoDj=\/!`(RE;+O,Ժ3~|ԣDERe )O=m]B搃|0M"<3;|u.Pm^v1qۨ~!nŸOJm(7EJ=\'CW5SUe3 fp%ʟ Vfe&`**z4JQl%_vSPFc?5>RNtU9reδm>B?5 z"/ -24*0mӃvm2)B4I˺F n9sl;uN2[Mz.S>q$/i;gL$s=B(G(Pm |}96M%E(H.%o۹=ʁu 3|>jsd\9?̭tn[t1g^ IB:kK; TidV̧ ]:RX'`aKGDAy𐷯r" މs8ȷ+җg.2)weA^sqg)))f}&4׌rP82"4Rt@s-}[vsz×*[yĐ u3?{3T3zS*z϶r_b4|j!^~|sN9PO/1x-%f>cQs/TTCn(G-4 4C XgBNZa98t>\$-܈+8\M6\_RtTiJ$蟰kh%葖,"D6t'9UzUZn~i$#7AyVgL8?'؞E2qs!sHA>gpՍGu.),n1t-r#>mTё|>jsf"̟;ByF噟I7atnȒV1x-%f>cQs/TRE%LG8r)r\eNQQ4l 9(hJن`ⲂFnD_A^[.fjZ*Vœ\p)7j\H_w:ϚLu*mnഔ#FϔSiԒdt$RMzfm)6}'MY;}ۑgWK~H-2r.)Pe6Gz2w*V5ipttuKq_g2 knYK?ywH >jCRinԣ=')q_"zIFfJ4gΤ!sE,*CiWr+4A(Um6-%6C1 _ZP5D} M0mВ\'CW4(E(I2:RK-bb/6ݵl##>NYt ))D\i(G]#1oA%MLR/9KRߌLϣ>uDq1.)ci?)czWii+I_1G[Lz\{ʊ?d ViIJo_vEYrO]1py BY8VMi(G]#18[ݹ|VqyoDt@srB搃vN'CUbZJHvNnD_A^[+'$#7윐 uhdؼu3}ۑgWJ?ywH ;'$/i;gd|>jY6/tdo_vEYrO]1py BY8VMi(G]#18[ݹ|VqyoDt@srB搃vN'CUbZJHvNnD_A^[+'$#7윐 uhdؼu3}ۑgWJ?ywH ;'$/i;gd|>jY6/tdo_vEYrO]1py BY8VMi(G]#18[ݹ|VqyoDt@srB搃vN'CUbZJHvNnD_A^[+'$#7윐 uhdؼu3}ۑgWJ?ywH ;'$/i;gd|>jY6/tdo_vEYrO]1py BY8VMi(G]#18[ݹ|VqyoDt@srB搃vN'CUbZJHvNnD_A^[+'$#7윐 uhdؼu3}ۏ(T8&ے1 JJQ8rNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)UrNU[G)U)kqg-J:&fa,CƼiȈrEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(9A(?PQC(rEP(]Qk9fgD,EHfDB{(_=P(We+粊E 종|QB{(_=P(We+粊E 종|QB{(_=P(We+粊E 종|QB{(_=P(We+粊E 종|QB{(_=P(We+粊E 종|QB{(_=P(We+粊E 종|QB{(_=P7n,G,ǢqS(W+E ~QB(_P(W+E ~QB(_P(W+E ~QB(_P(W+E ~QB(_P(W+E ~QB(_P(W+E ~QB(_P(W ȷKYˠPq0J0L+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h9AP(+h q-&՟r" j[h39gLجQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOhQOha3-Ғ"1䵬ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV|j1ZV,S(C".0ӎ4F!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q!Q mJU8888888888888888888888888RTJ22222222222222222222222222222222222222222222222222 &fYnD_B^[ѲrG:ey6drY4Z܈+8d䏼9!nu8mY7|9ɕ岲h|Vq yoFxsHBq ڲns+Aedkr"ލ>搅d/d&VɢEY%'$}! s_W3jɻ>6L-EȋⳈKz6NHÚB]8fՓwØ|lZ[+&[gl4-λ'}^pͫ&02VM"/!/-9#i[vN2VMaeh9l-nD_B^[ѲrG:ey6drY4Z܈+8d䏼9!nu8mY7|9ɕ岲h|Vq yoFxsHBq ڲns+Aedkr"ލ>搅d/d&VɢEY%'$}! s_W3jɻ>6L-EȋⳈKz6NHÚB]8fՓwØ|lZ[+&[gl4-λ'}^pͫ&02VM"/!/-*1UQBbH9 1UQBbI9d|<4-ξeNTNTN2;\ Ӫ$ixwم E)%u&d> SImFVIX TǬAp?u>A+9 B)*ӈ%SZ2ˉ4t0wØ|yRHTbS"_iJf̏/Ax&ȋ V˙M6ڔ]*+1Q^)RK+1Q^)LTWbS☨Ex*+1Q^)."/!/-D,iMF# HϬfy%E8M>r؃*HWz$}<<4-ξ`kY̤BHGK'jr" a4T&a%C# Tr}$+lRQ2A/2ZHˆ8f1L|LtIvZi$4nݯ1OzMtʤVϠa`uKV=XCE,~ag-@6hg|9ǘIhj!)W}I$!(썾9DÜg~Oeel)"D[W_>-1D|2*KsawB6M4JinkA-TpRBKEȋⳈKzߜs{n$.DTZ܈+8;2:HOzEƸn8.fy@OE5SK{L*"%f㋢j:s>HÚB_ 7OzhK.ڞjWEQ%L":Fc/~  ⤻9M;L -2# KYen{/[r@s Y|DyR /X]6b9•GОz;_u@b\2`{FEg@\L&{#\Gג9!vZ%¢EY%Y@H5M.+ֳR7k<]Q! ft$* S$}<<4-ξP2>?Qjg?|=D]gDٺNvOToӫ{) Hm%ܒ/C"CPk>ib$T]2\)D=-2hL$dig?vU˨"d&&[jϸJ[R C|"LTꊒk:N4.aJ*"\4*ntB dsDH.9|s1pDKpOmRRd|1Z[.ԗI:E] iGYkfx3\ [RR!M!QWlU~QWlU~QWlU~$¢EY% JITGAkvZD&_1E$K7k:D5JM9]/SGAN$nt]jC$'f3G:G$l!34K,mjDBM TSi!e(1u~*SJmN 2T'ԗ }^pͮzIR/o`$ REjQ"aFmSI*Ӑ7kSҚR!.}ŵeD eӺ~\+&ME%FIe'Li$4ns)M3HF +{kIP9#of.(DM). Hh3ٸTFbRآ%}rptRO~]ЭM6R"ON"Z3̧L>r5C+Kk{)qX[9-nD_B^[&P^%"[-"q|TxP45%37k<5R.he2^<$ڥ #. ,i%%EH2e-JU ##}mwvC霑31H[ŚH8u~b6߲ ݒh9lDGCQ*KGBTN}uft.FH5: z=!.hFC->jW4EȋⳈKz ʚUܝ0Mmϼ'^y4fd}$biL?II)lS"6L<2 @T#$}<<4-ξRV4?%7h:EA ]ҫ{* O)4TiFeryTdēDӺ=Sʖ$gnt]_?VxXmpĸ餉%4rqg^bx}Q'5._ć(aQ?-^wDY *Pk'FqR6B4[I=.F̏LϪe$f(^ΙG\H3u NI_u@b\3Lꖆ Rg,]۶^ϜsMip:?M,F"[j8\U=2-nD_B^[/\#FqS>HÚB_1뜈jGB++e.skICRJ.2\:ry'4ϺPSFßl0'ps3~j->f"GF{Pj/q9%}rsS9]"\kdNS#R]\*-nD_B^[/\#FqS>HÚB_1뜈j rh5=ԴM)G(}+=\4ey6u3 |Ni8DϬ⼂$ZQmwØ|yo9H㙊{qidkjNu#L]q|gNt%= +[]E)ZW;>ĦQN"h/!ui>ԩFe?%z>r5Β{A"\({'^-2YL0L0 -Ht̴I).✌}"Ui"/!/-8rBzO$}! sD_5NM:AtP0MtqG5ӠGs`~ҝSW?Jv N\)n1HYP".2\:ryv.nB$}do?'ȗdz]bW?=kVTS0Z<IeL/m$ԣ { K]wØ|yo9H㙋j9$|1Z[.cWs#\mz;D2c?*W!.Hb/̌Wʭ5%Kyq*i ZiSE)|*-nD_B^[/\#FqS>HÚB_1뜈jo {F_W3k\#9@ݯ1N޾,\UQups1w 9|s1p]pȵ|Vq yoG )mDTܓxleW_xle%N%J=*'+e ,SJT>W_xleW_/A$ipG:OC- $&VϷUY¯*WVpZRTkTSR覥G*WVp8UEU*~Ug h_UY¯*WVp8UEU*bV+zg8f1r1,$z2#lT"1QB#T"1Q3pmeJ3ENќ%q9/:"T|{j{GS#ZsAHr>U;F9s-Y'_>/P4tCjCG>a1p/DDtӖd+hD~[E#/B5#yaJ5zRIS씣I/VxⶋG,ē fR֓*;-1|bV)#9g+VXⷊGV*?xO!HKpE((禤tJ.IN$849rǮ7a/FXIZs N*ʧh̵f mؕZ/:>@A 7t<qTԣ|:-nD_B^[ѲrG:ey6drY4Z܈+8d䏼9!nu8mY7|9ɕ岲h|Vq yoFxsHBq ڲns+Aedkr"ލ>搅d/d&VɢEY%'$}! s_W3jɻ>6L-EȋⳈKz6NHÚB]8fՓwØ|lZ[+&[gl4-λ'}^pͫ&02VM"/!/-9#i[vN2VMaeh9l-nD_B^[ѲrG:ey6drY4Z܈+8d䏼9!nu8mY7|9ɕ岲h|Vq yoFxsHBq ڲns+Aedkr"ލ>搅d/d&VɢEY%'$}! s_W3jɻ>6L-EȋⳈKz6NHÚB]8fՓwØ|lZ[+&[gl4-λ'}^pͫ&02VMYtGqDHAKբ|vY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰWeF]MvY4ldѰ98J> aHSWBv N\} +;q'`>НWBv N\} +;q'`>НWBv N\} +;q'`>НWBv N\} +;q'`>НWBv N\} +;q'`>НWBv N\} +;q'`>НWBv N9kpGl4ے_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W!UHU~_W nrbRLS1LS1LS1LS1LS1LS1LS1LS1LS1LS1LS1LS1LSĵ&T!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Jt܈+8BjZal#u:G[al#u:G[al#u:G[al#u:G[al#u:G[al#u:G[al!֓I 2 D;54(_|#u:G_|#u:G_|#u:G_|#u:G_|#u:G_|#u:G_0*d_MM,|#u:G_|#u:G_|#u:G_|#u:G_|#u:G_|#u:G_|#92MMMP1:G[al#u:G[al#u:G[al#u:G[al#u:G[al#u:G[al#u:B[f\L6Z"᫒d߻Vqk]+8ɽ!;&Ƴg \k&ڳ{ZUY=vM65+8jY7՜Cd⯊!koxtHxɷqEYW%ɿvֻ'|Vqk]{âCvMge/,Mg8Ⳉ{Z#mk;-|Vpfoݫ8_CdnYhⳆK5~Y=vN*ֻ&DwD_5rYj!kqWg7:$֏(+ib_5L:_n÷kҤM/.XPn_iǔ&a8U(0wNTA ӛALQSkM42+8jsBqt $$e=LvMdTI%*e~@ta:3B ڳ{ZG(49I_IڜzTMk"!k'rҾ@ԣghTIItp Q qNͲALazDK1LtLtIvZdIJh"݁^bL<2 HisJ[dINL4gO*CҸ^];wφLaQ.PR&#K0_f{09K.*YΚI)'LSN>\O)`&$sL([2 #e]k:>NTODQ+GL!R*'.S&U@˘D_5rY"gIw mq:g]FnBL)v%tmRR4W՜CÛ I =s]ꝉ>|$UY=|9R65NZ)<̥4ĵo[.*xy}%`o]½!>07]" !)辂| ppqÔBevBi*daRNF't{ݛnYm^}EB{L~E31/ sD%@n@QegU^s?9ާ. )tp3r)OS/;|"᫒b ] 6|yۄf)1$Q-A=4r1Ȗie$/ipݫ8:H!bh yS7g¾VQ'E_CzGt$EL i'|s()f}%9*щRhQt2g{{^ x4P.2ϊ,taO@ݯ1OR}?*4hΚSIU# o֟ Ƴv9~+#qs& (%tJ$S –ڽ y3.4JʁȈz T?e4 H43BJdCl"f`䄵WN8xⳆK7 (cfQM,HAL4}ۄfnI8}--6S(AJ".-q$4LΑfD7AcUy *(Ә)kZY3>g {uM)T޻:S,lwpw lwpw lwpw lwpw l7 fm ֦gE_C4o 4S ?le#2ǫwѺ[H)89GNX# O-2hL,di37kS'q[뒢~Bdn9`T4W.qհyf⏥G,Bڴt0R"j/U'?RE-=cvˣ\+wυ,alIe%TKme42=O% >* i ڐ,& m{?iy'Hd@zHx])uE"F:WG}1yD0[n)**FGH"2PWGu4j g^zWL7T~&KD!]|TM" yn")'( ?.zG]9L>tj9ȸbL/L:>%,xⳆK7ܑ4ĨI2K{m.21s eR} BI)qۄf[c7ZQ0p4@{}稹j!k F+NMҕH4u٩•*zU'sz-Ec|v8XⳈ{ZOC̨`&{LCWE+E|T"sν+)ħG:$g$aZzSXP}'rs]1,luipWg𿌇/Z1{D=lJnΚOqSx-:3] 2[dÎpmvxtHx):{QG#O(0x"{gg^bGbxJr_9N*=$A?I/nYX(70Ҹ_t|QNI_Mg>o鼙NI55%Y|'!ENHcD_5rYTͷDT 5)TLΙ޵vc⏦ag8)Ŭ$c>3T]mTҢAQ&K3Oyv ڳ{ZYGDm$-n5=wLz9F(P+᢯!k4I[9ȋE>q0-S/gsR/k Db\KtD9t v4Bs@ݯ1NDZҢmE/<4Aq=Z4JurA򔸹S%؂9E\#wϚO`JW]4Y3RJ\L2]* ,B+hu_AG-zI?azĶE+8jsnqۄfq~̒Q#*DtWM{J?B'"/aݫ8qq\+ uÖnrYnPR+8TEtH¢`&O%K '<;#1(s7kSR_#o /2r%3MR&i)fEs´Uؒ&ȷR uT]wjSMȧ?6J}eVq]gLܽ֩Ȼˆ+8jsnqۄfg8I *9ɵtBKukq8 3$N.)Ȣ.+Ngn5=w[PJ Y"HIK5P 2zFO|Vqk_1ȋE=)^_qӚRGjJ.9uL#}y*w|gv2΃jJ@NC0EJVqk_1f\z$A EW%9>.a|Vqk_1ȋE:A))f.$~0zhp} )GOq SHCVRG:$lFfy$L3uHg \nb;pӱp'C\,IoT^Vʉk2(/ 8EK G՜CY8ޮ=<=3Bx1+8TEOiT2fA+WN:Du3  2ftfM>=ؗH-h Ƴᚑ=yRRCֻrA&8TOng4Q39d -!*>{CEvD@?TqMŠYJTss[.s:l"᫒Y^ynv0ܪ;' *0(̸[Dێ" HF[Q9SJ Vqk_1f\zGN6s -|%K_Car"Nḭ̈* |+G:ޕIg9.7V}ᓱnq]8ʥ ƾgµѼ))!-RwOvy$qYz!=_Ҏ#~Ges|^rGHEċ#ߞ^:TL,$$-<̡j3"Q_T#:."᫒Y^yny-:r!%!]9GqV\dSn#IKij<&>O8ie:BY3Vqk_1f\zLwzs'tmtBJQ&sⳈ{Z=NDZ)&xN%U2Laz%viF1 B1J@|埑NM|:8G:$R`֟Xit[IǽS>gh,e)^1Fs^bt*-![>TTkgv5 !XN5'-* W̡K|_z6S\ty,05(~i>3]*.a|Vpf-E4I堓*B#T"1QB#.>*KjJKNwK WӉI0M6 U0߼K$?gdD>d\)h}%MǼk"I/ڳ{Zn0%2 XO`5 XO`q&jTdЖ*a=*a=1JBK{3.g|Vqk_1i,J`ό*=*=>5Lz5JhPV]F]#a(C9benQ^P]A(ڪ<;#1+C'$SGJِb F*?pbv QSg2ӜrDd>U;DG9ոti?×[D؃R=ާs5 $#6_iiBPc1+XF 3o c1+XF 3o c1+XF 3o yBM8$Ns**$d$RI;!fXQ4%KiNFLB|A5$RCiRȥn@P,T-qSI \"᫒d߻Vqk]+8ɽ!;&Ƴg \k&ڳ{ZUY=vM65+8jY7՜Cd⯊!koxtHxɷqEYW%ɿvֻ'|Vqk]{âCvMge/,Mg8Ⳉ{Z#mk;-|Vpfoݫ8_CdnYhⳆK5~Y=vN*ֻ&DwD_5rYj!kqWg7:$b1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I x^d4$vyb1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I$|I>b1RO'T*I$|I>b0NIJeRR?ZC, !1QAa 0@Pq`?!'!! >UY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=QFb=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgGIUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgU aƪVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzTma UVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUUzZ$3pIlIo>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟg4&%&_ɯk5M&_ɯk5M&_ɯk5M&_ɯk5M&_ɯk5M&_ɯk5M&@}@e zgUVz*EY=gUVz*EY=gUVz*EY=gUVz*EY=gUVz*EY=gUVz*EY=gA䌄`"wZ!UZ*EUhUUVUZ*EUhUUVUZ*EUhUUVUZ*EUhUUVUZ*EUhUUVUZ*EUhUUVUZ*DO$c0!,H ӧN:tӧN:tӧN:tӧN:tӧN:tӧN:t  @ g fxTq]f~&9bY(;9tWeYIfznJ-N]'Vg꿒c&%RCӗIvUg fxTq]f~&9bY(;9tWeYIfznJ-N]'Vg꿒c&%RCӗIvUg fxTq]f~&9bY(;9tWeYIfznJ-N]'Vg꿒c&%RCӗIvUg fxTq]f~&9bY(;9tWeYIfznJ-N]'Vg; 7CȪEUEV@&$08s 1-q;=(asc_@P$}|\ HT4@ʀ %0Hv@kEٹ6f!@|_ Z0{EOh`!zwJ]&*̔߱݊U*EW_U~3;vU %7X$O6&9"D90_`^ =D f:FTϦ^Ԝ(؁|m՜& W Z̉H"Fppw;.WIʳ?/g%Xf,}!dk)Ify1@LMFxC&g*&%%2 (I }G(t@8S Z̠50xttQ9ʳ?-hܹ'f 6Q:<+.o,# 9 $Ӫ@9~׈hmNZ(((pخʳ?3jRch`iC 6bҰm@&g*&%"9!#O,ªd7Ѽwv@+J-R؂>X`PXŲA#ɱ`p.'ҵmo!LpYC+3_Ԥ3p/FZ, E+GR ==A, TVZS(H>\nZ&;vxFgA~W Zɇ8Qd,->ͬao0]']$F Srx~ ̃&pL@ؠ, $ `wZt^SVg꿣Ifys !dHO,DyDˉf(.MMKGmBfsCD0|T^7]p$W0EܚVPb|݈.; oz6ǔ`h݂ ਃQ@Z.| <OVg꿣Ifyc!ē`abn޸.׸H'͜C[CH| -z- "DrxTH]CVr4vqe R@̀Xm~R Su,٬itEo(d"hx2ye;-ĤcVgP*|vZ[ӉHl`.T7:YB\,ouinN%#Ͳ?SXSJF?ef ṟaե86Oc}NëKwq)m,ʟ2VR1+3X >e;-ĤcVgP*|vZ[ӉHl,jMHYBE*b\&H`y*Qڪ;UGjUTvQڪ;UGjPC>el,!k0}b/5i l̆!CyZ[pcpgxB#M"O=å/i`"x10Q x(cYE %X"{#lˬ*b\9P)q̋ulrt7IЎhܻRN$X!޿0z"]W-ܩoρ=B?}>P%C9)F719&Hi$ēn!D' Mòp} CԕP\C|{X6?ef~Yc*c Efulrt7NRh J2D {HZI6%)f< JS-ܠ]?cIy$xzS@dE  GE% Gfƈ]㶼&Gt6myxDVgV?A2K9iP;JX!)Q4hP\K9:|~ѭ)Ef[>DA{rvq U5|}#9L:rfW0vqKr >(-Q1 a&đ)7C%!MS0 ƳNq\NЁq5/-Z ݙlʬ4FL_H@[@y![[zHA!HɁHhC sZk5k\ֶhM~` ]"~7}f78`Xfln} yL:rh)ipb_0"ǐ'’X! Z7c.c`!) BߘݔaX55٭2ܸsKS­ɂo/\zǸQxcVg3#6Md $zV0 € $r p1w'̱N0ܠpxnYIu0ɌFKnL֛W` 倞' c;٭IH6cB+Va>z4I@ $قǮj7@M167Xێ 1+3 ?$nb 3ëS\nFXy;Npih/(p 9J|_iu! m#`@{;tY _ l aqao#)VLWd8nE$ n%!o 芀S2MD[/і2f-" 2n3)AqA1+3<(@Mh$LpCQY5ň|™ 7\'6<+Oc|PYȱElgs?DE' $vWZ ɉ+)VM m x{P v'r!۠Pc݈٘xS6NN/43~ǃ˸)di Y>?Vg>f0Đ.2Z(N^ Dv;#n_o1<(L iv5M\b+Oc|aa6l!Afh8lfXcuin {nCLig0Xvi!G"RiG*3V#ܮ q1+3kFI%'hm遅@clF19-tQ.#vY8[kJo7o`B/̑wmؖ@<b,LO~[-ܙ^ Ÿer fdhL̓+Ƭ1+{;$6$>2xm5}޷JZ@nobha4ӻd[aA?9!75M8ZTxJT $` ,L/G䏎Z2Hi`dzA $y 80vruin4 al/ߢp\hipUay7([ucVg6;> J2Ćm7f<=4'.Aw"+x,oHi&fKRy6K Nו7hw;̱NxؑٻQ)eA#C]H6/S-ܨ[f5տ 0`HS1ᇐSV 2f DrѐPz1)*wF0.ado=^~Slɬ !qQsfHwq$d -ǬSX'{^ pCLXه W<>S6xE7AëKw. C3chH: %464jK5xĤ@ c&o&q&v&?ef~Qc<4Dn& 9=v"J/khhj`N;t\'a[ܷ>eqXh֢ n 9<:sbOF{Ĥ@' gm{Zی/BgA|L6KO#p!l@( Ah]XEr$-moc8祂`9߆VA =s7{XHb;dcx0|Z[ 5\Cn4$7y;'ph,XL$@7 ADA>ylɴ g$J1YXgeS\yK6f ڠLXƆm2=x& &rf@p*Ɩ90>e ;CA gg5 e>-L Yj,Jf1A`>P99l:r'0["H4yP3P (89܈8=K3oG߮p(w詰m#OQ(Ͳ?SXSJF?ef ṟaե86Oc}NëKwq)m,ʟ2VR1+3X >e;-ĤcVgP*|vZ[ӉHl`.T7:YB\,ouinN%#Ͳ?SXSJF?ef ṟaե86Oc}NëKwq)m,ʟ2VR1+3X >e;-ĤcVgP*|vZ[ӉHl`.T7:$@` @ @ @ @ @ @% $Ĕ( 1pSڪ{UOjU=TSڪ{UOjU=TSڪ{UOjU=TSڪ{UOjU=TSڪ{UOjU=TSڪ{UOjU=TSڪ{Tp?I)ڂTRSUJjMU)5TRSUJjMU)5TRSUJjMU)5TRSUJjMU)5TRSUJjMU)5TRSUJjMU)5T3004{kTPU@j U5TPU@j U5TPU@j U5TPU@j U5TPU@j U5TPU@j U5T1&"cMD P ևSXbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,X{efuYԩRJ*TRJ*TRJ*TRJ*TRJ*TRJ*T@P:[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1=aDii{~~~~~~~~~~~~~a/ iWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuWUuL ƪUUG?UQT~UUG?UQT~UUG?UQT~UUG?UQT~UUG?UQT~UUG?UQT~UR+3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3N2ųu:<Ju: T +3䜌wa\& `p2ųrDq@L pu({ cXBFr9=P uiς?b~@i*D( 0d$% DShmp@@ٯ|PMFI j 'KJF R+@|4 !4TR*CEHh  GrN@"ujl)$4(h +RfXn@xAi IMQ|Tvwv,2y8xU;ʳG1a= x9=$&;)򽍬j`C>,H^@iudnŽ/ctb uE㍂Y@KP}fvװme&? nƜ'<L %Ft7UAFOv|KA\ +3rBdf`ܫIhܴO1騭%nj\zLddܧ%wHy%F?DKHi&%C2Z:dzǏ 0?"B/<A vWg8nSݼJVgU8', !nDh0F+`-9jj\zLjI̯zENn{ ˺ׇmZl7$Xg0h)Й2!="Y7 T^7`d5u*ܠT @yi#ɈGwr/LS @X7]`!0wv۷`pD $>- +3G@L"G!OpXŃDAb3] Lnۺ/b; Pt nBy"Ը-/ѶK9?bA-;f]1 ɖǡ\17 -cR]DcScٞ!HO1F@rHX$A=tN$;3Z5&x3y@ Db\GzO- sU8 @  $ d!̂  {t&lLYp lpUAyP پEL2`lvLpz=쟹VgU8%f;?`cID_VgbŽt7hwl|A`> &+eCܭKIbٸyD #aza2'$18.72 {#$`9=9 eeACjЀ;Zk"L'Y#<  ܑ/٣㋂Y e`?uV0Ye2 '̣>3є9'ȋh\ c A`$"LiLM7p5ܧy5#&r7ΫqZbʑtf+6"V$! 0p09_&+vbɏ)n]d66]r.=&efⷼ\U'syGy[Xf^+|VK@}&qjs{3qu"J1d-FIf>a͝?|N& ad rXEVk M&+3pDR$,sR,!cEA8zj`n8AyQ k*x0MK Qto(B 03@m%:$rbl 'p"Wr8 >9 VgU88,V r|# Kd572Ng璤j߹>;Ot% +3䜍6NLr<dh̬@|!f8To\9, i!gDX!@ x;|Jr-X&GC  |)c"[3DI,i{P<V=ɓMV5n>-i4.j0a2A\q fve Xe>@+Q2H߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߲:I3,[7SαSdSJP߰n e 0` 0` 0` 0` 0` 0` Ķ@vĴ]%vJ+Wd]%vJ+Wd]%vJ+Wd]%vJ+Wd]%vJ+Wd]%vJ+Wd]lJK4(c MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMn brppppppppppppppppppppppppppppppppppppppppppppppppppY n5W_U~ U*W_U~ U*W_U~ U*W_U~ U*W_U~ U*W_U~ U*WV0`plΞ%;*OU?ʧT*SOU?ʧT*SOU?ʧT*SOU?ʧT*SOU?ʧT*SOU?ʧT*SO&ŀ< U*W_UʯU*W_UʯU*W_UʯU*W_UʯU*W_UʯU*W_UʯU*5EHT*R_K~U/ʥT*R_K~U/ʥT*R_K~U/ʥT*R_K~U/ʥT*R_K~U/ʥT*R_K~U/ʥT)4+Q?GUʣT*Q?GUʣT*Q?GUʣT*Q?GUʣT*Q?GUʣT*Q?GUʣT*Q'״:f~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~nCb9 >Of~Ihn]u c";̰<\2}íL!&!\c yoBj13A!%́̌AJeA3 SDN"cT_}3"-E cb[mw%vqZ3&$w#2'OP?X8U$SbQ6@)lC`pm$IBZvTq(IJ3,ZM;N#AI39hsܑNt&4-۷nݻv8A%%[ i(L$}CCb9L 7Pۍ \@ %] vq#66& >V#؃/($̢I@eG+X(`, @8QW ]o%;Be .z `Eݜ\2\D^a" 0SwiV"a35c8J5`a6"7 x4F4 l$#m.Ɩ PpE!,,bOpn5e,Ė*O?gBD@ g3pyi6?I$lR -9!Q HÔs۸{'0%[Yw0,W:{ϔ-e@bN!-N>nRwVŋh>>[rVa0ӕ b`@ $J@h-R'A[fy #w4DxW?hX Yxý؈q j:J}-k..i{Bt[|vݧ ox X` 4fpaX$\wv/Vg`(+^BG14 7luZrWtv@Ork kZPy#ۍ&^q?$~~ b;q&Đ I!: Z ›Cq;te2*spX!;kwHZLdǡ̱܍JCr3v{4;E5А_;8LCK nnD0{Vbُ ;@w!,&Op yy@Ȅ5Oy/u ͬ\:4i$wb4nRyݩn݉Uj%SujXg!uעNGr7#O{sbQjC!Cb9.gt4pyNc$ [$3x'u h(hOneZu $]㉆B9""qzgjM ~rh;IC'hD n~G bX'OS :.`q-k 'tİ27 7"1=4{5f~Ij{/wp iZ7ELwi nFL$ 7Q̱܍JCr3cx$Yn7 `(2530Q@Q$=WŇ^N%Cbl keq0^9؄I.:אd`CC3b2vI; Í4n 8zWVg䖯-ױr2H]#3'i`7#O{sH5Cq8X9Ԥ7)s;揯v3o-;BGӉBBnOg07uTqp^1IKOkO6p*I&$I0fո2G,8!r>>[Ƭ-_[j^6c7|lUCĚzkc_engr ⹏in`Y8/Ƌ1h&XnF!K)G=&bzw{tad@@@/(]왷q OGwo▅$\r :>6|a(`I&j`ZؗPLaf6h}Bé퉇>Ii'M $5f~Ij"Ȇ܂g\\fs∋$ w0p( $xc{ۘN>r yzL `hs,G7#Rܥ k mà)jiM(cAeE,N5pPl^xd(sr Ņ m/01U# ی1-`Y}l2bMp8j; $x0璜ĞlhE@(Z*k+\V%sfDvKyiN ؠ(2'峹26^9A`v 3hc88f~Ij"Yu 2"= A .>3nF5k7"i鈽s,G7#Rܥax**,pݹg\!0!GۢH`ᙱY] (H5݁7kDE Y8%)[o,mmIYdli F(o[m7#O{sɾ"˽G$vF.P>K*Ԥ7)s;؂x3nE Â|bGùT0. > DG$ G8/2AOhSPLal/iɰP+{Ǭ,f 2DaBb x?r+|<#$ٞeWٲ(7 }ihb1sȵJ@Z[bV c-|# Iw#ǎ>斅s,G7"t)"0jՋhMp P''L;AYa#π3LaG=q  nE@l>_ə oh JQ>Q?aFkZE efzV GIb$9qvlٳf͛6lٰ%47tp$ Sohy]݈LRgD6@$v 7Lꚬ|&sM5cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<cexd(u<Y-X5K~/ҥTJR*_K~/ҥTJR*_K~/ҥTJR*_K~/ҥTJR*_K~/ҥTJR*_K~/ҥTJR*_K~Yhyc=/lңTJQ*?GңTJQ*?GңTJQ*?GңTJQ*?GңTJQ*?GңTJQ*?Gң@@P-lZ>'ҤTJR}*OI>'ҤTJR}*OI>'ҤTJR}*OI>'ҤTJR}*OI>'ҤTJR}*OI>'ҤTJR}*OI>'4OkңTJQ*?GңTJQ*?GңTJQ*?GңTJQ*?GңTJQ*?GңTJQ*?GңNvɷV`,I s<<<<<<̽}}}}}}qqqqqqw}}}}}}<@P@@>*P @P@@>*cP<  (@@D8@XPP C2*f 0@5,@@ 0>a*"PԈT`D' @@j| @ XaL PPd 0 @@P@ @)P>P*P3<@P=@@ 6X@$  @@+`Xg@Ps<(Ҁ @$D@>*P @P@@>*P @<<<<<`<?X ) @`<?X )8C( 4 @000sN8 X`@U "80Z< d 0?X *RGP0H`)4+0@   H )RŠ @  5 +PHP / @(+@9 ` D@ 8<  J?X* 4(0$ ")4-0*&(` x ) @\ 0+X*,C 09 =@Ġ @(c+lD ` 6 < X~?03X`AA 0 c `0C$ ) @`<?X ) @`<5AAAAAA<<<<<<3qqqqqq 0 0 0 0 0 0>* <) @>* <) 00c pAA$ @< (CI,@ X> )t!0* l@D "0)ȔX@ * `Q$ @  P"<( @0@)$ !AX# @Chq) @ C4p#0(p @  @D> ,* $  P)) *(B, +TP(@< @@ @!A)  @ !F ((@0@0 @`G@( 0  % ,>X Z* (B " @4!D@ 0@ @<) @>* <) @>*,,,,,,, 0 0 0 0 0 0H0 0 0 0 0 0 0AAAAAA?+!1AQa0@q P`?kFk=bCءP({=bCءP({=bCءP({==Z8X{=uaX{=uaX{=uaXѢҳ8u~S2_We:c)Nu~S2_We:c)Nu~S2_We:c)Nu~ÚeJS)R)2ǷS#/O|/{>)2ǷSݫi |$LgF(ebNo>_񾧺_ zwTqJO,МD]Ic:uVޱVywRmJncAU~Ai5sgkfI0zBw}-sڵmԯ?鮾^",M^ FsH@H)Nre٩鈒FLW"ȹ#.څjbwK~">,Yš.*.˻ BAVV ɎDy[UakG!~oZ)¯TD\SoS{Vg$Rx(p{ԉUdU['yU8V.*ՑKw~G 3YYr915%Bl'$ *%]?ElPL\M7kۥA9Ndof6:K_%UU[زbHؕ ;A2 RNSP4C䎒:J abXb%yV'PjW)' A EV_t"sdE(lE2wڎ"ɀUjk۰ $ ct0UYUVh,6^n@ 瑗|XgӳȳS| ֱ)We~WS+V(/j(흑4Y;OkDl?בtJ&2&/ͣ&IˣGq^R9 aW%WO0z/<_I&f^bbtʖ&D(&!RֱK=)KǤ7 _~|./QR#y,>k +59[B@`p鿦ctjTA 0~xUUTǪc?m3i&2#.R !<i7H/Wu06AqӶX b=C?_XW|SeQ}c_OEo~>F_=_XW|SeQ}c_OEn/%g?c9g4柱3~sOi?c9g4柱3~sOi?c9g4柱3~sOi?bx+k&Rac1 }>ac1 }>ac1 }>a SVc+ 2PC+ 2PC+ 2PC+ 2PC+ 2PC+ 2PC+έq}A{=}A{=}A{=}5,kg8a$JK㋀yQZz5y;v c cT/RU*Td%;@kwoNQtomދ<>/KtMB$~c\*\-cbFt$[+0p.K<*ZSjF'Y6*5\c4+Pqc]*Rd]([$c6VCDU,MwJwoA e3HniMދ!UCvpʀ).h&%ye8lEX[ +h!GX?,7mDG;H1d>f,&S,M[@YjLjg$Rf%,FFB$*3XrkלhYhdAVvh}gU~L6. 3)UTHJ x)l4!xHp_*ϤӸpw͌gEބlX%ͽk<^D[")\Aذ%`F{ %b "˵dIӖEI-2!@;j\:n ?_yiI*IKk40"pFhdmexRVND+29rYrG8:4@alX`jv\S`uE7T_w8wNWSpWJQ -䡄Ų*I$!N]`%`Y#bychԬ/JSj4i>%5oXS$ ِBI AڦJͫH Z alX`޳A#b?ͯ5{6?{(hl^[e!w{'FZ}5֝L~Mc!brYǶKrv,/o 1KdI햳Ylv: s[9sc]\sLCLы'"&:$L&c|;ٲcg dD2K(h_g G) s(txk9La+ʲif=TH έ"p'2ԟG(P/9/e8IU^d*R]@Ubԅ+5fw>>[>uڼ=>R4FLi)Y@dxH og9P+0YA LW;ЛR&\>>]֋%Ʉyr*Rb RHC*gF} M. gg"\͚θ WI`}ݵjϏŏviKLѹ0Z:6IG 5 Z^}ĒHZ?\DUmyYC"i&ȬvH0$iM!&8?HV1 vEj͚W,\l;kώnŵm,D (|V-opyM(x1]6)H Wf&? $'ܸoJ$!6e,9Pf$E&+J|<9 ^L b 6{U"ݹ?!V()t-f.y-?NC#oe1vYO~w<ݽm /2d/rk n *L%c23Y&䊒ɪb2  +&aZDmZUlHՠN["J`\-; Db]ZBqO?*`5A ٥vm!NSJ`\26? d1JMZߗD^6"ufb|:Nf5i2eL9me sqH\EUv1/+ K4;|gdZ\#본&F~ vV2m6MI65[Uj-x!m^M6툶HzCTߔds{4=v i' z)qoLw?mC}m e6롒[O?+XD=w`a}ÙJ̃I3pób黨>R٥v|t%[ج­'"j$֦edu͚@mF!`~W`lx;.g=? ,_?+ hf-)jpób黨>R٥v|.J  G)w;4=,(9W?@j>ơ&AcF8#+ﳲ `3;#3RN_݊SNW+ h2&E 'v6;X|߂rXvx_7ug]4ͳƪ;F[\ @1a%6S WMχsl>y3ě;.-}?=+ oX:n;ϹԻi]&2. bR%ˆ ɓ"`C9Osl63Fw\ߣ?>Hg5#(JEaI6&\4˶q2JJ́0|`v1D3ыaNg8**qVl9[0&nX:n;ϹԻi]|sl6?$622žœ]}ofo{"lxaKS H:=TD|R4N}JI%Ix29 lq ͑"Y 2ccccccaA"E xy($'k= +)a-sSYoO)f +-"F p%WHwoWHDn|%wQ)(,)~+V^Sxy*+߂Êx?[{<[XqO?P|g~ )g<ooa< WB-,8UWŹ:Rclsϟ>|ϟ>|ϟ>|gNI HYMCh}4>MCh}4>MCh}4>~ Ɣ)G#D|">G#D|">G#D|"> 1BiN      "\ɚ\uc5ՌV3]Xuc5ՌV3]Xuc5ՌV3]Xuc5ՌV3]Xuc5ՌV3]Xuc5ՌV3]XuaɓcO|DǓr8?sQNG>"cGLy9^'#?1qz~sh1O<,y"=Q@ [;7腛ezdςʬ&~\`^A&p, 7.qAS>l& vE9^I`et vy,ו n SnIr di( ^j?|"HEcc+W5;@6PQh45WIhfV)fR*O_Siqlr1U ^YvhjT,QfP̛H%Q`.N$Ql9baQUU6:LfɸIk`slT`UUG>AM|C ٣:]`SiI?#Gl*IL70YS9 19}oQ +d`i\ ۩$վt tʮAM&ĕĘdh}IDH<˜c7/f`U뚭( m Ԙt m@k"d2HYMKB1fi,@ʁ'Oy9^*H W14-Ŗl4Rg؅dDZLȸ$E W Aͥ}*$Bʆ UoVk^sz;-y(%/zdc&Nb@vLr+.MN,\M"^OR$eJD/+HUhUr >H0'LFo֙+ExXI4 k;g| hf[5U&|W1@'df;lp9ѱC?/'#?6 ]eݐBUvy, ZB,>g(C//̧5-%jJrIЎ@&׾Drp鲚=Dr7giU`@Yzj& S8$YSɤLmjr0idՆ[}=4%]޼;JrVbha{U.A`DJzlS@6i, GOlޕ%БcMjJH툝P]} +&c@!A#Cqz~svy, 5y$0/cA"Ql6c^$ ˚ʽᙚ*Ղh6,Q!Ж慰׾KI5*"՝{p(f "I:K ³+2dVni\olCe|Oso sd+IDި bXe4eӒ&D ,Hj\[[74-o' .9"Րb U E5;Bb6&⍂ƶ)j`#~ƛ`]i+3J@ qz~s5W3r>p9%aifR?;'WgԸd=D fڶ= %0#I2Y5Y3 S*MٵU7ଯ a q͙r&:|ͩZv4V+b_nh3 'ԃB1>r8?sQNG>"cGLy9^'#?1qz~&C!G#?|>C!G#?|>C!G!c $uΑ0|>f3a0|>f3a0|>f3a02t5*':P(|> CP(|> CP(|> C ҔF5_yQiF>k%n`ZEkQxy<֑c^k=F5_yQiF>k%n`ZEkQxy<6$M柲<'E&|WHÚ|@s%,&eެ|WMV+ $idMK<%[h_Q}F )V{ڌ=?b^'^L98[־8jك\TLbEnJ']$-F>-?MUKt<< Sy3?axk =Q*ED 13Ul2i?i/0Sp K(sSX{S5x9O90cd K YӪv1Jݨy%PzbUvA{a`xZEΝt{9vxZ|9[+x_,0 aq (ueWܗ׹{(6!gݨ3QP(ɤZ̸ZE9gT?q,$~YtX P@TZ(qZ,Eb  j Z" \ m}xxSn3as!K6s ƲilD'=EJ5 vh\^ǽ%z{_/>[N,aމ' H~S?1ԗ߻nAEggyq}xxT)4Y3H0H沊i_ =N2]#Xs6ΗmSyQXsZ~1WJ[i2;B]<9X7cxݰ[Vc`ubR+YJI@V :iZЦȡG %"Xt]1HRD@.3SQ)Mp&w =F X ?o6R qK:^&uV)lsxc[%TÊMw6ԿgݨeSu,)%2V- HrٓdƉgROB~ kz3zDتL4 ٜ-F> c!`I%̒L*["fGI0% ,,Dr R,a3aHj\~Bj(:@xY7j0xYc_$dv$U)ECҷ-F>.i%3lеV*APZZW,<"Htc@'xZE'+NP`00ZnV]ˉE4+#ȑ&2$K/*FI FSj8qP87j0y"5d<{ڌkHj1Y/5v/Z|Kgݨ洋֣5Y7j0y"5d<{ڌkHj1Y/5v/Z|Kg}!FLH$?ďG#H$?ďG#H$?ďG#GE5Ç8pÇ8pÇ8p0$BTQ @ @ @Y)(LJ<O C ,X5W=;_xU0&40YWK8Y .`cQm؉΅q.|\3p"*6wo:rp X(z\t  7Ll ($l#ZJ`$?儛v9 _- A6+Kc&(#Id77nRRPO6Hz)O)ҡZؤÑ:=P[.ȡ0QxQ>bhGM>w<ߛ+T7LJr>KF䤆tr:˲t^0"\:΢pfJ; >rI1XJT3qco7F/`weS"O(1j ȭw@~ H@fAFK1T)DHK=x=UfLw~n&<"otUmi(POMP0QI0?b*?6RM.BBfd8j7pDcc@@ BtkK~)b9!zBtH"HX}ٚ)J@*.lU5cJ&Q$& 83rX0$juiphxZԱ.$.@pJHWލ=Qp5!FdU* ܔb19%2Lz^>Y"#Ρ%|6ގp< a'1N'HE[֗bUDɠzНDU1Wׅaƣw KJ7:WQ&$ wvl/خ}"&}.EG`ӻ`Ɏո :RkOm&`5C*fdJ^Nvʨ%%ҭR'!m'=hwxR6S$?($BJ?fa0Lj7pYJBɥ#IBm2OB6l $5*9HXX}h? 7 s5Y\-JM bz %Aw1'o0 wz&]Wvat, C&o1:P#x^ɟ 2[ZCx,\UKU\ߛ0g'ԩHH)'+ <Mxz*.~>  aX|,> aX|,> -B jo6h٣f6h٣f6h٣f6h٣f6h٣f6h٣f6Z-D%}Uw#܎'r8w#܎'r8w#܎'r8w#܎'r8w#܎'r8w#܎'r8y]4Uw4I$I$I$I$I$I$lJXWr?܏G#r?܏G#r?܏G#r2 ,h& FHi9sZk,${洋95A=ZEf|ց "3>k@ed|֑wC5_2GkHσ/Yd#5]Pgh lc@c@ZwZ% 3pDԈ^@MplLN5(Sܓ`@S6g37.AS7E&ǦDԣ ܂&a{F^#"w2G=ya\fMqԠ&i8E;MոdTa`\[ve- ::^ˬ4(? V]JI8i xSbST//ɴEL%EF/I3{1T)dABtR/C+Rݧ?e;@CM Jz%g9Q?~ DUK3)V-aE񒋀W9/+T`]zuҨ76Wkje}<;A=^v/rb'ɼm*l-u|O̅En]ߙ20T[:Rv-L@!,L\L έAv: V!z6tY[FƢ1$j[},. nof hx/L%`M03AVH ^[%i*v99%QЯ|Fկ7"sOMS@3R PXYԁJIҦsspRPW{p4!5ւU+ѵ?fmZ, }i)vd6 OJm;4؜ "ݘm6:P"gPٔ| -k(9ޮ尨 #i*kX%@5KzҴ0.<{3{'`"zaa TfQF2hYe Vσ~cRʑk&urGkVgJ큜Z)U!RIÅ 3 cR T@p@/̑h%gDL]֭K>v6X3=# NA - `f:hw2GmfMwMM.ṛ'ql Q{oI * =h9ٳ{=3cxS%eI4gl9͢b NċR UYx2W FO˂@]O55UYh@DCjr[} B$ yٓx`bp4(nk@/[dGVUr2{6os0V*] Xu aMq%a`^9VRS tT㽙prs`}IkCJjW)f,SleiO~@0Aɢ{@] ? v6NRiL]FTav8YHͼɼnvi8EYٔ|OE-F"k@be4G2{6o~9Z6:of|Ü3>\N9̞ٜ͛Bt<7OTJ'i1 + ,Fgl9/txWkb!$d`z !*5RRiw2G VM{YbGSd3aɳIRρ]֜L>.ӎɚNLGaaaaaaS,g[>9T9 }|2SfҫOWjiuTOp]͜3( JAo< σ&GB> )i*v9R> a $( 9QC4e:6 6੆sgh 8$ ]T22GkHσ/Yd#5]Pgh .s3 YHi9sZk,${洋95A=ZEf|ց "3>k@etFeA)a5Xza5Xza5Xza5Xza5Xza5Xza$Li)iTxQG*4eItJqE?Į=ѯx&Q(9f C[R rAlKOF#t$&G5 d)-k@XNt7~.Y4ڴq$.O5pSFct NyFx7MH&c'K T t'(<؜ OL8ZO͔R"tl⨧&S?1Q?s* Y{ʧw7xP Ǧ7^gGi> 7Ls^{޳45׼7Ls^{޳45׼7Ls^{޳45׼7Ls^{R LcA |H>$ăA |H>$ăA |H>$ă@f%Lٵ3G#?|>g3G#?|>g3G#?|>g3fMlcvQ1F;(ecvQ1F;(ecvQ1F;(e!Y7J%DQ(J%DQ(J%DQ(K-!1AQa q0@P`p?K+?~߿~߿~߿~߿~߿~߿~kŨ=<߿~߿~߿~߿~߿~߿~߭j*O4߿~߿~߿~߿~߿~߿~4Df as߿~߿~߿~߿~߿~߿~|(y߿~߿~߿~߿~߿~߿~ЮS^XF IUnfŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbʼn[YIWYU?u]gWYU?u]gWYU?u]gWYU?u]gWYU?u]gWYU?u]gWYpM V9PfǏTFvD#d8:ξUyBW^t]9*V8԰5'ۆ Y3-9_C ߑ(ąWzwQ,Bk sNgTFp Wv4&.&UG$(d2[0%k=g_*w oץ?ݍQGiCM n}`a]@h,Beb/Bԣ?*Ao leP0 5xLs}@$lmΡ-NĂH/I{ ޚ3) GD0NP*`vʘxn>@ m9W^0fI\a1B ^ X@@2_2Cj*Cl }ep8>jL!gK _T.*^ĹgsO0Vec c.r: <%&Xم߄ "AD8T  b-x kAnNgx+J@RC9(f ]`J]cvec萊eG/#ugʬ~B`WJfc`wl s\J@~Yʶ_0a1+!Ls)Ѷ3*$`s:SUV&6A1!+%FpѤ`? `efM6HxN爩 VrdyO)~!s$$ ]4uU_+F}uv|AQe @9[{ v \w 8An Kl)D?aW+0d X TN8&cf4c|dp^jnbԧ$$e[t,"X (P eQᐊ`CUgTPMU4oQ.v|gu2oۺsHH& 1"S MaW+4 ס*PY'+)=x&I76-M^;8)/Q(}Abb"!L,E,^hv2A1-xη:~;:N1C $7/RĘ%Rsq'zBoh2=LJ S2V̈d-w j_J090 #,˧x۷*6 ]11Le ꚼ>{̈́悚_= =Ҕũ-]@ȁt8<PV簰I6Oe  ;a\uW( ;~;:^hr)Dq@[4Rp` J!wt0GrB<WKߴcrlڲ,~qElWDCUSWz$ eG"cr'ɬ p' `Ȁ"ꪮ\3  )nbŁ3v?*Gbfh]/B2>_ot F=ZRbix. j_{?tXmܺ=_q34Uɹl5&3 UYVꚼfyʎ4nH< %6w7 IaW+3U ߺ,3"\ i|*UnzU0$y G 7GP^_-x`)i=C*UzuHdpfFA]7Ul Q*VR oXYb8Ĺ%VB}5M}_D5M"!<T3_={*%mۀ(t [BɊaeĽj9 9qة+5$Tr%9røRJ*@l&c%gb=3vT8TV j&#'= ҀS5ALG\U%Do>Hg!JX Š0-",1^1y5\ d` =$)ly5xl`!ȱ_`'R-ဳ?80<}?|orqFK%X0EH \[₅Ȓ]# X Pnn }W25xH h 1\Őd[{.&X\QϰfLuM^:'Isڊq pDwȧF6Z-NMRX0>T1 7*^+i"£HE\י"E X0-nݻ@kѕ' 6'ýÇ8pÇ d T@bTsΧR6_ph ϲݺ5Do҂L *HzPUwAUU - `2iF#ذspH;7rm_80AܽKr`hwGIP4uoDzL@$|bg@ꚿ졷wuM_lP݇;6{(m]SW=6a {gTe ~3jg?5ACovꚿ졷wuM_lP݇;6{(m]SW=6a {gTe ~3jg.Y!XP}kgϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|Ϻ$IHh+?utG#]D~?utG#]D~?utG#]D~?utG#]D~?utG#]D~?utG#]D~?uN }A*h$V`ŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,XbŋrEjV |߿~߿~߿~߿~߿~߿~pPQI&0EDfbp3 @ @ @ @ @ @ O $߀, *[yիVZjիVZjիVZjիVZjիVZjիVZjխQ CB?% f @ @ @ @ @ @l=*]2`3 0` 0` 0` 0` 0` 0`-:Wg0I`H"D$H"D$H"D$H"D$H"D$H"D$H~*R{AC3իVZjիVZjիVZjիVZjիVZjիVZjթ5Uy&`榀ď$1ؑ䟚F7h*)- ҞB6 +2Pwݻv۷nݻv푴nd9!L2cea1ON%{3Z53f.^_:> F7[XA%F>Gn|&HpB\ڧd gVHI"L`Ȓ{m8T^ =2KY:(JB, g<Rm3!G)鰦SM1f"m$cKo`%aPy  ^pd*H7 ;[Pf'΍ #'K!2"4IFM%-^q 0Dh*wϳ@cA`llɑ&H;Q UҲHr2O](]Vۇ~"$7HMq6mr9`y4+0vH'I"̙*U@JI0{Yĭx?h 7 h!u.ԣtǹ4emYK̓Q`F#HGu[+d It77cc1H属` Ql Jlt36bdXqMEãq( ܐ66Yq(~@!($X63H)ʠre: t?:BR5*F}{@oe@M5h &xh#CgLf%m 8''%d-GCWx$Y]ڛO,< "bPxQ٠8 M}X,[1y6}x4oH]%O2>p=PH( k'0$2d4b\ln(7IV5@p_O1AXȴHUWvBRnt@BT3ỠrT{Oq|OHJ-pR hR8dP3"NRJ1io(h;D$2#ƍ4hѣFa' L p"(x,̉ 6 G׋v|^Ku b,`;Hȫ gl Dcx<-8ʼnvVmA"R.Oc\00$RE0V0A"DԆTdx1 Ȓ<#)]5hEV}/0`GEp2ԑzBnŸ`~x8-@ -< ~E814(@$)?5 2h02eR){J3/!oq )VW}N 9Ac^P"V}&⁶j9"yHNiNjeD/xHŐD@5-QtjX="$"b'nn.C>B;Բ5B4:g>V^@UjdQȨK/E%o^ c$f;$l p@/Rn^E' j-"ĀD3?7h OLtʗ;*sUW7cr&@ ?Ȑ݂4V2B&9* &p4ĐvCA;A~l1?$h4ڃ0,`8AI`0;VD <.2)R˱ , $u m,U &'~nd|RVCr9<7;\,AWȤ+>?Nf \9¢хݠG #?5lAUf(# rO1gfdkϨ˂gdʟZq+RAn$EL 46% mH+gB#,-89PVWnS.Bj1:zӵυ.# X*H\5# GK{A,a%Rʼnqx2iA2UEJ\rIhl5yOQ $؀v-*F PT%V+HԙSi:S0^\ C5&VsJ `2 &H]`kV 5PͿ?b|B:$Ì \1% ƀH8FAnWr #a:lh,pCaeЕa`#"[/Ǒ$xc B%Հ9-\}_XXc&8h`~z#֣~OVK:?HRU{f`xr.8QXJ,٨|/\\V yk@YZd,hY[(0h! 쇠K[w]-(IUȒ{mY]i(pxQ'ºBU*e: GmSީ(nW0յL?d@%f߫fQL݇`Uڰ)i[$8D>x`W nw $eB{Vq_YNl9y*HNEPnmAr5,; < 8gS'2~j6@ (d(2ˌCg`U@@F]z |V-SG ;Xm5V,EESf}x !u.7Gn1^aanׂ$$ԑn O*HNEHЈ7K׵2XD^lsf.zxwKz( "UĐC\#"Rp%Qn6Pv '`$DƪeLK{mj{ c4P@|*% P3' yc>_F !&B$V *Q$EPK LxM o@ be+I)25Pv҈ owʒ<Ӈ~#QMrk0xka `{ ؋L9QC5U^ا716wȤՕ I𭑉8!,B; ظ cnJAO|o"7ܔ.* I`E,[@Xdea3\f0?-ʨcv[s8ceaX@^Q0j$=F>$|M1pQ'|j:hF7¹)$ xoӔ{9L~EL󒃲CsͿM D<Q4_'"K.ʒ@0GjZN# jA5B* 3e5~j7qqg<D#w"̘ (2J"4bq-$=(J^ԲTd-ܨ"0ICU]_w"ͅ$ޛՁ]/#%u[P K܂j3#Q]V.2 Zq0V>Hn0LY,m$8b{dόHf, `%PqOΘEkGщ>H}$xlQnEKC5uʚhd0pcA2n$)IZfzSDqoYˀ-lX01&|(oVbHϒP>fRfZ>#H4d|M<5DQ!8JXb-8 ce7)|Ԝ&7=K%7Fv\'">gFuIC4 6d  +h eSzXe?:]ܺ^OQb#d Y{k"fyǕ`yCE :TEU[]b7UGg,)|ECÈҶH8*3(B=b͆6{/Z!IR*ʒ򏼣(>򏼣(>򏼣(>򏼣(>򏼣(>򏼣(>򏼣(>򏼣(>򏼣(>򏼣(>򏼣(>򏼣(!+&D;>g Cr?h_C0)Q>7Q=G/3:D;>g Cr?h_C0)Q>7Q=G/3:D;>g Cr?h_C0)Q>7Q=G/3:D;>g Cr?h_C0)Q>7xOT @]ZtG#ATɍ;xʀj+?uK>Dǂ"h7PRb\B}PR3~ e/A V">F`U65ߪo b%Lb'Fc,/գ?*p~MJ#)Q0Us#.x(z^ ChҎDC#=>p94i`:nĐbh_g@O=ŋ,XbQ'{E+`%XXP3/(f]2 woSYPCsX _ 0`KOHQ` VIX9UJD,K8@\SNHB-U)hݬ0m$ c (:V(`lжOdljN\LF" (@ cX.C0"p/M,xbƼ^d9Ļ`Ɔ gf$wz^.S.P4F4(G88 mR=Y0Q:}UblbW4U I,bn!o_(!fŤ n DX k8 p=]:e%&s(TI-d W6,$uWs 6y_=uIUKN,zH77ȟ!B6 bQV2ɱR ,Nif^=Y%LT&mI|߄n!k]P^!LPFy $@AB渶1mV2ux-ZK#=`2` jLqevjCo^x7xs*D01F*KbZS`s]T֫X y  o{1{FH=]Kkݐ&bNk {a~0=m vAe0q.)_X.B$B @{T @fųLЀIY۱p  , ݆&w2]ޠ |}CG0˜* ٔ<8C? _W*CY_& V K$ ߥf;K*DVL0 e#.+T/Sh .9-իNQ">>БAN  \dəˈ']k6;p8w* r34ila&R*h:5nԾؓФBv-x,K4dFq(?r?XP 3/pא p"6DĨn&kuo>}jW`ɂ]%=3;aE, wZ?xǂ#D -%$0ݪ . G*vL}>}ag.+/ O0,M˼3p0Ո $v) *(4|}CGZ"604g/f'x"8Tϗ>vn]f8%a0]^#OW2 w+ۖXq+pZLyȰf:":%渮m+:/H04$J؆$Pd00YY[y y[עdR-րG4|/^!uV1U%jC݋|SD{􅷁 sZ /M(nu{F+@P𫀆'G+0*ν:ƀ|j*@n0'g}s\͞k 4얛"',F%!患jF` G4|/^!^ dė] cn =IU7h 䊑,L%.TKd!BS@^#OW2 w+$JmilU =AU~*x vE FQXXU ˻v?ūsWyp"11gk9&f2.p5 M,J6R^>>}bkh>tG4ݩ*c3 I4% i5zVX_/$8Y:J w7xJu=^`ȁz-euZQ`n]0@ `dcCCpX 0 Dx0\-wz^*PD,V ;/u KD+["ѣ8՘kӁ`!X"J =ѧNJHPS#:މC .#q(3$`Ϙ;1׼^#OW2 w+$.kX8|ZQ XhwŰ\ ldj0*7 8 n<9B4|/^NfZ]n>o(z^!tU6pJ=+( v>*.ub1@yTVawaE/P{ޡ9y$+|}CG+I-rNZǽ>o(z^!tfp,ʟ `Glo*!~Ea҆[OpE2N`LBݲb a ӠYzz^*Pj"1 q$H"Q[r0 Oi"Dg6 7:234QIoe`:Bȶq5P M+v0Luݿw}MwG%:0d@WH n`2a).Br `nxA'_< Rm4&ZW MnO(ڤre9Y2yMBS~:=4)J @`+b^fɻ-uVqIzru  z^k2tG2@mMfGU`^#a/QBCQ% 6 We[4gb"u{ϛG%:0d@WH|:|+:#XbC4z^g(GPxGvu~ ."ZAW|SD{~ C,T^ {'!+U q[8ڴQ#>/5l(i0I{@le2١ۑ>/ ~x3@8ND}{ @J߃J mhP!߶ e =8s@AU2d7Q=G/3:D;>g Cr?h_C0)Q>7Q=G/3:D;>g Cr?h_C0)Q>7Q=G/3:D;>g Cr?h_C0)Q>7Q=G/3:!dT(Q` Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,X/ JX2)塒EWJztӧN:tӧN:tӧN:tӧN:tӧN:tӧN:tq H%p4\qExpÇ8pÇ8pÇ8pÇ8pÇ8pÇ8p&!`U%emzVյڶ[V׫jm[^kյmzVյڶ[V׫jm[^kյmzVյڶ[V׫jm[^kյmzVյڶ[V׫jm[^kյmzVյڶ[V׫jm[^kյmzVյڶ[V׫jm[^kյmzVblrv[JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ @&r + ! cjw}UWwڮ]jw}UWwڮ]jw}UWwڮ]jw}UWwڮ]jw}UWwڮ]jw}UWwڮ]jeLK!hN,k[w={5ksٮ]fw={5ksٮ]fw={5ksٮ]fw={5ksٮ]fw={5ksٮ]fw={5ksٮ]fw=26 [%[w}uwۮ]nw}uwۮ]nw}uwۮ]nw}uwۮ]nw}uwۮ]nw}uwۮ]nw}@B[7o{TS{ڦMj7o{TS{ڦMj7o{TS{ڦMj7o{TS{ڦMj7o{TS{ڦMj7o{TS{ڦMj7gق<_}cW#DG_\ʓug5)r8Ln*NyX1;_}cW#DG_\ʓug5)r8Ln*NyX1;_}cW#DG_\ʓug5)r8Ln*NyX1;_}cW#DG_\ʓug5)r8Ln*NxP]mRXVffl=hG0 4tk~NYȷzC`B#d{ޱTAֺ]YR±2Rȯ+"ȯ+"ȯ+""8$ZtGEn.`t9GZhߛ>j̯"((?OR&7U^rBu1'Q;@%ivqc|*hFm+Қ<O! Wrp}hEu rI-)6?75;탺{EC)n":>R'f?BږB3x?G-Eɲ G"pEf}_Gr1H7i(%*$+unzkOK f. j{ܖr 2ꪫ+ml&xX 2IKq u W^`I oHhsmٓ zP:ݘI6mXm˅$+oaq0_EZ g`;U `C= Vd5L!-0; F4 6JJmg;φX1ҥ@]B%)se'0Z:rc{v]@":4^ nUJKM $X]Xcq/LB ~$0mKz8a--H%(!'w0Nm7 pFpm;7Q 0+ 9ysջL^̓MX951s07omx%aD~.)2pG;v`mNдM T {KbCzCs!b=LnwYu` T/A6tAۻVb-ޅɚctC ,V^ScF A2Zi7JtAvĥ(' t qs摵 @yfMH0;ʓ,Y(q e%Ӄ],8ջ.M `V}SQ5y("2#|gjk{$3ns_kf 1rXHϘ|LnwɈ%M--b]Rx?epщF>>xMYqS'7ǰ}9#x+#~d\001N巨5&dfAL(7*+wGTmUH4!^Hᄸd *J |;Ǡ\s@X }_G!d$M&X @ bq9VHaqg9 cpsVhPL*YWjŤir/@ u_"yh9A0` (ڹz U Yng(pk.6 a.3XdAnbV;^@ '*KXw .B40杇$'>%*[+ 09$1ޱM 1Dr[] ZuJ c@6%i3]Ԕ;CUUT L2%GlUԞn̏::f p^ ġFi䴦˕PiBgPֶ/TzȐz2B\iPDx y@u.dƓ]8'XCb$.1Xu`[P#4?TD$8rD8'wOοfPh 3<2%X1-qEu}E:p9A;]6%Z^DS:Ʈ˂N:1 Ǵ,ܙFAT;%lNg և[n1d1HPl@v.NDB(*.^ S`#-bvŌﰌ/2w`w1$_WC2.YE=@`,v$:P•2K7{=4 , .ӆ$.` d%8P+dZYn6kH0F~80k.YoyDݳo*@X#K` ;Hi1k~]1;W^ef%0 L ` al-7~laFXjK$(7 e}xp(pӴbȉBK)>j\,ًG!'b٬0j/I2&9 ׁl!gCn Br|WJC!OQ/!D*lh9P^\5~b b(#2|H4q'imt A;2 a˶pwzn~1l*$GMBdSXjɴ!hy͂cN@Wpd5#_dshΕmx)l잛+=h[l0yP*n ݡ8)3׀l>bzw:QE3[>ekf,J7лp-`U{:18q{==\h,4[9[z,MQa&F0*C& 9;s =WUk] [GJ/,d[%.Ws톝%V꺽pTF3<3Kb$f.U97p߲-Z/pH_2hZreX&.(u-[Y d6 |nhb\Y!*CRXXP vM܀B?>&`|**}՟ : ky7=W p ww>;οV\j3LUpu}>؉D :1!;Ȇ/ xБ\nQ U2wαrj@ l4:mm;{Zevy%Ņ=y>4p#VlA$i  ,rы]*Jq1 bjqY? ,`LnwnAՖL.n+7_rԾ/~ }=2h%-3w2S$C0g'-gM\NԪLưXsBPʫu^>vLL…9MN: ;VuڏR8٨zOnƆ\̌G{5x'?!֡9==i;YwX-ͤ[kXv 6DlkH]]NDc5\`DqA`cu[.*+spy9BKc\h0",FȔx!I,$.3s+JI3 w 1[%㷫lpo'xY_G  {r[ W"/g()<;0bV8K*7I֐b)byہ;α9tcsdIFId8""un0Ӱ9IO23x4j`-6cu[.Z*I)*P !KN4NHN|)1&;ocv$x+,S[Upnr\xDA}#{_gu}$s: `f\2` ,o&q!%F.i9N ;NjN~Clc} xd hkD (&6G`E%$ a"GaDNe 1`7(yf}Kq0Gl %噚K귋]:ޘ9>s7㌶p1f#1:_%݃څIS>k_ ]qöԥVü_OO\*E>_gu}7'|)!UW)XA~y5x'?!Vĵϫ^ [̡l!GfSة,$P0F$'jm X1S(}Dn4C[&7UbPFA) 1K'i_{,r#DY1Kޚ!Vݵgǔx'f` M,Uv e%2['Be12wuv%yk@4~: ;tsOU$s$'ÐH?x-Av~α9 *##&%hj:Xvaq3Ϡ($ AšؙK٢,Rq1+uc#䈸wHX(Z/F3j>*qҋBZ)AW_v*3M$f[\h I!6 ?h J7}n0jq3>-٭>uwW0]zטmJ&;-xnLj e]kS P{!*2y5x'?!V7gՇ$pHS{QD8y`vI.^P#:lj0 b5P ]p~ f$ш`ǣ'xY_GHD"MrrDKRbdx&LVAA")9jJ$s8%$Q56tLkP_"]bGkszr6 ZBW5x'.Ybx-۶B|ɧA`t߁nݱqBOpm۴-^o٩ rw֌t7N=WcXpl(eЃ}ZZR6&p8$1E)bxY<jMFrVd '*Oأ§u2hzWzPFHI!+H<5% ۷nݻv۶ۂ\,d𧩀@F\A 8h\w_ N;5IY~7$1r2$HQl/G_\ʓug5)r8Ln*NyX1;_}cW#DG_\ʓug5)r8Ln*NyX1;_}cW#DG_\ʓug5)r8Ln*NyX1;_}cW#DG_\ʓug5)r8Ln*NyX185pڳB 诽h>h>h>h>h>h>h>h>h>h>h>h>h;rP&zt;jg_iWU}_iWU}_iWU}_iWU}_iWU}_iWU}_iWU}_iWU}_iWU}_iWU}_iWU}_iWU}_iWU8^DoKs ξ揹h>揹h>揹h>揹h>揹h>揹h>揹h>揹h>揹h>揹h>揹h>揹h>揹@XnSѵʾ֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏hZ>֏(!!64|,AY@կpappl-1.0.3/testsuite/landscape-color.png000066400000000000000000002514651403603036100204450ustar00rootroot00000000000000PNG  IHDR+elgAMA a cHRMz&u0`:pQ<bKGD pHYs.#.#x?vorNTϢwIDATxwW^B f]RJ):UmVQE)UjUڪJgE;!"{'h{y>Grs{|>^{3<3cƌԖɓ'['N֭K,ѥK5<xPۯj{G~tvU]׵kk6(P׮],袷~ZE1cFg}V!b4PJNgG߾} @@U?1u<%~|K)\ <@?čԆ=-+(PqOdD7^CgL  p*PՖXbg7 T[lε]jGޞ]:`\{UwP3:P#]7[ (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((^[%yױcu{ޭ[Zə4Swѣ huLsҾcwJi)m'IcCNȤ146dLiT- ̙]YuլJVY%gӮ\3vl^z)ÆeذR^x!/@e"fUY_z-ج@bkyɼD^{2*49 { lpu]b%(Rz묵GRKU+l 63ロAr]80C@ Zeղ&Yj,jX%wK.=G$Ya@6GGQć2UW:iF^uK}@ ~vmĈyW>j oSv9o+"dウI2thn-__t)~xY>nU[Ow W+7n8!ҽCrUu_Znɕw* /~0sLZV;g睳Zu7+WO˟.07Zexۂke1d*\1f}-]cc?7|# 3cƌ]tR-H׮9Yvٖu`lM6gsVgX5h,T>ΥkfS=<|}sCA}@P!#Fm~aZJϞ- {s9Lcl>8}Wn:lUȾ 'Ȭ.&Тv2@PZ+pXu\vY^{-G٢)W^66+m%*>s|ŬK 9f`8ҝwy[lŻᆱ@0 wܑg~Uh-p. < 6p%-n[jv:"{-L!'K-)7fL # %6mp衇Κ5K5f¹<@٦O{o89"s#p%Uo>C.K\j=ZNA") ey뭷6tK/T)fֶm~ W۵[ӻ u{xn~B6حOUl_~LVk4߹٠@,Dݒ@P{guyGhf_R|0gkv!O=\d}:fW!58awdJյyMe &nI (gV[5J)ԪU~9GXNSzk6R,J|ds%nW,wzG@sZd NܶcoYdj7)3XLKB@P$̗)S^? 47ΓOf /` ԈDeM(W(ј1j@"n khfA?I"2@-^.>T>MzywR X|2T\@P$̣;suyghNm}{ȱfj+ç闯 @Nw @$- SNnƎ@s3ߞSOno .Hv*χdeA>mge(ʏ.;F'nI ;&M}ݏ>&{tͪ_ VXn|{`!):d_)P)P /o_o5Xt,=ӭ2@-x<7e *0ޟ/R&Mʌ@@0n[W Ex~ P#|hƽ k.[ |: @%A=@ŚN<į~ƍS 9\p2|B )ԂI^p|?se*DA=/0a„]v㏷8PfK콷2@xw2|G,2fĘbe*gcm1t]veСJPfeCYqEŘo}+g P#.QV21fLx?ϔi1stO>- kma<2v2cLg[w  le3dHznޛkyټnƏ4):W,@]4l>RZka`Ҙ\ŷvR{KKbҤ\}un%gԹa2lX.$${oJ \㬺UX|}kj13e\]AYkjV `553qJV^ѡC:wn'޵O{we_~Y)seҗrŷK9\~ysaη?)W^PY\2 SOQsfƆitjԔo}+?^@SCfݲ4݄ {Ò!3/SrorGyXz-j,s1>/Sf<$3 3foV)Z0eJv)3-|'RnBؘ}CU&k\@=֍ϟُ0 xQ~|9&c̍'WN݌`Nqk @3%|z3ϬwyRcĈS&OoljEesi( S.rskӪq;3|Ur2vDsͿ͘wJ北bϽ @s2z&k\s̀@y|[szseMe{,=ҧA5/?o|Q_i;g?SrQbEf~f,MCCƍ+q -n8∽kʔ)ԦnQsWCCN<1kh2qbiD@ 61|'MM537WhAw_3rQ|o)МƎ-~ou=NQF6ۜq? .@Ezj. ײf9̜<{pql\}tfw4;m{C7*[iU* ;O>:3hРJvzᇟuYӦMS:(d5g' Xz@@ bK}y{Ox>jT :'/W\qFmoT#8SNu J1#W^O}~e„?{Ic7@ْ2q hge/vaocc9OO2k֬Wh{;πʖrcYL 3ʜ+>C[ A9%- #v[{mݶ{=p}k_Va'rZ~%Oy .U@QzuM7uڵ6wi!C,첟@|,42G|n1fR{AaV6X᣿̧^ҡR$ujUV iUV7ܽ{2s >O Ę1;Vf&eSǎ^"+n\V8S7._TXk/_Ϙ̏1cjYA@埏]~Gys@|7/w_Tclgh[oٳ̙3U`vܱx@J5[\V㷜77?ZXk{ ֫''K Yy/Vs.]\wu'|rs7xf̘%6>kV~XJ;Y?g*0K k[~|*L@$u]dWeY桇׿>]0{_L@Emg6߯M;dܔZ;a,l>?E H ȯs`뭷~V]uyk+{ Sc]!߻_/_@ Χ km~gOq/  U>Ժu+r{r)mڴ́oۯ.SYk9tQJQF)vmr!io4O ƍ˰aLǔ)}ƌt޽?X`޹E)$|]7`'瓝;wsl7ҿmo՟şo4ؘˤ6>kYgcƔl۶Yp$y<|^z)C~Qfǎ;gͲYwݴoRpU>_~nԔ_~7tk1-ڴǗקA Kuj $?s 9v09femE?mZy'3JΝ3`@6|҄ݮ>Y၆wN픵/F?3uj9vX>8ݻ) Ѻuvک6́}˗9礩It얅J5,._Ǐʯ[Ϲ@nɐW kr/indS n<2CaOW/&B́؂ Sb\xJC]5}@G[,TϮ 4){7'lw^t1$ϼ_Lֹth_f0?3vl 99gϥs90cS^..(ǫ4Ե~NNN2#@߹11-_&ǸqiݽdMy s923NT9wk]̚Vil~__^/˯,0P-Yw-̈.8'B$IK+曊 TvV\7ェ}%Z%c9b 0?ƌ3ᆲfv͙o^% i*_eʔ{bUiȜm.IfMW Cfds3eL!V/)C=́Q䤓)$o^z/ga+P)?]5K%O:Na-HNs.`~U1I "]7M齼nN;M*=]3*)VXkNs- ̇|!;Z:#6)-xn9:g$3s3>i"3nMVoihtyWS|3;!CF([N,Xg*|5WXko2u  z']ziY^Mˁ hFGn΃w=to u}Ne:|藿̰a DMMy}?rq45c;:_,dzL?3qb&MĉjyŕrY98H `>j/.T^neLKEs\sln>5iR%ܕ)綾Nierr䑹/\z鬱FV[-.eKgEҪ\8vl{.>џC+@ ̇s~w?^ՁzԺMyJQ~GfLU ]Qd8;@}w~qVPu~{s}9 uWWy!Y*]wI } `p@ 5sfy-˴IyS'G)H:nǹ Puz=w[?{lȵצ} u)y@{\tQ1#oVux|Hh//T|VM]"3fes2U>zw͹_(.TMw<ӕ.H `W.<+{sKnEՁ1cjK~YN&cީ\me_+?T3o=0n6ؠB7#Gw]@7riSWNN;UJ+?]5_U7fXE* y977VW"jsϭлFV P0eJvr@u<6;g핆cغu+r{7jT6\ԦYsѕhZc.}&yһ@ |v*?f6$O=@zvPF^z/ .ZRYlr S@'lMn1:TC6LՁZԘ/DGoZzr7NH ߾|s:vD_Ox.$*{Dj\$Խ6˭VWg]2ueUn:!mItR??쓙3U;@T)Sm_Q'$Ա6kJu)9J M(ӱJ %uB@0 g#G+9P:Tdiő@PJ@=ZqGn٦zh?_Ɂ֣O%zERvQ>Ϻ;ӽ{͚o;W^@L1cJbո.H 3k+=z7r-JPMViBL+.H 'kJϞx4H碕ewߝ3b.H kJcfJ$=c/(w~Yl1e je,@^:<YkjUz/3A QvaD@PVY%w;z|+6L>֎e* P_̫ۅM Gk%ƭr Bo:lX6D)m;dT^UlBWw!jJ+eРSzGO?M7͛o*9l[:vDG"MMӟ_T(~ G@Xj}w%v:rzc-1-+ܔnͤZʱn-^I+2 Ch ɐ!9,GQĆMf;_\Ze "&,DN~wsӵkjL{W@k1uvJcC^7ܔGoo5!c95}|OaDWr9,`v1;N N1"矟Nˌ=7ΒKE@7wߝeD_\]}ϨѺG"C^y4ܔ7ܶ;emaY J.eSlaZ룏O}6-7ߜ/]ww]#-xβ˪P[7˭o&3g2>z> 3 KլM:4O+<n A>gnY}VJy}wڴy߁Oe{4f6n:}+eJ?ˮ6-V_@81 ;f啳+ ,3iR&Lc<|z*/YZIydX;"dରJ|v>IffLIc3iG:1mۧCt욎5{deӮCK]zI>}7JT?!:(C=jTS5<2|;W:%MSS9VZNrP$@锼p2|G~~I =xeh]6?25 P=ƏY{A%ZΝs6wJ46{gHhڷύ7fwJpb -\_6* /V @"ͿISJp~WH[%Z姹*yd~3ecjȽW@SO@-)9sh&N]µi('n/Z eyo9ky̜^;mRu~lP˝wfmӪU͞vs9̚ncslG6'+n\wGeП?d{.;P:[o^Yg+2mZWsQYc 9J-Ԕqxa~i~L9.#^S{aD9FԞsy뭜xbYϥs|;:4fx￞Oʍ'e2`,KmD}%ܜ7vU9zkn5Z5ߦM*{]wM׮.&HG#Iұ[aV47Ͳ]}ß#7ћs!Poڵ˖[f-?"z( -h7 =jDWQ,}3 Ct]>?;L̜Y32szfM6)Q0ꣿQ3̜lųa?ky݌2mZOcƌ45}ۧCt옎ӡC:w gEYd2Wɩ ԁ gy9K4e eh[OV93 !Z)-4 (TvҮ2Vpxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxv}뮻SN{V 9a6q8ߒ}Sn<@?MKCڧwW!n\g)^d:@ZOm۩@f$qdR1Lz$UrДmҪbTԤiyyd^y7L4?'MˌY>];[tnҵcΊooEA ؘq#G~VŇ)>%3~)5*mۥmO|tHңOzIҽOzIYpIQnXx24a\2]=%͸ZSļ9%oLSƔ 2+S2!MIIv.=ڥghһ}V鑵zei `=V?gf؈7/tϊeųJKiFi[2 zj^g2|vf}+M5әg%ҵ+Uii Sߴr $<;eUv,I Ic#y呼<$/ɤ*}'gcX8A36ew+:ScO$&&oKY_se6vF127;Ԇј'qW|o\2/fCyxXv;##?>Zr;057=IΡdөrH Ȑ3䆼`> @3@]\\RytWaN{ cʌ|xnyԅj.s9o/76Ǫ@3l|=̛IϓՓ]B3sdV3?}:[v\|;0A;l;N%fotQFOl9|Tvz8/fh'`~bN!G,4OɗK<-o&#\N5'6jrsihr:yfrřըHJ@0œ_gY HJT\\>MrޫwI/fS\F2oΦ̥;ѱ>Y$̣oOVOKzɆ#5tF'&\]oWuU| wVszB92Ћ[sI/b$̵w_o˙_o)@s"&Wv^MMiC#P'@~TfXZoVΨ *񙎽&'PݧؔC !`}i~jC%Z_'&5<'upy+ u)G<_<[S'uLopy:#.K}UN&~$]KvI+@5#>$~:8gkt0[ Mќ1OwCfB@K\qJ|c)74%߹ uK'`_m-éWvdWo62?BhI=u̓ۃ52r.o$|ȵ3!h]gp}Cs?ǿ7,! {>)'y|<|뜼\ _`%9aˌ{W%Z:|z7sԳuq7=ܦL^ggMcb|IӲ2ve3){>3f%@y4|' 3UeLqA5GS?/N~f{pLyP%؁g?G^qK"3{E.N"h9O^ӟb@-83 -{57C3sdyS%UXw:kЗB=pM?MÒ &W Ps{*rS\Snv*&NϮ=Nl<|}46@KuPKnz'^>fFyP!e3sS'\c|?cv '=q{+nnZ{U!INr*@;#{+p͋\J̩Ywg}<>/Ozu]V :p@8ŌQE yp%퉬wT[%/[ )9M33yJԬ6w+IsmH2 Y:Y LH$c1ɋɐd|sՋɠd+#s+ʐ|ѼMz()'ݐ_^k她3 ~N}+QP  LjْCoe*t]{8NYɟ*{H \7*C<)~- 0%[5N{]q>W΋|kZQnL2YhtӡswN%;afNȔ2>S2iLw_ǖ@uX*Vd9IG=m:;FB5{rl~cY}5flH/Pd3tJ̵^]4CfE"Y~,-o#3ldx?}|16WܛM($yghq$Jfō tz/67HgȈsy|z. O7uϗcee*jdE:Zr&ll\ y y$gQݳb~>IfcSޛ=*s9-x9XЀh='ļ]e'@J0GUHٽd z6_[=O{|\@ƆLlPyoEYr,ǟ15メs偺aj醊s|Gedp|ؽm~bD:/h*vʢ9`=%}3&V O/ wu9Go\w;_#-y_ӡ]_+ۯqThGFGF@ןl~;tɀ=w +oN ${|Pmڌ1%6u,iH]\+$_M|Vu*DdߥrjY\|W9rB~34'P̩/0M>mOļ;̪=_d[vY/;^RYt"(M ;ҝ.^N63\[騬tT WܾlfV Nϗ ܚlW摿`p@>iV3<~{9h}$Mh,0Vv9=/1WW蚻˪K7nCM69Wgsk/KYջkͤ1n!w5%z߷2\t(NɥI2PS*]s?ǚWe*qSWk? ?̯ҽS%W;[o6%{ $_7\z˭>7Rl3ь\b+PJ}}nU[ Qz-,\V Xd >5d1>J% swqiۦW7/=wCQ3o=Wn[)6vfZ; ڀڤ2߬"i(àZC\.z,i-f1G%{)BzSvc@͞G:/2I ?(vT L+Pʻj,TS]Yؔ'ƕ@Od׾ٹ7Y fklsRGE;Y[0.Nf);.@ۡ#V3|6UbosPVPcrJkOҪRgHi-/`@U:1f~6ٻR_f-!c2t!kY zcvͬ.-W/\4eT?LQQrklsdvq/{pZWfr%+xJkY1嶿ѮBҷs~ٿ.!lt\]Nj7IOe'lRb=:guJlVcy?LQwƎWҦ2̑J~w̲1TSgrۿ?~qNfpݱ[~zC:vQc905/f:,Pʛ1.rM5.̸_s6쳟ג%455$=QO3'k~ l?,Y= t-} |@ԗsyYur\BJlP#L{9Iiޤ!hJo`){~-13x#ҥC;s|*oh&_^'#~`ƼSJomU`9ԘagB[j 5Cri)]Z& My̝< 'v[fbd.=iKOv'z+嶿*8|lļ@ԗ+]QZMslĭRpC )o_UK PUFNˬ2ZW׷ظc9kvѡm?37)\?M[|Gw3Pm6xvyjzUeJ*9g)fmD嶿ҕ;R$O 79&9?ioa@*u@ДϿ#j+SNfLZQf*YٕyH 34Kx0Yq9Ɔ 1L ;#.M6Ynm.˭-Ѩ3_bt\X>,XI6LH~,\[锜c@5+uĻӚޝ۟4/гKny68:?hй}.>8{n4b,=mT[ |I } j|LKؐa_ryAhqJd1ԓ1s\dl!0GvY/$[~5l΁:#\Ans^[5zB{gh sr=vʏݒ''w_Chf~_n+P%$߬x?5UW>I{6ϩ]Vs=ra>cKW9Y4-];Qs@Ծ2@,K0rZX956K_dDh6S̛W~ ui8 @*u$m61O+@k:;Sdڝ1vr@$KsCm֌zfeŹ,!FPwȄJlj鳥269/9;iSPr]rMۅ֥mVQz/aKbDJii<9E@e({Z]iyal*af>Irw2uBgc(Ceށ.%Vfm I֮C]!9>YeaE:]zx,E5Th8@N :#eFl><󯪯}ͽ5Lr_iQϵbPr\ ^Orn|ufJv4B45崡I@e(6yuhWu9~ղ$w_7H4ռ}}gYS9.u@| ;ɱ+!I[ R]XL7 ) w/-,Ϙ[ eM3=vݯb.VƿWVˍ 5Ris7q(gɾ{Ӣ ̱[!})7@c })z7 bVaP*LH j[Is j+C2Be1}t^.V8L||K:'U_$]W?\F4%Weܓ/pҬ:"?x"3Ḉ0y2ؙ+=ײ@ԘU+׫aJ(Vf`\4\6Viԛe櫋}iQ8*?~B鐅:d&cfd􌚖la @/s 1F˓gT/ S`NMH~Lk92/9?CkuW:ק)T.0e({0j0n;K ^fy,2sdԖ?,uf>Y2A ProW˞_ Msفy9e]Sc^:.AZ>ߘddމҔ\<B=j0@ڕ:Nfm,y%1bZ6kz;VYd\$J>+3d}J5ߏg砋`%~.Y; |&&#_K6MJ~6Kg2zJ|>e'ʹNs;y:wԫj1Oz.վնj_Iυ?gfyyy3kyJ/hm~iS0)9"SHCrRrGre c|ofJ|>(^_.; h~{E}Xls ^+?9%yC紜lN}"+oQrM]5x|P?Vf3 O֭?KL<1zpėD'eb @1oYyH jY&ˬ=>uwNf5-Yo/F 0/Ynln ֐d@R{o tj+3(CٯK H"m͵D679ѬmjrSY} /g(;^Ȉ )30;7&[$5dd+ 5mr5r.-e@e0e֭H jÝH]yߒuv̝ag%en},eZ?Fɛ7ԴVz TYl`RƔ@%(iʹEԒ\@Բ'+lӞʛs!?>ZSx'6#F -#e/#8 ']u]m[ӭ_6֭26һabj3%SWwjUx~k"4ަm4 ,^Y̕WMc{go)3i&Sl˵ڵlq*>H.I~ O {OVP gɯO9;ZV9,cH%vѹ{s~'2fىyrX{iIO59HMkRdk~k){D%n Ʉtt`;k7<EYL5VZRsQEY fJJjc!姾MHn>{^'O޵W9?J`YOЪm'FTףy@ m{bɎXJѫ䀳@fK S2`9eKpW,oYo@_+1 ]&%# vrஹsܼnV{z.[+s%6S݄ʾҼXQe$Yk f;eJN &J yuf阥HfEd"Z5WRe'w"PX4 MX2m}I<=9g߯M ke~~fJ w4v5+;Xif;P3ω>Mz%^QnK* $InIQr;59CrqRfKf(PR圲r\uf sʸS@eاnjoztM;,XÕ+.L7ۡiؾ0/3gFr;@{&'_(o%w$S_H5PfthWK[/fݧgCQ_έg؋r{9@*3x9>;~R&O/%1jĦ5k,d=5u_%\M~[nn+4$;$OV-ak%d'jIφtZ:3f斷r[u2O/}ün e%k 2@)ӡGFɱbI ZKlsϴkߜGש{.̒'9@e>EWg'|+&H6O]f/$'k1P{g>ٰO~J^[c3vZN;2vZMtz5dYKdE>VK2eV~\<(O,ӷGTzۨYPleluVb׬iyrB\]Xf_K*d2{9;|wy7'N(`FڷI66ڦSto(lKfʬj=s%+u D7*@^c,( Dkַb̙Ӝ(u 0sysyYnSn*so+x, iu䌤q|Z,ژEnvtR]Ͻ>@v\#z~L[DݚYgʔw*Y3 SsgEY* |2?ae?#ՠof6bU]^xG4B6$YǮRf,׼*15>uK&<]nK7m;4M7qD{$_- <;!/O*5{6@:5o/{EU $ f\[A4תּ&O(>mmOR^qT݊6hfr4k2VXb^1{v^,s͛ Lʕĸ7&Pb ;wۧ>JRҋ.ʹ?M/+Pq3fJiyEUՖ*ٕ;2o%&@rxyhbF.)Y&If=C| rTܟ_˛%#xƬc P& ~_/&@rЦy& M|!o\]n=LUNfzWZ:@ŝ9f_52?^?{׫OfVH x̚\nv* |ڸrT-={TMcrRZޡ[0UꍘB&OQ<8*hȀ^zSZ7xsP/?Rfu2sr^8.tSiWu]ciDṪ3Oi)PY .3s8۟(Ֆ2RF@z0oz[koyL+KT%X5XҞ[TaO)ܧ}_Dh-];QbFJH Z Uݟz/a3+,Ti`nJJ fWcT}ץmvq &v̚˖~W1RFQ>+[S?eje?O#*F 7&鶒JvuMHI jMrݾ6.9)o1 uϟUsK}'m;40oKkf=%Jk]255ߓ_Tn/v6hN_]#˼sZыagΑ^1:Vt]$oUnٻd̲\ m iS+(6Y* |l[139 Y;:fԻG'Ь:vkY7E67}m󵵍!!Yz#oɜ^N_Y3}{e/)P^UfO+~ۦӒ* |R`f:l9C(ά99kT6;cZ='@}r۟<=]R[c(&ɢ !!m}JKsɏ nslmlV~ {܋{yfBzƒYUM}Q'խ9tnއ䄌SCm߶;SOk?(}?A^蝺}5'9OQܥM ͞Q(Pi`>tN*GM9-gJeKX1 =w\]U"n^miko4ar8)OVjlthP,$ DmYtlwzJnu|ƴ\}b\1Bxo]{cjWe+vѸX|]i]+Pr//'%#YA4Od\FTWLF'"-תDG'^S.̙??gKƁ[B>}耚w>oPi`lR^IVINKfdݼfˠn<>>fUfoo'3 -?W ]r g:}B]PV9$;|Ȭzhv>+ϿU*Ah>(WPG=QvYo X)=ͧތiy<ۂIDAT!ܓY3*]UH]oߞE}ctod;\,$)j}O;*Uh5Hۺ,%_U nYCOѓN x;_Ng2>$w jKeپYo3f\Fc2jL|*zu8ԆŐ@Ԣmmw+ɹOOIRW}ӥW̘&[d\@M%vIETXJNTw&%K%IL5wK%UV=}h-f\Wi/t2ӭ\~umu2+geLџMI=9/OI;Ur7'Rm[?fu纇?Jj֬xbPW6Ԝ]QGθ73j)*fԮqJeUX@MHEHrrtrl24n<_%_̚. ^LcsVuP/vn*YctK'ݒvdB2=$_dchQV_QjwY~z \$($Fk]}~[uwR)ԮI+JeЁ* 4ɏ1LK$&#[Gw;~Hrh@Zmr@[{PaYJv@Q$k-l~Dcj dNouZ&m@ll H*-G}r B[wN~Qv=%K*> ] Je@9< @ Q\04;/@ѧ[Nع}ۜmgX^ԴriՍyQ5f%ϴ]3г`${ @ha,uWl_XZwu^-x"jڬiy{YiEsf*|9 T@msX;9/{Fs]mzs򡦽zY)eB\ \W8eh2-ՠ~h]sNA ٸw2ljڜ9uFJ:/@HNQ$I]riF&@Pr_A%ma9`V~wK!57xlws3{tJ98*$'[tkau<=ÖkGwq\ i1ߘ\e||gsEU* d>6E5VJk}Zm88?.O4v7eZ3ri֩Zރy{YJx~I|\i-g,E%V_u\.ҪjӶ[WvrY~Vxhkl#oIcg'Ȩ_EE3`J6*nuZ^M]N>FK]fޭp_1΍Nl$_s߲nZ9k~u,1%i6ER#Z,7YωڽMrĊ*fcӿg? # `.;g?eбK]}v8&\z$g%Kϵ!@L,sdnFBwZ}gՁ-Z=7NJ@0OSGfzb9|9@LˀӸbr2*ٯ.ܕ\r*VT"Y+O N̷6lyM}N5=,ݸB$|+f95^;PJ2WҚjHIH6t^c\04Ԟ xih9;,3w÷W{ؘCKZcHM9]qO̜Uz/O/$%$`ֺɣwզ.;ψsFYB-}ow|VVӲz*M|Y35޲LzL{^@sKJLڶCX4Mro2U6oٌ"WdI5c=ӣS>s,< M +SK]ۆ 8kn_IN0_.J;PdddLr]22y$y:Q>Y5Y3Y#٬U 2=fϬ{Wr:7ƒ)?:n;_[so]YlJvY7}SUEAè]V*oSȼHF?dVl{.e2kϠ q4KX<<<<$WΰUʉgqYaݬG1-=я浧3i\&>?Ϟ$m! !ҽoz/Kd%{,6(=)-@ےՓՓ>nV\T^A2>`n$O&k䋉d dNm$7=O1%M3nzMY<+SfeʬLYs>B]ҡM:I6?wnd,1Kv̒hcum P ޛdMd(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((^N;tW?4tH^ld|M4‖l̙ h$sڤW-|Ys65WgZ&f|}vRX&MRX*ŪU(V}.]T(VT(V}NT(VyKY{mZo̔)szdZGno hܘYS251-ej2)C2-{ypJ^߯;X{/ l}v2-o7q@>ח)h/S3wzeMq@K`z߯+ P8 P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< P< PJ5dL0SkB|_3';~u[4]{+Pۥ_,=ҡ!]:4m̚ SČ'fܤSU*Dԉyd^{*>ןɇf֌nS[> JA|N TR]L߬DV^"IYgLMim̌Q 7͍? ee؎iɁϵ&9ixR=(mH^U^4Gmϣ/s?Nw=7کD?o53uS/zZӳ~PdhfNυWL;/-/V{<{_.> ~3[L0/ msMT"_dEqTV[*?-ȉ3sl{X;;ogؠ.Ba,+oI> FCr7r9Lw't_:ÆDs<8' PrT{SBH ZN;孷TBzSƻ>'ןWh7}J91w\4J}B<(}nnnn[Qt>?䚇kHu͕Wo_hNmKبPAW{MNqD!ꕠ9lwZ[q>eԃ9iJoU2ʊȾck7~Xc?K/=enj~~7w%~h~GC پ9ڴKǮ:5/V EVhn/Ο=6.Ӆx~v99:'.=7_oN5;һ?8#Oͥ8ڛsysOh~cQ(Ru^6)SbH Z^;UBz|ج嗹LZhFxh;?ĝD5zꃳƲx}؝&i۪VWon]ߟ0䩥m@:kͯ!\vT k@:Eq{2twZgCx\zPGnʡfԃN2cyj-rywV?oGb2m#ANYC$GQ>;ÇgXPdhn>;W_dΜ|N~Zkc=orl;~>jY>㿞.jU}N%WiUaذt3نxv\ŦL?A%ZS^W}-}+OY~rN:5C6vi:47{gk;6]-R_saXj.Z!MC%\rIڴQ (@Co5EQ^y%mGU EVWrv#7=2Z9?!l,;Ֆpp.'K/jdlF !heےڬJTSNɠAd6K SU qE;rPGfGԭk(~Sm=!yj-KΝNJ>h_euo3rla_Cԑ](qF6,ᆱ g/I2I=9S?cj~@8dvtzwc\!)TZvHn-{D%_:!9t5<# j2yrv%ٳC69g?9$ɳǫ噻kw5! tl=jz}.{ rYEeos9ߪt9#*,9a=ՀbªK/es* RZ;>vE9~'w媟*-}Iy?.4>m\thP Zt|0;U_{+,>K)nެ(p:4O< cy՗$=+Q~̜_2oG[ңJTs+4ݶG[y(f5qbv)G9sC:1gˏm9r˷7Q6)IN];Cs5x~]Pt2|O [Nk6o~jsX9|:T V *gvyYPdhx6o7]}MteZ[?djB\{hVX\%g!YG͞q23!Lؐ.1];dӭ* y`qAo+k *Q. =z(,~+k)I@J|0A%ZϹW=ߘ#Y*K}ӱ[:vKc̜2Q#2alG3?0z|uh./];{bpX-{{^̚Y߿X|a`d-/횧 [0.t;ɖ[*,_~)A$=;NP Ey]y\v-kr⦙95tȐY},zΟoʳ?;8!bP rN9fGh+ޓolP@S=|<XXtg+]Usc&N5.>cQ` >;Mq  $(}tJ(2B痻W_ ^?{&Yb.Yq:,~3̞YjнcZ]%gY~ylpo.'/]3ɹ\pG6[ћke~AAso?fTEKձ{cV7-2mrԉN~$ezl]^%Zמ-WB[y\wj-[> |saWB~l[ƾ\jƎNh,J`tĂԬٹ[w~&_84}5?1mTK hm\{m/z ~h#ezm30\vזz%(UWe0+2N#Q䬹} .V[~|t9|iʕ契haqH7?n嗷dfsoiLήg6pY3b4( 0혓G Son͑@`qDv)QdhWeG̝,M,F]]~,B:'ȪٱkSagxQA3=3)ʃҹQ1 vХy6o<-'_%g;{lW7슄0h>+LΊ2i>;NjzJ7cI (0뮹VPdh&w㮾Bߐ#o4}[/6"9~xF=XzM^|ظzvegUULw55;ݙ̛,߯w`,[\vY6\%Zk+|=eYWBaK"A@'Wdh^y"!~(獁~ ii.ÿ)7~9&=ҿ2gqA%D>EP[7̐o-~SKkJ~X W|KS?5GQ+k%[)2F"G,z~dA4`H/m:W͗Xz/?c *f/*Q/+u{cͩiylJ*=KDKҮ].$۫|/휓̢ԷIe5.5+]vɊ̞Kٻddkn]O;=iǹ!iRz/T@G.?0SN^Μ913\ʍTy*5gm$uTV_]s^qF*3έ+;wU/樣>HukwٙPKS\Pm" y ;hԩPCTeʷʌuo0]9YV٤.d S3|vz9il_vF>-w&(~[-#GfoghӾ}.$mE0oW˩#reLC-Mq6X?du+C>Z 0e"+2?unem7~$kkېNe:~c7H]O!L{%hgܐ/M @r]({~#G\`sYiyauZ,R)3rIPKS\ @4Ձߨ;GOO33k"+2oT:qz̚ٵ}uoL|9[<=Jsַr~N&1I֧'U콉Z潡$;K/t pI9$i6m8 M:9F>:+]ZCmki@P,/ꦛ*I'?sH-BǎgTVhOq+>jlܳ%IT\N*ݙg#?kHrMWƙ鮾$ó%~c:QHL`y%9OLկԭmLT|on~,\1M{lzPQZ3 Nqkkk)} ˵HT+/DGm䢋}5|Eٷ"W_}sQ7uwͶq?%Wyh|Ӌ9nySdܳ"۬|/>=YTÇ?(=waj:u/jLyS+i3s3%UoҦ Lە9Ԓiqǹ|TL4] SWWLkQ*2ו<͘g@EQ$ 礓e5ޫWn=n;)24Oʪ]}{'g͹]}"L+y 0Q.+>&'亇OTv/{Y緣򷿕6M6um~s&9,d1Mzk|0ItQWZˏcMq6s܅($X˲ŷ ?sךx"C3^}_%\}V h~(9P@~udvj'*׫sږ]O+0fg] %ofr߬Xp[m|֎;?XA+6gv5o0Žl\+ckבoŵHzȵצs7Wfe |EҹG JNgE(;賔SqXhyȬud^++k benzS LqnLXvCZ+/8Գg.n1#F|gk h&ejSrZy{S\()8.3µHQ!CT]]~\w|h?\} RCKϼD"@t=Ԙ x|W45gN*۞ ʭbY-?1V`*8=o}lm&LtYlb:5_;%kOWMqWӶ͔ϜjmL5KQvȑG.l#]kӟ~.T"CsC[諯K&_]ZqcJl|e9m)ܹPQvY(x~:'ۇ~{l|@,w.)|jyJϫsBOqS>/scYw@, DqN8!_r|2bܷD9E*'dՅ.G}.LoT 1-6ǟaG}.Ll@BYy1喅ߞϰaswz.03Es>k >&.75lN Se,Yk&oez(;ReNy(=sw쿢SI +͏d#vf;'bWoΚkg{~bݻ/>jJ9bͼ>S\MD]6Z%(T#׿{3Y:uϡ-N rtUQQc*ll{J}5g?y~]ŮRfe5gmxWO6den.˟Kn[A͘<>mR_sr~M&/צ8 @mr%Nr9/>]KwAWff#7ܤmTӯ~ctf" mUJ,է)Ӭ7{~vQGe߬s kE0Ԏןι=M3wGeNqmki,WJP ^:>wlJ>\>g`5%iqC9GM jb~fv~饬vI5w[cszǎ*Mmy\;S/3IS5@, D9?>[nY߰f92+}<_}W*ԑYW5U[0ӌ~g}mgy&o DX~wY&UP|goe<%;@|cs9Ϝ-ͫMjj y TOYs<\Cs)i|ESX3oʡ/g D5=;\Rnn4Cdߐ#.ˬMogUf5[9֍mV K.;|!ڬj-x"C?8]3vH|$k3oZ͞/a;.(3盿.IFvgljPlj1EwJ_ go?Fg>߬ړO?$Ԭןɯ?ƎetS} a,z74xV4IMΨy枼OiDDɶ.G$o#yg<>P.$C6Ω#b Dl]2S%}(']$w>?֩},>+YnzS۵9$ ZLk mk}<3{YV۶]CKе$y>!(*x|B7ܑMv.5P~ӬFڪg>P%,FVߪϭ:U`D%:*M8/yBmX, g!´;!۟I*ߎ禛j;Y17mKҡCa ~zFQrl^|8ܴP?k>-MC 8C6N榳r2l$嫯WRp>P=WТ>k ӽbKCu='7,gѥ1K.Uf`/2<@uos Eo@͏*gO9#93){vj;"ʄ.f5P 5Uh»av%Ҵz<΍ɯvW,;|{so?Ϳp2*9#ʞ{f筯@@WWN=ZquV?]hWH>ЊYQ~|v]ltҴz@|Z^0oQcIju~ڵ%Y3C}I SԷ]dxf.?*gb'Z*sϥy蚒͖4>Փfcf ħ'YuZ{' (CѹW ͶsЮ DqEV]ʻ{M-@>s䬛ymŠ7 Yc~xa͞o;S7K 5@#]qhlltW9sr73eB4t'*6-Nʏ.456KҶuV|p~Y,.j`G'u|$<mO)'W,G/[,J gr5q欳sù{7_b-=dyobzя kmΜ|;2e~h)n"_U `.:u66BZ] {qS\+kO8|)9mی{VX7RoE}T6=Qiee kr䂯GX]jfD.CI[+5g^Yz/ Sg/=f{(WvJt`ʹ캼8 PXkGh{Av9)cFWDa$@*Q{qTfyTuu9[yW9+ؠ$Psͯ~Ud][oUWt{@6[ Z5i|NryWVi5Ԁo}+_J w>@]4kFpLIҽo2,,ώ̘ӷͨ+݆{fTht˙ߏ΢ZrYE6x饹fuԉo̜ai @m=+g'oPwCgߨ:P;6GO.ggZ\xa4TWLMge.kb;ӧdj& GէC4vNct]%}Iū]zes EI ̙󿓇Pw琫Ӷ5e'9Qh]li @5R-7]Oͪkͱ/gcy剼b~)c_@O:Ȁ!Yb,1$KeתW&X(%{.P_=7{M}N5,"S,O/. 7+PF?Jee5>O2K2aa4.ޗg话zd3dԷi [>Ȼ M$q յO=,@-~L+!5S|쿿-w53llZ+ܝ/qyaɰAL* P8:7Y:1+:rnflp}{ $#O7ʹ? csy9h\vdNj_eS'鬁p %G-Kylt~w|OC4KcC}gniW/n~S޿;=S戀@@s=}5ta7d-Uhn{"+\!ZaPsr2"[Bˈr:hMnd2"g}}NYա!\6mme}x/?u]DX 59k̞Y;[2dcUZ+ȏ.P_[1=:)9TOfu(ŵT@4I[%VrΕ[:-_\:ڰgʹjC`p@m~x?GR%?|hou2ujV_ݘy,]lc DH Uל̚Q:vQfThe ˔J zu/N]]ͮbk(NօLbLyM|C } ]{䦜cfND_]主@4mfmbC:WPחޅi$=zKءBC%r}Of{;+6T!CX.܅i$В=vk>3U~+o9JtHc_.};Q7Z'ӷ͌ks=@ՁVx&X=^ .LM#;rW+?~ˊSF~^|^E{iթqSH z+>}U[:z !Wzbbd5nJ-3gdzEK~pIڶSu~]H 5i\K hQ}_N:&U(~j5-NCr۟.h #l+׶GdRPޙPz Ҩ){Bk Bz0'BîfדYW $N-2{^f(9PtD/(N DLVz/<6ϔ R6?}MɁWbt(L ϪqSH Y&PџW2G4UѾSRn?M!*#9aL_'k?>+Wr$uD/4@1V(m+sSH Z~,'nZ;Mv#nΠaJox%:@e-m27+OM2;h-Kꩫ+)3kbu Jէr&^^"G=)9eX(+2va&@@y?<)嘿JoM+*ŷ;Wz&@@5y9~x&%nME0,+ё ˗Vz/n2 T7ߔZnX9{(9ܘvP_/Jb D+T7Gg|H cn?Ktk8cw=+#/8@k>{/nԵ:+y$Pz!ocJhWsMi@vZ;#OͿN맡Ů?lY>>oͯblvCz6퓆-^Y2`i" 4^q7JhrUi^Ɂbo$/K+Hv}ҮROfl۶ijRA|R/Csi_ 4kܗӍw>ίRW@v_QsMy蝿A*㭏)P+RZk JlK_ĉ #ТtW_j}2`H[:VS/<מ24NgtZ}vzӾ]({nG3iZ>Ac2wE;1cyɖ)΄ ~m77?y}5\M~V!@,pRh2nwj 4d!:==k_G潉]һzͼ˭ʮ'yz g5Y||Ya8gNqP$}-?8.Cf\6]-]cC24_Ys3\p^{9֫ggpfo@ wlsp98#7m>rGlOm?{}_(^O7ؗ*kO]Zv7QCVEkQ جn{<-^C<<+S>+FH^wflbja;cf;S2JzaI ƍO7[ϫؐ5͚~i3+ u}] g3.m` Z]º0}j^:ΤqGOϓe4vN5Ct+e|TǛם; K2t}G33nRMʸyGpJڦStnLtnLteMvxhw=< _Ccb>^u?s& J9q24A-}8jgex̜ WvlzZZ7ћr͙P`\|2FPS=(@ p)yg2FP{O -[/ӔH YUMsu$=;1U͜3ϨU`RpunR9g<~JOP W)@ p3Je(`^~sTǹe(K[%(ʜ99&h;57 %@c|^hn>' @`tfn~T%ݾb(`a=JqnE%ؗs#!hYs 9/>S1ȿW̤q*Q!&z~L<8J%ofĕ*QQ΄xu~wKYLP1*M,?? i߮ĩ9ƜqC>-;sG橫k-숼<$B>3׏#ӽc^]uA~wGι%op<0Vr\ggU~InN'緷緷g"}~J[s\ZןoGfg{|,&riyy'I ~''^ɚkkgYmoy[< ~kN?ˊۤ2-Ϟ'}CdD'ZH [OvYsُ҈/ J*Ky<\MPe󛏲V 2u2@o;o;7.&Yz \-WMfˏ|<{M2e*5PdhnW='Zj$Sg'פM}e2fE̢YoY4]:ɫ7ܛ5&/f `aՠ*qG v).IyC\SVJU=WkND+xG+ʫ`?53܆f c͈ d+p1ϡm4]*%x:\hh#È>|s=矿 ONiX@֓~oJUj{^'89kwƐ~TMw5MJ\p!}Q֠zFW8eȡ"p{4cN=~y~ã!T믿~SZ79s|g92vЬٳ曇rz7?ÇGsA\|r!̯{^WS2U!mꪾE[wW׈-:B(G\]/>뚋jzf$wIRr9Au?ѳt?z=d^ܚ;X&Km=?~|ĈAAAG>zO<O=ztvlcǎ=p@4 DSrU`>"YaTsڞPg=ۍ. 8-o]pz^v~/h޽V9t[h 'ֺ({KgBZ9i֭lٲ{מ]-7ow}w޽udgW_rr,QĢE>&C8sUU)o6ȜlU;ܧbO<pS7hz2I4@o?9)J#:Gl!mW{h9]A};*g|DKGL,s5Jj}EE͛?ܴiS˖-tC*TaÆމeߥիڵSNY%߸qcѢE^lϕ!oPD"S|BtI j0Ws w.7ИZG(zbz[kS9 UVx9ߘHY:^3PoouRN#__gy&22o߾nM8oə37mTDիܹy\8x!/wC5ۡD"S vQC*Bk} خ;qkhvB(dߧtmZtC7Ь'5;6hXA;PRDz7Ў7T9+`[L9ʖ-?N<9,,ֶ͚m={ԭ[W_9s p7r̹lٲAq؆ +=T W+0ȸR[p-4F)Pp!niSB'hCKxkZR`VW';3)}2p]^^^C ٽ{w͚nnBVZոk6{^~,Xoo3g>\G6, XUʋ53Xا|ZG}iX*p!.f ӇljaHv7ҿ)_?*ϼI5[5jX[-~A$! .jժiӦwK&M?^rawߟ%W_&M$2ѹUm@>r:yD 8]_?BWOh}ܧS-5qnҦ݀HsaZUї3M6{qJB4kGk9m5{^zqMFif;*ȸjN%Y p=jnB}54bNEW.oqyhZI;PD6f PFnۺųcqq}_zPP7|ӰaC'C=~?lӦ 8WSy5X\D"rSYH.g&P˺L(^;v|jyB8p?+HƋ4}  N/_UV57_|N|gv|'E3U{(/NK&kH%^}*:7kpKhAx`p>FϙC5MN`I8QzvݨQ#OB޼y?3oЯ*44]sWRBM6D"SW#k9cuUw 8]GBq[ъm 1JܟX YW<>>SL3gfM6=z<ا~[nt0@8_sL)je+弮[u wnъ#wsjGE~ bFv#H~*>g EpaK6Pg ٲe8qwMm괄|CU 33PWD^>Dp!n4pMGi>{Q>W"q@/ܦN4="Tț+u-Z*U?nCEcƌ)P6gΜƍY >וV-*ԕHdV6=_޾Dp!Gt,BBu4jAi;qtVW?:,t>muY{tVШNt?%K;yWef * 2W^f-2NF۪6eH$2+j#]-:()nݞ\̙3h)KG⿙jŠ_Brh +/*A zT}w7fE:| 2ŕ'B9D+bVxή־׼K.ܦbT+ o}*<ڃAӦM+W m=ӧOt钉›5k֤.7Wv17a v ָw"jU(,bB RIC\|X.}6XOUPLyrUVw.ZK(,H?5]]~AJףMJ"~RJ5k֌PUHHHϞ=Nj^xc;vy%%%,A>龯VHX*5*-Ew\qR7x=N _/|~aPr -ŢRȒp'u:Ä.0ĝuP=2^Ro5xF}WBCfNԒ˞C]=iJf!\rp+H$Jb~j.(_ԻWJ  JII֭͛˕mDJ*͛79ˑ#G9rB .]L2ETz=g%xiWZ/HX#[9x%$.m半Tqş#%Sz+[YUR RB^ܨµ->Bk5Sb$8}G3b@(/['lr}AXP)}pO+ݦupjL$3휳P3Z]KwDb^ʛ]r"QO+'tʟCCZksaֵkڵku֜907p?~|߾}.\~cccr?:uɓYVZ wҋ yު/Ux@XG?o .ӥ^q-.<-I> Rh)K4K& 0gq_՗"Vj0e郺*=k1/2pT/^J$!W6=XcGpT״}V"ԫ5RJ]_~s+WիW/]t׮]/SLzz锍z=rH ,RC=d/^W^ٻw|MbbҥK.]کScVhkje˖رW_}yd WyߖHXQ :>[W~]i+k;}G(㪮+ʴQ7tq3[utmU =gL.}Ն;t ּ!j[HϣM⧺x=uXIK4FwizF~Fi6lx'Νk/^x͚5 {~JqCZ?Ku}e˖1b%3g裏bb2*O?ԯ_{O?-^=a)RHbN8aIi>>>={ᰯ^ڼy}mZZ̙3SRR}]YVF[/?E !֫#.z:>ja!hCA-u KTQ  D\ wORزӵ'MQvTtovPzo![MFe;[nmժUOo6o\J 5jXUT&MkcbbZlo{g}ֆ>c\wGHPy]upF*JZ{MΓt&t`: lk׮޽{kׯ_Z4DPIDAT8/^8}tӵdϞ=O ֨a _~9%%:4w\ܴi|qe x$ՙ/_~W2[9ZP7lR'e&+S= qwMgs`E*Є*E&a  kzcU\"_Og#GGԱcVVͪeҤI7n_1s|||Zje w[^1c >!7@d._ZiJYq6ohǣJKGpY[[zJjh.".똎}/v z-}k&c*W޹fCr*VxZa^lZ{J-7 ^Յj~௶ivUl7a„| 6|WrW\OLג#GLPz0FiߒΝ;7uTG޹sg.}F&WUJ#'k0zPߣ #IwlvPZmg٩zC]4MKu6+Cz Ö!x XvVMUJvP(])P YUijzNV>TƳ\ANHҧmjSstBpK\.Alׯ~UTqԇ[j*2?I&Fp۶m+V0ToitD:uead 2ho5٤{wy~zN|<(e5XQ3guI .1W ?q!nzlRxK}$%O9=am26Yhk*J'-sKR;axW[uݮN\տ0[E\YRRR.]=իW߾}cb?=kiiif?ez ļy~%K+׷QF\nF"LUN%W^>*TWks79R Ӱ>cs%4Q%(+Ȥ^L 懸hiyzKJQj;27U֊hcuASNJI| CRf˟漿Zq&g7]r]v׮]_;VnO?Eի?`_ҾlhCR͹2 Dokw^ѿ٣`*|N(5 wikz֟Ye%(a+zVn C.ۇ_?%}́dC+d fw/9Mg1V.b𓗴͉I|/f7pk׮)))5jݻץ022hªN:AA߳^~ F`W^5W> A?vVOl„ w\Ș+W-?Zj=իW+W-iqJ2:uN1TbӒ1"@tSa,s: kF͕?L2lPGCB=_2%{BkW'\p¬/EfHLV׷4wݟ-3s;9]3X8v~M Z@{5;wd1d 2eK+LnSVaRF _ӸVjeʹJ: 0[RJ=qp+U16ą+UvC\'\bY16 Oe`Kl¯PZ6喫0epC;],6BB.t9r*P!gF{&+R\Njk=ջ26অOio> CܐRg`+U8QX!MVKqPi:j`gU{^Nԅs,ypuK?T "'3Z~LLLILeǎ T9C7_ȜhmꤔX+"_3Թ 0mfأ@Fjo/[N0 >6W~{/j]K+ӷ<$U9xˤWCuf;*P ۽_}g1GΫKwr`qK~/00e3>>>˗7wlv::::**\UV?gikFw>Tk)]0"Ғun =OJFmi[~2S3aFCЌJFU *tĆC\3_oׅz5KW358.5R/ݥZ#u_\u˝x gG5 0WC2 789rhyT\.X'6uLԇkJor}׽|^Q)7@+|wWQ Wx:RyJPG3Պ|zVWj|2YūޕzWz`Fh">љM3Xxl]vFߴiShѢF2sKжr8Sⴹ.a *닽 dJEUH*緄( }1Nһf,QjU.6HOz( =;N?yPuũ2,Ei4ffxyFe9Lms*Wqq7m^Z4GjuХ,<8MKTI%pi۶*%e˖-v2U8JbWcZSWgf费fv.6< )*yrPpQ]gu]Kfm9rǭ)b]݉3\l=Jb˳Y㺪NT X=-/ӵ|iQoy5)Cp}ɾڦ_0COr5}mt!&~UBbŊ+׮]qqLg N8as;f|2KL«*;~򰒣3Xρ0 SO9*¯WHQyvJ/W*jʀxUZz/h rd|@/q7W@ Pq q}! ˑ%~7ĥxT<+PODLWG'*Wy=~ C[ޟZuDM9\_>Kṕp5\ A: GnBp]=|21@H-&l6UN_v]/i;Zfۯ҃&wsPh*v?hgJĢC~n09K;%x+w$y{{h˗/̷p8Ο?osM<@d& f$_ӏ}Sⵥ}Dr 0Y]Bx8PtFPﯺUKj*5PUڕ:V|Sof& @ZS;͕+ތq!B?,zd́TByGmթ41םS:[iL-'$zk .Z:Nԍ?<S¼L̦ 'B$顇P򣢢֬Yf .]bs9?鑁2JwBks r1R5TzZUϕ~yYG9loTNފCTA~+u1ۤM̗W`tJC\o)&bKxuUƶ䘭5 XIJHjczv$5 rA<3wz"J?ZP[a4Q|LPCXl+b f5g0ٿ˗/+ D0:4NhU ]cis9asoKQ26 (~5٤) N^DMvhGUs '7W~5.?zI%5Qz|_|!a қJ%%@ͪgcRGԄK m$ٯ#5ź&Wa WNkZC÷d u޽\rF?~ơ{\p)7Z3v:]fW^="9C˨,n&t#3\4JTͶjɕ~9N7ES?L2`Mjj>ֽ@RLyc=뽦T?\טϵ}++/{΁5[~͒sfg<  N:iӦ̔#GhsAzCCCcccw9+bDy /^gnQj}DLo6.d"lt3}f*A3W̞97@=mUC=BKޥ:dD,vɯW ax^z.]h3fd 2&gΜtX!+6}]5?R q3|U5A\́pn<R=mr_Y_q1q3Y7M@ulѻ*ϖ+W &ƍfd!0]oDD<]x5O΁--/kC VXW Bj!๘Dtmf+?=G3<{Kzq>K5*Htux"7]u6PjvpN=x-{͛h3Ys 29E"Kr9s?50nvKioRtvx"@8z?F1~a #!.# E~ʸ_Ӟ4 K8EԦw0h~sWV&jB|֣' ޕVzvT6N%Z{\A&7 ҲT:}qKdaUx5k?t-.\x7*- iΚaސ:|k mO$ֽڟɗ[LD;mыa BEhrWHUȫfґZ֠)XEL?З+ZUٝfu&| !o(I1[~aY88%Tm#jZ5W~nVխ_]%\P(" _r+x [Aү>,o kAKLs-Z@+ڸqٳ-,tY0|!'(TZd"&7EST q7ʩfLy6S<=I'c0%^kطÙ2w5j͛<ᰰL'%%9$&&-9E.$LUMBTm/ep&L#?6ilis hu>W>)㜼sWmWm|UKe敵q&,^d 1^{zȆƌipOMMuI1]/"T^rM?/eiZ ~>{PW~ .V=8H.4} QlYj |}u)4iȷMcb+̈ <àA^z%*ھ}[oeyd 2 Dz 14䬭ָs4h48H:}@]xvD%=PgPӿWZ}yyg}2C+G뙶*Oˇ1@G=xΝ;Ϙ1Æn޼ٿOM?Iw @RÅfJ/H" ջ29gw.js| ogo5' ,ǢNuw_5f5ND)򼢮R.f"B'@=!wW~O?ێw~(9Nd BqQ6:) JF`zޔ>:k}5T)Y-;{Xaf&+.^800І͛7{lC@9{w@Va M Va@f}Oe*I3@Ji-voHmz\(YоSn'"E,[,G6u+쾩zRpO{e.t!L Vf Dd\[FxJOȇPWv״KB{5GI<ȴn]!pKy]jUBl+!![nqqqHN6X\ *Lp!>w{PQݼKJRR|_c+=T;ȠZHs(Q=Js^ײWTc͗+ >AoP0"Ϣ˗.]چǀo{+s;Yɺ@x*j7/J]X+x_\Vvg$}#/2QW}NVn>ӪސTiϧRF8Y43gQ́ YtkOu/ Lגd|әgk:nUJ@DҦѩy~H֥Vܣ4P%kdmجP %n.7H?fU j'pi Eխ[מΝ;n8*"1́H/2p DNmlUunk&A7";}52b&-ZQ ௶H]tXܬ]J}4#Y)eEǩ 7k̞V^OS'd p Ҙ #UFiUM&E[UtaMGqA\%Fa_}(cO_W<pgEnҖ۝FJf //9stɦtbn@fRwtFoïgdm&hS]l缏 }jê!v*bs5'-}M7lk~>%iژϵhoņ"B~ Č3kO]gϞmӦ]>:-9Em@\Q+:MK:Mu?@:*hq'c$duЙ:[9JISip#A}g*͡<٭/<5MWnMzSWTTTfN|6~n:p'́p5d J DQJ)&W!NAwЍ*A "~v lgx=}IK@ON4ɦ!$)SN6lpVc\b|gd^әC.$Ֆ9Lg뼍O2U$.,:aaFܒeٷi YQ_{TPjgR@9l]h7ĎrH}>w>8wU+w7yÍTt9Ȫ 0}t{JNN~衇/_~={v+< ;'@V.B2Jc`CE&*SP!XPZߵj&5.nCJ%%zO>񱡮/]nzge j@~YpNӢ-?ўe2#͓zƧx\NPL l +qƗQ~{"sQן<&x;cn&2lk׮ͳ'߮]+VHM?Ign!bz:iW g @ɚl`?Lj3[jnnǚS5Q5|QUTl&R}{3Wh?pQ\q1|AewQh Ν;ϟ?׎bcc[nfi~rrk̕o:p'r]hKִJ5Z`!!voFȩzJZU`?s588) JQ*]pi]2C53]qsC+~R13CQ1&`t 'YEǎ,X`OƍZڰaĉ ϟ?SU@sGEE%''s ӫ0%8#E,UtMOG^-h ]WU1KFJBM.|mtS$$E*{W_m5OX Uqp蟿6Ԓ6Grڑ%<l͛7ߴi 7 `ۥM?]ҥ6<8---Eh"88lٲzF߳gUk3mUκlU@GuK}iZUWu 5씩pUŹM˫*o_'<l;#~Ui RnޞÈ#FEk.WVͶ@0"c@D"j;4Xx2k4-UFE^-t[Mu |I1tjTAU)-D.v˖-˖-'Lucuq{TZնԨQh́2p-)骩){E']i `tI>F(ԍP5M>E3!n=`؋؆.f͚˗/ 3]QJJJ޽y,.azZi9 \_@6U9~ O-xbɏPh3S 0aद_+.@Ek"+_kX*3[}JK|oV~G܉ éQ(uv*U磷UFfk-)Sf͚53]ի5k!]`G=~:vXn]GuׯseSljo'l_RfbjsPjwRHJ>?g'6mD+P5c4Z^7; 6TRk֬ɟ?/^ܶm۸8Oŋccc͕ /X[MGtn푁Jד ):k*@xs:>1Z|ӄ( yΚ\́`h+g p%K]`+Ov횘!믍V1l0kׯ|F955? 2p]'@{՘# {ly=+#=h353Qf?wTǢ*J2&39]h1W~iѺ)um=O6r8E%֮][P!yw)))y-?88x^^<+\رcMd͚5 @u_Qk)GUX/HQ:R9R)纡ӵ 0B !E}zE7lnY]#VX5Fj;ISx] Ŋ[fM…MW4a„;W^}Ut}/'o޼Va%>K/@u9RtjJ-|u7̎#?nxAV]*;Kiעk[F fnI-u7c2Zq~EߎPB6zm;JaE]hQG5b rjj {<'NL 9s\jU2eLjbb镩Qv'2@-YS4t-C5PG қLnKǛUz9\!sC 4$QUzVz:ז}H^.\x͚5Ŋ3]Oo3wo=dooI<7o|3aaa˖-T o_Id ,zҎrSy{7#Un|m HK9Y׬YSdIBa &SQΝݻ`o~Ϟ= ,-y729pu9Y^\)}:Cg֩Os@ Hx(RЎu2wkVnU*pjwJV%Ř(AI/4A/Y:o}Z W'բY^BRR9tR{O꧓_`ˣ{^9/{ٯ&$)JJN_fmOqˎ;V^ݴiSz衇udɒݻw=zȑ#GΟ?2eJ.]L&MT^8dr ' Vў*SiyAIW$;@y1L<  {n&§M{ۭ-ZqyDMQEdKHfk:g[":H-3 >XX GYb rQo+&A) WpsXnˣyT,dw@Nqa|YƆ݆W&L'qwǎ;vۿ81/'Xt*Sv^ *+~x]ڵB+L2L5tyLM'i+j5TJTɚ*UNlRNkN$X=vDV05fըy4{ 6[|yr刃S\r5kt87 iҤIiiiK@;*=L!E}0mdqeU&ҥ7V5v-dTmѭ:zPhv+$]1J9f2B @VrO`RJ':tM<'NV!!-QRy>ӵsZ`&@Ș ğ$*弮5 pU ]@nH$Oy?aÆ%&2Yƛ  G"TvjGd@KUi ;˭Ԋ82;HiĈ׮]Va-2J/(\ъ~_Ƨ? &m>!LU8z=-H+**_&'&&2So-/ K}IJͿ\̤mfݐA0La4PȰ{Uz\zqe<9G% {wv98qѣG9",&v Ć']v -Q4t-4 m1Y}J*/qj*'g?;+ݻ߸|smdI yDc9瀧[ut-4Psz 5#ջYxS 5|-[k&OpBβ!d Ukc;]ZͼBqh&4TCB Bo'Vnp[]t9v5mݺu/"2RC[]mVȑ©q;5jƍ<@J OߌYYj6MnشCkrh"vWwB rYgSSjuvhLj7iNVM:uqƧNrlذuֱ<}3 XJ>sF]#:oF?P@'YzbxG~oп</ED;qDƍϜ9~6mڐ~XmHX4USӔf {0k~s+̽_*ҮУ$^ǎ_={n6mq6m@\k{pkC xErp>HRjZyh0FjSj喓!(Z@s*r84{՜5xȰ'Nw}~a;ԑ#G>G{[qHkw_X*j5w`aF#\!5`޸_eݦQ}Y]jR#Upi?/tUGdVBBBUٳ7~7r!wvSiSG8e9v>('+~fTUU Nz#=Wٳz[n gJM-3J hs$`{~pCV?plFb-)KgZ~~KߗֱY>Gd<3N%>ji7kޕU qBDX2Cm%Lضm[*U Gxm۶k%^u2p[Kk0݌rK/>.ɚl*A6g S;)#ձrf;(V;wW3/yqs@t,x0*%%eeʔy\.]4hРʕ+/],--QGRO)0)階?pG訌uVZǫ8u_k×rC7]8 +5&EE;/˭`˗8cƌC#88؉sĉƌ111"CtuiPG} 4]٢StcqNhl!\RcUGiZJRw?-u/Y[.j/rܢ#ڿc=6| `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=2zd @둁#GX `=;G6Oփ@+ CǷ5C\+j3Ň$t֍ k `=o]re,vPx*eXIfMM*V^(if,,+?*%tEXtdate:create2020-12-03T15:47:23+00:00o%tEXtdate:modify2020-12-03T15:47:23+00:002putEXtexif:ColorSpace1ItEXtexif:ExifOffset102sB) tEXtexif:PhotometricInterpretation2+tEXtexif:PixelXDimension2091sԵtEXtexif:PixelYDimension16742IENDB`pappl-1.0.3/testsuite/landscape-gray.jpg000066400000000000000000004327621403603036100202660ustar00rootroot00000000000000JFIF,,ExifMM*V^(ifUUUU+8Photoshop 3.08BIM8BIM%ُ B~C  C +"  :SAvqo]Avqo]Avqo]Avqo]Avqo]Avqo]<9j0˴Ë.,L80˴Ë.,L80˴Ë.,L80˴Ë.,L80˴Ë.,L80˴Ë.,L80hwdݐov@qdݐov@qdݐov@qdݐov@qdݐov@qdݐov@qd՚([#μC:9 3#μC:9 3#μC:9 3#μC:9 3#μC:9 3#μC:9 3#)XG'auryXG'auryXG'auryXG'auryXG'auryXG'aury#fWA8?N ^O5RLe ^q$t8&Pq{=Tm`/t)&|agS8J/Φe lO~$mK vB5VUS+  Vc eH1pLjqg0)S@ W֘JyҒ`250-W@,tx^D' Jnw=M/t-TRHquTQj{|m`l9C@MvXĎsgԾ`IxvnONPJyҒ`փIVܕ:9]q8A!|@+-EW4]ۗDUZb{n^[)=ء%]Z%hK6DjWArK+0o*4BA`:q~((<2-PP/t)&Uq_Zsog^D耨7]fH Jnw=M/t-3Hdr;Fڪe{}X9-4" ~8 eH1pL S*d:n0/t)&9nu!a #d,dzjlڂȀ,,-Xճ ]jSu9i{nwTy j\6-椋1sv_>5VUS+ Rk64BA`:q~(k&?ٸҘ+q{AtSZʇWˈ{n^[ `ۂ# |h^qB\(q 8ӎc@0HHm3]>p8Ҟktqk܁v_7PrU{PY ekQ  9i{nU(&{}@DjWA lh惲$t8&PaG Fx?pxw;L JyҒ`(Bw xꛜxm\  ^D2E^1GQr*N9 cZ=M/t- (fX73Z̝ s$&He_l^qB\(q 8ӎc@ŭ`Ʉ^O5RLClqᖦ{~+-EWƧR]mK v AMh f~^qj@ ~*fĎs Ǥ+l*J ¤6Kdq{bid(7#)629G ʺԦ(gVŋAhy}+P I 3WM1}D5 S@弨vً QE?U.t̯Rymԓ` C!)VC?F'rQI`9Vx烡x:9TzW e]jSuk^:rبܪjtg]t@(TN c>(ndvFɭ{5]VD\QIe7QmgYVGk/kp\^۩& KJdp[^j3y,J~@ 3~@ʺԦ("JjZ;Sj޷5t̀P I'C/vi<4o72 ffÔDO ܊n.[ʇo0Z s?yw82sp\^۩&uB&7~:@$zvnJC~O_cfX2|<]:9uM$P)%oZ(#1x~6BX*u&`si1;\~}0/D k`@To7Զ)弨vQiN@dO6~۠ ΐYO-lO^r Sm!%JcFO:ѫkFo1%#`-v9uM$P9t?  G3s S:0*$|qnhށHݨ E/ɼlX EU\"z *0$72F4@ FkV 2IRMS5 ?W_TjںrQI`o4'U֥7X@S+gvV8c:_ׄNTLV(9U؍8db5]!`:/TM_ !ꢀKS+G(^d+k@*:AfW-{0C*WW4&NTL? rAsD5 "n(o*a3cmzP,uP ΐYO-lܵ1`:ILZ餿D ?vrQI`Jt$utZO@ʺԦ(9X>m6xL4?+:-DzD~BX*u&`U*Oj y;D*&[uQ@%yP^ttؾtah`n +Ԟ[u$(r | $2ސךbKG4]qt]WZbEh<vɘS:0ӂ?e4{(Q6 `,۪.[ʇoUE9ؚ@x^L&[H,'I6gE[/'"Γ0@9my$?UTųnʺԦ(2Щu'z;g1P I~yܡY$0~?`TM_ P`k"x ũܑKvBn +Ԟ[u$kFo1%WZbE `ԙQ6 `,*:AfWGK`R+'[,`M@|%, GVҫeHlf s`k6V? $pD[J"uɛ)́Zd@7B^~en*XWy_?W&nc|}σsQS s`k6y} 6^Iy$JZ02 ԛlZ*U@ "=ghl*"N6K͠!/wHb<lHm 7fn]N T x\`o8, GVҫe[I*5*di%ƀRm@R˧K PHlfΰTW f/|95X iͦbՀvXp"y'x$ Gҥ@ RJs![LIQ^=]BP@5mt@B^N^&"ͲsXIyA9"X@̭եWTTk%TKԥwPڀNY:d`ASI> ԟ:m`,e6UOVbxzd:6a.49# Gҥ@ RJf0`RWS%;S4kC B^N^&"rDxկߣHBTad`K`Jd IqPTR+'[,s:U*u)`g߬~# K`Jd IqPTR+'[,s:U*u*آ"ZNl ~&Zji+X``+[{ꯚo,ŴvTkR=}AP@5mt@B^N^&"W X~en*XgՀmڈ,%{}XXPR+'[,-z &6R-z ^ת-z ^d s`k6PT4BZ٨@?tOо$@|/\0}M0=L $pU")iw8MѨW, GVҫeHlf s`k6V? $pD[J"uɛ)́Zd@7B^~en*X&n6Xki# {>GK`R+'[,`M@|%, GVҫeHlf s`k6V? $pD[J"uɛ)́Zd@7B^~en*X&n6Xki# {>GKum2C k$1ƲC k$1ƲC k$1ƲC k$1ƲC k$1ƲC k$1ƲB3=;<@;<@;<@;<@;<@;<@Cص|G}1|G}1|G}1|G}1|G}1|G}1|Ʒ=Ck=Ck=Ck=Ck=Ck=Ck1O7+0C#Oj;q5^SV`G7*nv  k, oT>ݨ@?2@yLXYX!@̨"}Qی4wd2?0B;PDvh_1`ef~`=`w2Gn2qbze@ڎd>$ ŀ!6=ĉ|#=灐>|_a}|_Oj;q=~[BiZ#G?2@Cy@}OH=0džboYR'ȜIQY֋H!Gef~`=`c)X`0|qYI履G7*}]#oQ*jܩ! Eo]Gn2#В(`9w}WaKf8F4wdKE}~ ]AuTc>A>|NG_1`q"{r;+W:S8.m or VpҞkV`?Dr Fp0|Md-qO&_yo8w2(H7 %U-]Tu[3?@st 6SS>ݨ@7f(S'{(h?ә/(+ŇY}~@?2@]*4PPkA^G}o_!g'Xac_1`^pyQ N9ғ}Q]v֎__9;(\H+0CP{P]`0xzPV0| y?| ʀuM:,LQ*j\,X כ"}QیʦS?`9Z۟4NQ0;B3pO46tlg?2@[ʇ2an'wtDPkA_bS6ʲ;$Q;ǨT ŀuef,uO*.o1 xfN_5Ї>!y]%F! seI 9xznn'FGuފ{B]^ZJ#Jvר4v֩Z %U-]T:W(yʻGVGn2Cd0 w}4NQ0,FxB΃JoQ%:#+:N?2@]8, Z ~)u4 /x'(hvPn_1`8^*[Jr]> kMYX>p҅@b +TlPᬜz<oTUVQ*jˠ[p09~S|xOj;qq]`& sci$'>z#H?2@.PkA;wˎX@Ŝ_w?LGA9 l[h TV QUq*M."Zy/I!|M<ѮrD@7zD3ՍmĶZB;PU[TDG4y9w>ݨ@ǽtRS'{(M0x>'5\ś\b9ԭh9qt_ Ԏ"L'ZPl}@jԤ*C-@/)阀Vʟh*v kq enV.Ouef~`=`:7x<`^#j93Lʚ-Mժ{%[@!@̨ jUR@}~V,10>ݨ@ǽtRS'{(׋^P) s9P Q Ϡ  JP{K5aą͗:{`b#zp\O`7wm"&||@u[v!|M<胯Rk9=-\*Bs;ъ  oTUVQ*j:H`ڎd\{E/X9w.-:Σ2|hc04 }C-zqt_  HX__d(L6k,N$ ŀ! ʀ'۵Gx}H/)+0C#Oj;q5^SV`G7*nv  k, oT>ݨ@?2@yLX\H7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V7V>xmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMTmMT}xa`}g`}g`}g`}g`}g`}g`V(eXbUV(eXbUV(eXbUV(eXbUV(eXbUV(eXbUV(z<4 456P 03@12!$%&#"pU㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㢓f::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮:)6j*z㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::#fqqqqqqqqqqqqqqqqqqqqqqqqqqqQAӔQqqqqqqqqqqqqqqqqqqqqqqqqq_rgQFĈ.$Aq" \HD Ĉ.$Aq" \HD Ĉ.$Aq" \HD Ĉ.$Aq" \HD Ĉ.$Aq" \HD Ĉ.$Aq" \HD ĈaOhhE(EƊ.4Qq\hE(EƊ.4Qq\hE(EƊ.4Qq\hE(EƊ.4Qq\hE(EƊ.4Qq\hE(EƊ.4Qq\hEpJrTar \F*0Qʌ.Tar \F*0Qʌ.Tar \F*0Qʌ.Tar \F*0Qʌ.Tar \F*0Qʌ.Tar \F*0Qʌ.Tar ő$m\H2@̐.ds$ \H2@̐.ds$ \H2@̐.ds$ \H2@̐.ds$ \H2@̐.ds$ \H2@̐.ds$ \HXUڭ&L[$] "IBHEВ.t$$] "IBHEВ.t$$] "IBHEВ.t$$] "IBHEВ.t$$] "IBHEВ.t$$] "IBHEВ.t$$] " *gPNIfNfo:mK_VZ'$u}y'n`J7Vm~JrU-xnټtv%YΫ6%[zR9*Ձ<7]_lIۺX;?U_~rku Do$,VfͯV޿TNJ`{:rO W7v3yfo_\%Z=B9';wKgUkU.S}XΡhu䝻*uY*)VgPNIfNfo:mK_VZ'$u}y'n`J7Vm~JrU-xnټtv%YΫ6%[zR9*Ձ<7]_lIۺX;?U_~rku Do$,VfͯV޿TNJ`{:rO W7v3yfo_\%Z=B9';wKgUkU.S}XΡhBs(Yejo}Q5.Yi}uʢ(]s$+b$ą3tv%@X 4j esO+]s dL\HYU_VV OcHLi Hq|.SWX"S;TGjQ?Gvڣ;T~7+_VZ'P 浄QEVUoưQ[jǢ0d'-5[&$"|R~Gn`©5 4.2dU,+3yf)(~ipS+o_\Z?T Or-RS MXvUo'.4##tv[U4FE5#U_W@WV޿TO~ xZ{}XΡh#ޒ(v+:voYɩ'{ " X!6D|ʶ^qwKgK EVVRM!XI%QJ5l'UU *iS;*Z#_fo:;tu( &*{%mKjg~JՁ+(rz| W72Xv|lUm(bzgJ&0v7n`wku A2˾ژ]Rڜ fo:4QC/w:֙[ <0 H&|-k~i%ˎliu Rڻޟ3u֑/'7jɓ()͵Eſtv 0moȂ67&Ouk՜Q"9J)ljdM_VZ'2\ 1dU/xVw>gW e6m_ݻRA[j&4YΫ6pHm3LE|,kϝMS)*jE4,U&ʯwMPfͯV{ƋrkZLEY[-=o| 3E-=ZCɝ16sԦ*Z=B>zVoʤI uv{lJh3U`Uk/؛s&G:e?닣e'9k[.aqt5kYu{⭽~rRlthmNku De[W{xnٿ"4o7aX5w1nRɗwKgKRۛ7Vmvǖw[zR>]j,??Z=B>zVoƠM,qrڿT0 CtvP1ەLYΫ6ZcW;⭽~rR 4b0b!Z=B>zVo|DWYjGB?hG#vmԑYm:$Βb_aHH1)RĦ˃U_v7s%?NǧӇzpv1kЍOV*)V2Z5jAHNr!v>M8m$־gPOvUw>gV3AektvtD)LK*XY`(+LV3|uk5S+o_\Z DuKfF(6xʼoR־gPOvUw>g:G'n`M6I$&3yfksEV޿TO/J%ku De[W{xnٿty;vlŸ\]75U@4¿%fo:MnhJ)?R 6BwyꓛUi(TQD4|-ZI.U?VFu}~% n4ƛ`lMi?64ƛ`l詿tv}!PḺNj}LUK\ /U_q6%nm$ַ[zR>3Dv1TeT+P?8RM%$*L,X`{:rO W7v3yfo_\%Z=B9';wKgUkU.S}XΡhu䝻*uY*)VgPNIfNfo:mK_VZ'$u}y'n`J7Vm~JrU-xnټtv%YΫ6%[zR9*Ձ<7]_lIۺX;?U_~rku Do$,VfͯV޿TNJ`{:rO W7v3yfo_\%Z=B9';wKgUkU.S}XΡhu䝻*uY*)VgPNIfNfo:mK_VZ'$u}y'n`J7Vm~JrU-xnټtv%YΫ6%[zR9*Ձ<7]_lIۺX;?U_~rk4j[* `1cXc:0u`1cXc:0u`1cXc:0u`1cXc:0u`1cXc:0u`1cXc:0u`'Nfqg#Z1kbHŮ]#F-tZ1kbHŮ]#F-tZ1kbHŮ]#F-tZ1kbHŮ]#F-tZ1kbHŮ]#F-tZ1kbHŮ]#F-tZ'NbiwI81[b8nq+sV1[b8nq+sV1[b8nq+sV1[b8nq+sV1[b8nq+sV1[b8nq+sV1[6hk Kb(.Q\F)rR1Kb(.Q\F)rR1Kb(.Q\F)rR1Kb(.Q\F)rR1Kb(.Q\F)rR1Kb(.Q\F)rR1Kb(8yFa5U$13bg!C8&q L13bg!C8&q L13bg!C8&q L13bg!C8&q L13bg!C8&q L13bg!C8&q L13bg8yB0{:mOm l -0[``m l -0[``m l -0[``m l -0[``m l -0[``m l -0[``mrD-,=0{h`Cm =0{h`Cm =0{h`Cm =0{h`Cm =0{h`Cm =0{h`Cm =,n"DMф 7Fn$I0ta&Mф 7Fn$I0ta&Mф 7Fn$I0ta&Mф 7Fn$I0ta&Mф 7Fn$I0ta&Mф 7Fn$I0ta&MфP֊"RN@Yad@Yad@Yad@Yad@Yad@Yad@Yad@Yad@Yad@Yad@Yad@Yad@Y<ɧ((I(C 20(C 20(C 20(C 20(C 20(C 20(C 20(C 20(C 20(C 20(C 20(C 20(C yiN97?mrNyy'bh|`j/G$σԝ+rn~:KnO}*՘^I:W$ 8uU0>gRtIɸkp-=+CVaz9'|Τ\p3$ך[w{V&VrO=Iҹ'&gIï4$ܬM Y䟙{:rNM\^imIX%X ?3u'J䜛'ۼr4>J5f~gN97?mrNyy'bh|`j/G$σԝ+rn~:KnO}*՘^I:W$ 8uU0>gRtIɸkp-=+CVaz9'|Τ\p3$ך[w{V&VrO=Iҹ'&gIï4$ܬM Y䟙{:rNM\^imIX%X ?3u'J $(EYe7c i#p3>|ұH# D1H# D1H# D1:Q^imߘ)*f%Mc$~a48gxȓ>A>gbh1)*f>?ѿ2^3i( YgTN93:tw'CF =G:W6l֫,jT+ʽm97?m|/hmï4ȺOE nuZ{J8Fb%`E@>I_#r4?uI)8Ui좚a)⨟JR?-`j/GE462|t:ls&J:U2˝/σԝ+YY$MQpcnɸkx+CmNy~7qEqxIȆ` |o}7c톚i 0^W Y3 D>Vp#pF>h#>gRtɄMPѪe4ܓNֻM 3;>G&g㶼QM48uπs'()D5nyR`)m3{V&,)@>t3.dخU!ԕ9'KlX UX&%YhVڰ Z|ɑΙibG ʥf+Rl:WXiPn,$%˗*|M_  q5k!0C\`t5ck^im߉:-Z(, 0CcIV`YU~@hq$AI C`S2OX ^sJѳgw2ŭ+ĩ2ܩmD?Jj:Wĺ :Lɑ͎Ɠn797?m}U+ZIQ>7ۿݹmZءڽ]NsJ'#J]3-mX w,MM4MLTmYZjѮV4'ZFk[>gRtTBEDܛHQң)Jab(ï4ĪNMaȩ&t~˖Zڹ#>7bh_$GcV좚hX mgk7|Τ_ ?Gj; qV*~ʀ3D [VԗRܛs1QBpG64DWۿٲKpLT7GRi QLܬMrRMOkjBVaz>k+Z{hֱ~~k?]3u'JW>FUm{7%چrXܛzW[W=rך[w⦚!f}U'85џN:>UfK*nɱ&T6#!(; Y-kM?Ǖ?;YVo=IҾhtSKPuBabZ ,["?97?m|ED0UbYWt|y~'Dmg Q0<@+H-6NR,*N4I͕dfҚU7JZA$a;|mV6#16`xߙ{:|1CDpϕL#Mk)rZG?rn~r=$op-=. )&IOd'Ks#ܪQM4RoM*lR7I2Ju+`j/Gmek]RzPcw-`Ǎ:W%й-o)°lq.]i%Kt!>'&g{[W=Oך[wpE.؅}D?YKo{V&=(.^}%~:՘^ֻU J6C0H' Z7,-7|Τ_h%` 1C6VY6HҠܛWЉj:tjr&_ך[wtJKs#1!NBǚUk[ri.=+C?/<ʒlٓXBje|mV#GeD6.+JH%QLR4xJ3u'J+''=<ݨo(aZ?U*W7ɓ'L.Orn~_͵l8u>GH[>h]{Tj2ljTQD4|O}7*ÓSuF VȨ>@0X =[YZk6Lh)HTA9f(N6:ZoTσԝ+}"WMOܞ?&L7&gīB2DQS642}y~%+ N5ܹo}7j邟5f{h% xZ|^G|Τ_#%Z#Mn3bML&Srn~+_Pzï4Ƽ$%ezrYYƁdZ~GbhCSU I*5f{hd )OL89 4+>gRtaI*5`Bm&**.tGIJP%1Ik0p\Q!'}.y~H8TYHg1@RrL W@\F0Q"DWbh}`L#8 "Sn0Z)Z(dQ =`j/G| Z9m)J,3P=vCI~%0cL ?3u'J䜛'ۼr4>J5f~gN97?mrNyy'bh|`j/G$σԝ+rn~:KnO}*՘^I:W$ 8uU0>gRtIɸkp-=+CVaz9'|Τ\p3$ך[w{V&VrO=Iҹ'&gIï4$ܬM Y䟙{:rNM\^imIX%X ?3u'J䜛'ۼr4>J5f~gN97?mrNyy'bh|`j/G$σԝ+rn~:KnO}*՘^I:W$ 8uU0>gRtIɸkp-=+CVaz9'|Τ\p3$ך[w{V&VrO=Iҹ'&gIï4$ܬM Y䟙nb5v\1#bGĎ8$p H1#bGĎ8$p H1#bGĎ8$p H1#bGĎ8$p H1#bGĎ8$p H1#bGĎ8$p H1#M2t+KbW!^xbW!^xbW!^xbW!^xbW!^xbW!^xbW!^xbW!^xbW!^xbW!^xbW!^xbW!^xbWɓ'L^]/ p_\/ p_\/ p_\/ p_\/ p_\/ p_\/z}Ȕ.Dr% (\BJ"PȔ.Dr% (\BJ"PȔ.Dr% (\BJ"PȔ.Dr% (\BJ"PȔ.Dr% (\BJ"PȔ.Dr% (\BJ"PȔ.D"Qe$DfI\M:hEΚ.ts4\M:hEΚ.ts4\M:hEΚ.ts4\M:hEΚ.ts4\M:hEΚ.ts4\M:hEΚ.ts4\M 4P.u'<]IORxԞ.u'<]IORxԞ.u'<]IORxԞ.u'<]IORxԞ.u'<]IORxԞ.u'<]IORxԞ.u'KN2<ƪJ&8uf9s_$IcU@%]MJ:wO䜹﯒p $hrNYN\w8stGXP4~IWSDҹ''.u9{:C#,j?$i\Vn:N=!5T U4Ië7tI˝n'`ΐK*hW$՛$η}|0gHdy%UGu4M+p?r[IØ2<ƪJ&8uf9s_$IcU@%]MJ:wO䜹﯒p $hrNYN\w8stGXP4~IWSDҹ''.u9{:C#,j?$i\Vn:N=!5T U4Ië7tI˝n'`ΐK*hW$՛$η}|0gHd~?p ˜bSM?84SE>eUGu#'"8<2̛ AzK9v9 GrhWDQӘ9xKBOo-A:wOh:*:c0#2_⣦0:cη} 2gGGLdtDRgAGwL`tGLdtGLdtGLdtGLdtDP >G`ΐɚbjfQn(LȭPڹ5T  1GB=rZ)H,\I;" h&iyU]MJȧ \6V[q)(Rw$P 2a<@t+VniKxHq(˗,[Ѽ\w53Ab ɲ6m~ixn_#0gHd|Ɇ'$Iּ ('&Lع5T H M?3ݔQM43aK!fD3KM*hWy7Z2;k-"IELBĤU+UL0w՛i6k'-&iiH֭W.uAsck /X{{ܾG`ΐQ[i(P=k 4&Qy}cU@FfJv3JQ"yRάEA$'ݐ6Y!?=M%:wO"Z0"'r&[Ѽ\wNM8e&mbmxn_#0gHd|mZfa;lE2$62< cU@ Ię2~gZ]qڲjϞIeR7\jj'yu4M+kY"-ڈ5Vw%Et4h@16wVJ#VnFLDHM/Y9o Y %a{o+:"Mσ\F+J?+DCG`ΐAirCSZEk\1T6)K.rbucU@mh:t63ݲ̑1%HzҏiS3TG gxu4M+lQ 0 D4'8V[T*b2hE2Gle)p P cZ/8ل4(8yЙ/bލr[=)zdJɷ4㶱6k mgL9{:C#jV)*KeZs6MkخU&Y*Ft~-\kݓaS.L`Y4CDq~u4M+]rTs$uU}GNy Ɍ(QcFp?_vLF%&dwWsGVxM՚ #JŽη}~45(gϖS(<G(aQs[X{ #as. fN nqK e<"t&\h8?!et4%Cj +k=@#J4"y>j$rKV vx fY#VnF[EVY9n+G袊(HM&~zp[Ѽ\wh9bB6yPq7#bv&(?*Ur؎i=q&1i$)stG.d3"(ⱥֻϜ^+P{W52ƪ(i:i063F5)|~<&by͵nh^E^HX)(Iw.+VnYLʶjw)MЊ]Xَy`7]"&39DP./!,AS4!H0s0s0sC9#6:3b93xh(q xF#9RIs0WTpgFpL:W#f{U!1 )&6@K1V/bGVJ!=TUR=&QPaWő;k'D#mVO"z'ꗐ`L,ʺ*\.Z b} og]픆O%s*=há>aͫ$gdzڼPVld k*K!)):")H^C-=\ăråS=k˂mr*j] E1SgKk|f%IJ;k}BqJOC b۵"ijH3R-?yCM\Qm[X{I.+_,Sg!WnK*5JRKs6K+䫧wCԜېlU5ƫm#ĸ 0 馚i*u^pJ9]jNch[>ڲp–jsߥh'6$[ZNHL*uOp8d7AS-\eMO?lGmrH^3~ϑVSU*T򶲌t̞ʒ1[]d)GDŧGs#쪲Hu"7Sd*XkPNfe|2`*]Nz/)\Gc(ݶ}' {!!QE^C)GeMmr*h4NcOeU7&\0S93vO:)lH謅59__SYk%VvZ|e2njZA]㲸)3~ω>TDE"L8,?.kO)=?,)GWb{t4&1LR+=8UV$ڲ-]2l&fe~ mWUm5W`_ ݵl3P}6j;YQ?s0WTrvK+ݓILrC\s ~3~υQAmiOmM뭮U;>S6ŵVfzV&)H8%e6>9%*I5_ BKOdِIҷ`_ ݵe4f*\{W(Q9Q{kU2Dʱu1[iR5r^ɈytNr8LQEHh soMM:u$#m`<gNbok\\vʏ)3mx|y-=UC֊tT”Vϝfe~ mWVȷկU3+*7mo*~_}q;ӦHr[[QJ`੬˒''Uxf [\/bJɦ[$ZEB߳ȞqTMJĬՊ6elmr,+ [&̂L(<=^mksW/{VɋUrh@jƭ *=4>3Y_4kcUa ֮KFI6d\Tk'Wʐ>@EګS-ˈR zёH Rqxf [\.ֺ5u>(GU5`'T(OLOIk)q̏o*Zq,f9F;Ll(^أ{/bQ՛"Q;*Ň*\u2q:Wci)mk8.vߘvGͱma~ل"'SMrߧ3dTi6ƫWȷJ L [>)Y.yn4-QG`੬˒pdO8|6({ey9f<dz=^mks6]wO1 {%Z<3dTi6ƫFur( H \Tk'ږueXIt((U79+*k9mrjxSMԔy[QTP3s6K+2^)?[xUos!ЈړU?VwV5`_aS7cGzE`h#[iS(.icf`ږ ?E=[irY~|3}؜՟}?EXꤑ\_MSf!Kiɠi4У\sieؾxNJNYښSXjkMa?5ښSXjku؞W&n/B[J{4gQ7YLt,Wd!\q|=,$g0W/FG$s6K+G3}|g*{;#͒Fsr__$oʞrG3d\$s0Wr=,$g0W/FG$s6K+G3}|g*{;#͒Fsr__$oʞrG3d\$s0Wr=,$g0W/FG$s6K+G3}|g*{;#͒Fsr__$oʞrG3d\$s0Wr=,$g0W/FG$s6K+G3}|g*{;#͒Fsr__$oʞ@://///////////////////////&LْfI{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M{M2?YxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxLxL6e3c>)pq1:W\cuq1:W\cuq1:W\cuq1:W\cuq1:W\cuq1:W\cuq12e3)\ (?V?VU  4 1Prs!0235@AQaqt"#BR$bCScD%dT?stUjk)XÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;ObpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝjyg'18sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxң;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wNOĄ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wN918sbpxÝ;'wND3M&RȯEe \ Rhͤ%7@|R(E{Ho|>) "P7@|R(E{Ho|>) "P7@|R(E{Ho|>) "P7@|R(E{Ho|>) "P7@|R(E{Ho|>) "P7@|R(E{Ho|>) "P7@4BWˋRU)baMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMILSDZE\b:.-H9Tf~q& b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@1M`)4 & b@f&JJB.Q1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZh1iLXŦ1cŌZhȅi,A}ura֭W|d-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8&L@3Md-4 8! Z#g"O|Ee?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?WNuTӪi2t54NzQo!8<zEe?\$#:vBMu2q˘ ˘jl S#mE[{R(fgK[M=U8gBm8ҍ M-c jG>[S'_qs!Xףc+P|k«|bP7> ߵ^ VSpx"0*Wk42 Q.S >XԬ!m/y"jY$aQMyIrqĚT\!K%AZ֞1S/81.j|ʄW=ԫ:)-a%wU$wC_Hk9s3OpҥW%VŞTo|fJ§"[(QM$[r ĢؔP7bQ@lJ( yv֍rRZIgǩVQSYP|K$Ci|BZziֳTmpo62VK Qn$ivvy2.9~P Y:O#G^ ss洏ig9]QF|fzC&הIIr[ZQ^ig)UoX^YF#O/: ?qDaTipGePE{/\\Go_F^ɝ?Gz%q[۬ԪXC>V:*^ 󏅦rSHxEm5Qk^7^2J֞1S/8IG![pΔtYZCg*eJ;T\""-Tk9'e5l)"" M#GS)L<5{]'#SSGƽ/j%#ఽX*yU%jKu TH18Q#%p*{eҳ=@6R$m u@qdE۴Gz7m"{"rEY-8喷^EOȭBmr?ki=z8/^ 8S zy+tK-$HOY#ఽX*yU}O=K VYIO~&zA|FYEOf2+ Z#)^.C)+lNR M#G\^9沞UPAqjRٙ"p)"faeʺ R}aUGazgUqmF}*YIRxUFJǩVQSY* ¥a4=Xogx3>R.y6[3.s2fBi>3M['$K/i2EγEL[ =c+FWa=dZͫ? >zp5>CmJ;DDP4$O*aW`v[}zQo!9*"9SC6M0G |dЗ~LRZDM%T AyPlĶ+iQH~6.TGOJs-%Y8$=H".UpeV+y[{Ӱ#ఽX*yZJ֍̺,N|ˠDȓ;h?dXBGJ]/ǩVQSY* ¥a4=Xogx3> PL4R++PmkWMQ$mTAeĞnSĈhdm6UJu=WOg|-8o3Kn=IXB&Cq*.2%JwC_Hi5+ѷJW:꫖홞 aUz|Bi ؿ&݋i ؿeK,qD~ MlR_R^C.Q4_*&6|Tj \gzgU YE̯T$;ea =H".UpeV+y[{ozgU 9<^vOUt[uDI) ~Ԉ+(]WYaR|uTr:9iJU(jejM}`_$L||_7! bض-bط*Z{UԼ|T>T[cE[?Js*P0hlԗki=z8TXN«|bP7> K1[*UGr!x"0*Wk4EUqXT!m/y[zp>VŞTo| *jIn49Zg$Ze50z)`=OaO@ʾdLЯH$"x"0*Wk4DEIRAHDrJF'Qg}=V[*IjAԚ =Xogx3>T'"\S$J3bso19wNc;'1g}3bso19w1RMJjRk{䓁~yxy?QI!KUC.e/+@qMqh0#!^i b꧹ i5TO?ֹ 2%SOI<;|Mǭ%ח2#I8+']~8s5T$\zQo!O%G"J/jm)Z(KmFYa]5Š=$\9tWBXCzLS)Ekv&@L´[/UZ^FYEOfӫm/ytZ{4|iD&MH'Ƒ4O"hD>4|iD&MH'Ƒ4O"hD>4|iD&MH'Ƒ4O"hD>4|iD&MH'Ƒ4O"hD>4|iD&MH'Ƒ4O"hD>4|iD&2dTNNH"< ytXw8 ;TzÙ ӊo0V"DaA[+Tù o):WWCy>j]60%|7NR#i]:1xyNpNFYDҺuK;bӪ=azyNt7CEcYwtzu"0 -Ӫ]a7Qg+wN+5Z.PNөVQo4Rj]60%|7NR#i]:1xyNpNFYDҺuK;bӪ=azyNt7CEcYwtzu"0 -Ӫ]a7Qg+wN+5Z.PNөVQo4O#hˈR[lIzi"+OOuy:PZ a`eyviQ']a7Y6mG!11#|X$očc,c7ŌbFH11#|X$očcY8I9HQg+|TVYOĉ7\E26'9 hlFCøP|i9Kt7CG2~-y(| nOŸ$SjQ8:|z! g(E'O6b"P3xxy(X8r>Uh.5$'ML̆pd!ByFo.ID%ރ=:EexK)הHBu" bOEEDy-6P=+W]a7ugyQg+|+O>hl5?e`S*' (zRRY@K,mzeh,WCy>jpSa'+I0xd䜆@gg{<"l'1,TITiNAG)Vp6G]+kSHn'QEhXaꯤot҉hVl=:EexKlj+IIm|%|9m-tҼpZ1xyOG^qlOpvJWUm֧EaZ zUNj0co'`\N2~\+5Z8=NI OX !۫As#6=O%+E.E9붾R^z:ߎ^"e8v$v+=4DE,q\ggdOI6U;VQihM-b]QA!UbdL.=:EexK[i>ir\|z0뺯"MIZ.Tù o)~ mEpz/+. TzÙ ?dQXUzXGy3lao0Veu6Z8fEZ`H*_*t= װx[Z})!zu"0 -h=t'X1 e#*/ETù o)նAyʣ\|'۠HZ\3> UenJ/tʉhYJF\eK\rCvOU6EUd~|+5Z8Jqg㪆 1 ];Nl1BU\ g(E'NȸH >PG\AدtR4ej~#HR 8H%i~nK/CDaA[.}XrKe)O`8F} 6{ `RqRF_ pЬ+$ǰ1򗞝 &U.q|)zaer^|I֢4N TzÙ 8Sx|eaXM v=(ZR.i>j_9_iR jR R.S @):yO"!Uģqe۞ʈ1f]+%Ȼ g(E'N#]stT6G V+)\=*6-V1jLbNŇ}d#gA+dԈ+(\2'-^=_(ejȝ!u&9Z8m&)ؔLe|}%<3ĮtZ1xyOєyطWQOUv(PIt=azyO9ҐOsJ9Ģ`(X&}f$Osn] [WT%bSM>{[E+.!:| g(E'Nӫab{JAx-T~v{MөVQo4 3찋.My*d96!UXG*L5TLeel%R>$i,<^{kۣi&~i+!/sN{-ZZ(Jv+IVq60%|<'MUNU^FuWɲCDaA[.F;"}Ĕ#'hܶ} zJ>fȴ]NK^ʰu¡c6Eʒ"ʹ7褸%R<x[i&1ݶ."Zl<m8Tvx5Qg+|ONFYDUGv(I^(;D^O%m?#RȭqAZ&Ius@S ԦϊUi;Ƹ^3Xs8A^Sf@aN gz,?Ѹ1]'ٓ4m/? rQF*T/$WCy>jpEj=wPVMa!S*lLTڛi~ќB{ ,?1:l>ZV~vr.*'[$>NFYDQp;Ow@HAHX§j"Z=:1xyOr}C9xVBGW=Qg+| 0 EGxVl$B6MyPZ'P|]0||h?ŝoBGo0V -zϜd~w#4C0oRf QBϮ $e] /ඒV!J{O1,?1:l>ZbG`{Dl[Gd;Z?-R#ip#Jq[mFXլMڏܢ4!us@S oz{ee+MzG/S=nTzÙ yʖ -S=TJ95ר^qөBC# dn+۽:5e-c! i3q] %$fpJ S>bVgCy5Br]t6-V1,sB.kH{dEK]R#ip8ȼ a [eEߖ9DR.FfD^HMus@S+,6 vzH×+TWy=X ^]ⲩ'ga/"ρt7CG )W{t).5e"Uz/cYw~bt|YDB-g'G):AZu$'DޓJRh!zu"0 - /*t*4'z+ڤr2]G\1%"='aߪҼU.q|ՠyFI?qXCZȞ3eBEE*Xs8A^Sq>7 ]}:uH9Kqz22`뙆*ğ@{_t7CG 8zdG&UzrPLpCR!$ApF0%|<'MUqɴd;k1 啩yUOUk6UZU+\U $L5 M$ٲCDaA[.O<:*Q<:?G oA \{u u֭s33v<xVO']a71¶rGm}W5I%>_'dKVWH"mpOUѺϑ?@XDkQt1K?8Vikp8|h hqlK.c\;M#¸}&EH efKOaX6$+^y)`K9B.:xN'IzDȾ*De\c!RQ$\g$<*P|Uu8n./G jazu"0 -3-_E$$=5 e5DqpJ1xyOVRS=V)\uD9 ڲ.SW/kWmOQZ088HAJgh~*(6k4H|h))UUqkU g(E'OafuV W/aD,9W-fkն|{,>NFYD૪2Fb]IF\.%^Y<VK D +Oi)Z1xyO4~]W꠬ R&T =azyOVjpDS&ɚ=OhLC.3ڧv0%|<'MUW%ZFSc6ԊJ7]|Ec[>6өVQo47 }]+dJJ<ЛO2D5b?pPoF6~56z6=R<\zW8J?#?7g +Hfj\WW)e)RZ3ַ8īi)F[_BI~b)%=+-Q-_U_jWSTjIH\+5Z8!FB- fG/0e*YiOƚGm7Ҫı'IW+wIIHEr]ty_%`a>k}?|L簚oɺ~58YVk2KYii>4=*he?]jA3m[<>NFYDҺuK;bӪ=azyNt7CEcYwtzu"0 -Ӫ]a7Qg+wN+5Z.PNөVQo4Rj]60%|7NR#i]:1xyNpNFYDҺuK;bӪ=azyNt7CEcYwtzu"0 -Ӫ]a7Qg+wN+5Z.PNөVQo4Rj]60%|7NR#i]:1xyNp"j5OMS&T|D>"j5OMS&T|D>"j5OMS&T|D>"j5OMS&T|D>"j5OMS&T|D>"j5ODV(<)j-sd|D>"l6O'M&d|D>"l6O'M&d|D>"l6O'M&d|D>"l6O'M&d|D>"l6O'M&d|D>"l6O'M&d|DCMC".iC.)j-srrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrkPUhxF^#p/ n7xF^#p/ n7xF^#p/ n7xF^#p/ n7xF^#p/ n7xF^#p/ n7xBk/MWHd7 ĆCq!Hn$7 ĆCq!Hn$7 ĆCq!Hn$7 ĆCq!Hn$7 ĆCq!Hn$7 ĆCq!Hn$7%֑R#N3 MӋ¯(tsH|gt0I]6tDaCӒaWC^qxUnix=&+H"|r]8*k7N/ M{!o%t۽өVQNK^Q {WCio|9>3m{wu"0!it0!t8*~7M4wMtnNFYD> 9.fyD5^QѦ鷃amԈ+(%ӌ¯(t!476zL=WM:Ee4qU׺n^yD?FÚC;IJ{R#N3 MӋ¯(tsH|gt0I]6tDaCӒaWC^qxUnix=&+H"|r]8*k7N/ M{!o%t۽өVQNK^Q {WCio|9>3m{wu"0!it0!t8*~7M4wMtnNFYD> 9.fyD5^QѦ鷃amԈ+(%ӌ¯(t!476zL=WM:Ee4[H9nj!U׺xڻyikI  `񦑬O!,|삱ĚT\G"!4)Rt ]sW`i/ ˔rvoJZFkCDvAZ>~ÚC>\e7%vܕcIYRڌ JmNtR J)%V{ nJ1+ nJ1+ nJ1ZVtDaCӓ%kZȎ"Gr96"EzyUXN!U׺x!\DUJv_XC`Y7Y"0%}ND>N)rIUi-Z_IiGEWCi)EwB )"-X;d0M.7H/!\rJײql)~E~µ~d?ٶ3i ck[xm%)"+E~'~s iB B""Տ;{3x=&.ڣ+0Ƚg(.phTmXZ5l RlQŚ[+wu"0!iɳd"F,흋ɰߘmv# qM^x*k;9CB"g<,I_%6MfUBM~!$6b!E>`MjrlxUvt qvbv+T{;dn9BjOX% 4xb8hȮ4)[M{+it qvc#z/Tts_!lha׸Eapʫ7aɶjRلմݶmoOM;^ۉ.㯋4VtDaCӓf'\PEb +pEƿ)$!6aWC^ٽvcB&NUqƶ1{UZ-rz6X*~;7Nm{TJm;gAYA+%-"F k5Z}5Q2~5%!N_|9>3ټulڧVU+n: y c;OfK}~ٛ0IlɄo8}\ hö'2lj4T߂8-:Ee4SU D7}@$"r9,**L]S*8./{b D'XI(sOfo%.")!9?=TL<2 ѬI+Ea P|"UW8ԙȟ}F(u{ p+}]QөVQNMdZ3>M_-ԕ(ڠ>wUNK"jϔ5}'PVT\D\/{j48aJpFtDaCӓeR0kTiK~'H8׬ V^%-"F+\ֈon\PR9|HO=4|:N y )> Q DSƪ5+7a?%z,>j4E:E_b\,[dKF<i|[+wu"0!i[V[/Sc^x*k<# cTII $ JuUN 31*5Ai٢!4iVM.Y QO)Ķ^ćπ^)A_!Y~"!?WWir6$mÑzj7|VsOfo%~JX|i]M(ᬜuȋ_'YQ- eG)en%]-/=42өVQNN9z& "aWC^1M=;{ͅA럵A, "-SZB+fI΍/ OF;diQ&x xwGML,Re^UN2D#]d^Sπ^)A_!X Rp0T[>S=_ezsRwaw4fzL=\EF:IA΋63&!Fyu%!ha0Q(x/bE"]?*_˲7{R#er968LXEg¯(b0,!zv( y%XkgZ뙘88#+gn^QѧޣMlb1f)ۗ\@'rE)̶R6== #i u2J3ZߥWf/Xmv;FݎЅِcPih)mJmv;CmQ*NMt+aQeNb-Լ/MW}dQӲ7{R#er968LXEg¯(b0,!zv +Xa5XT$!fTBjx ^yD?Fz6wɳCלܨ=o 'ԕJvGe;++KfIKӳE#+ Z捑:Ee4-+-ɱ`/{ݑ4|:N y Ż٣ùDZa?%z,>j4.)E^nxr2gFH"|rp0ea=3 Oh鲔dmaX5UQ1Km#؇FOWCi1a|/2|EqJRˮ Ym>rMYhm;! $/7ҭ|l|9>31רvWV(#"9 Uz]P'Uz]P'Uz]P'UQ)Q`:6F!uT wU@}C8)6zL=\EF.Pn )ǒiM,R2V$!fZDIIs[%U5FtDaCӓ|^& +!U׺x FE6MZZ>=~" MchV("לe]"k5HkM~&Z_tgɩkd¯(coQY,bpZu ߆I(d?+ȕ@O-gHH.=:!O~14+j4اSDQȢA-*jr8ȹ|HǣdkAKK.+h xVɦi)+be/t[[өVQNN9z& "aWC^1M=61/a6H*~<6l 'd:[N?ge{!FS^B+7a?%z,>j4اc\DBɶ)Tg<%L3:ͧIH"|rp0ea=3 Oh鱎v ѲEWCi1a|/j$֗_b7&\j RSBٞsH|gcQ אF?i{^6)J%Va9+OI#[3wu"0!iQ5)5r{?nOv' ਸ਼҃"/:޶+jm$G!FbpܞO݉.(̥aWC^.k]wEd7'r7'''/1O⒵yj+YyLFu?nOSu?nOSu?nOSu?nOSu?nOSu?nOS-BějȴxUx BҵDUGkn/Su?n/S:!|y[3j ւ\IG:GOJ{hq'c<{VA|̙2r:yNl|9>31 BNHIb;q #!ۃC6FuEodJψOGu?n1 /mh)R%QIl{bҵ"I$32Oun=~DQay+JIBʺ!Mm'=a[TaRlO܇D6=}*rƦTay;UB+KƀhJ`+Cu[RS۽өVQNK^Q {WCio|9>3m{wu"0!it0!t8*~7M4wMtnNFYD> 9.fyD5^QѦ鷃amԈ+(%ӌ¯(t!476zL=WM:Ee4qU׺n^yD?FÚC;IJ{R#N3 MӋ¯(tsH|gt0I]6tDaCӒaWC^qxUnix=&+H"|r]8*k7N/ M{!o%t۽өVQNK^Q {WCio|9>3m{wu"0!it0!t8*~7M4wMtnNFYD> 9.fyD5^QѦ鷃amԈ+(%ӌ¯(t!476zL=WM:Ee4qU׺n^yD?FÚC;IJ{R#N3 MӋ¯(tsH|gt0I]6tDaCӒaWC^qxUnix=&+G\kQJJBS4 КMq&8@} N>'Bh4 КMq&8@} N>'Bh4 КMq&8@} N>'Bh4 КMq&8@} N>'Bh4 КMq&8@} N>'Bh4 КMq&8@} N>'Bh4 Кt[j> a"VwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@74 MwS@*rI˲!6Қ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚ})nJhҚouPL.6~nIP7_T *uJ%@~nIP7_T *uJ%@~nIP7_T *uJ%@~nIP7_T *uJ%@~nIP7_T *uJ%@~nIP7_T *uJ%@~nIP ȅW(N@d«kCu!|n7_ Cu!|n7_ Cu!|n7_ Cu!|n7_ Cu!|n7_ Cu!|n7_ Cu J}UZEe Qkq[zݣhG=nv[zݣhG=nv[zݣhG=nv[zݣhG=nv[zݣhG=nv[zݣhG=nv[zݡi*2 qRcq>8Gh}q>8Gh}q>8Gh}q>8Gh}q>8Gh}q>8Gh}q$̂Ve18cq18cq18cq18cq18cq18cq186Jcq18cq18cq18cq18cq18cq18cqu0+t]Z#Һo_n|aM^eu0+t]Z#Һo_n|aM^eu0+t]Z#Һo_n|aM^eu0+t]Z#Һo_n|aM^eu0+t]Z#Һo_n|aM^eu0+t]Z#Һo_n|aM^eu0+t]Z#Һo_n|aM^eu0+t]Z#[7Ln+YOd Hڌ_YLLo x+=rMYeZ7Ln+ĚzJKdژژژژژژژژژژژژ)6Go#W!(n+W1PhkRRas\Ta[Lko k~͔y7hQqH|^UiJ#"1/cp_tǥmHI ԣ-pFL~CY ZΩU ӗG/ȇ{OXK][Kv[;!G"(S g l8J=#ٕ#Ah_0KM$EDZ3BD$Jy5LG!Ghu6r Ϥ흁e))|v|Gt -qlѳ&0i()L&#KX_f߽tv:K/^J)ؚI)'lSDދ] ʠs+l+ɰ"բ4CZL<3dh[Rl_ogLOhq]\|D $~8]$&,cSpɲa7dv6tBAo<ȔEPDG["9OƦGy$+[/)1r¨^9SB#UpM8r%)TjȭS챩G2U>QB-'+/)zW!l mԒТ.TT~Nۍًeo(?=ByL~3Y.g sDJrϋl?s.+jSD\[*ܑmsͧO+(nbzV ̚B`[&O9=xK2uM(?X>k*VH.syѬM#4Rl_ofP?=\c==gvPXƦe)΄0=ŷY\+!D,#*沩E+'3idSϬBmG!YeЬ83~I((GRj`'zB?떟vʯp߅ VBYzGUeS0d+"HqJRqv챕G%q$,2;FF f2Oe.c7Ҳ7GZjOJ!R= ".I8*SJڽ j9k\iF!Ԉ}eD*C?e6~ T43BK¹gZEc=l"k`ꄫINkҔ ^YYSl岗FX5TؓHK̨ʹ*-r2=O/U = ]jH6 o_/]T ZC6K-!3\)yY="9‰mD4j"җWG u:YWJL7V~&D!\D{T&ͷJIe<>vh;bL^Yq{R0-iYeK=cWG1sɠ46DSo^Y~5[Z}NT$=bg9.F*R4ě\er7bVMJ!T|k!a7.UҙXA`~핖)XcҹЛE;oM} U5%FId٧۠m%)"+DZjSfaSR䖷6M/!YT ևhժfI; $ug+="jTpa*2K ;fnMJRRjuNUMׂaJY)%ͯaS0d+Z].v ԦZ:-C(fJI2JӧEĴۦ:#hrT^/ fGv9G8S'S?æSo?[RS8WSuMŔ YӫSa QN*JO70W$?ܛ ^B)e\4ɪ)R˘ð.9ei^8U> 5r=!s;Xt:d֚Um㲫=%z\qP'tIQZ2=EFLJ*UNSzyb!T# Ʃƥ:lcf>&gcTjbX/$b'~ڦԏ?K:¦S"U[!N]Ml# zPTEŐ@#\nYN.BGzRxOeW{RuMxk?5hm|+à)ڦԪs*f̅cT[u_¸gXęm {谩:k);I%y8&!,U]mVҢCI[V<[3Ih9 *&):G)iYXdr<^/F 4KCG xEPYvgo#fKo)%$5$Q?є*ny &9*?}ܶ$iX9RBq)eidrf|{*߳%ǔIYVVhv3B~r#Vum:]:l\Iv>J;dmw_$IЭ8!UFD~J4R+o%=s=HE-2`EE'8YEEe ޗbbRZ$mo]n+_AHXE{+1o ?vS>: `[e$1jU ^\A8j=5JaSpVdjF\AW>[,Kxҥ$}TgOTv3BJεIS'k"z1쾚[*2 _5Ed0,.!|I EQy-~"yb1[!tp4T3RzkZh|a85K*Q%Vhhb]rð>G٢0.^7ŃV'Ylbb9ȫK__y}ei^>]%l"*sL|>ж*նf.C- 7VXc~V_qmނڗnpxSE+9X/% o*s)(&zO9kYGyo(qi[GSUͳ+=7*f̅bZ:ՠ =Rlʹi/Us6֒H.B),{>Wu?d.L׺bKG@a3`mPi@ܯWz.B-TFvr0fJ!Yy:_k˲=|aJ lMAÕs( T6$~U֡j3"5IW\-dfpxSE+*D KkB6M4HX YDד )El\sD.[I['5~sUTv3B b5"YtEBNJ'\wV.RS?Qs,{9l*\/R-!tpmNoʾ!N{G_TꛥM_5ᚔ33m>.(amdtZ/vh"K~u׍䱇/eN`PR"/3cmIp-ca Eإl_oaҰm!oOhv8~YmGW; ^Bjq\ T~ҎTHB S3DAu:,؄-cpgӨ%$2#$8zɈzŗx:'taFw; s!Y!~g*]DmV2RNіTۇG{م)լYgz\]63^rx`l͙B=!tpuNh}㐈0Lƺ^_ҟ`u:;T.h\q1+7]t딥k;K6VYM'|5JaSpV)ye*aSek? ,^QU6?r=uBeQېa UR~%_7dW{ So)T# a%/i$MU+_Q+.CuRezia:ʗQ_7d.S7xI$)x3jN[Ԋrm,ۛ-n8ʥ+\ʦEKq kXUfK^F_ƪ/%I#nI#,_`y,c*y|&ZT"-61[u- .Q_oaҰ+0>w=eAމq|a^7*ny ƪ;쥢>d]M651m[Ci)R#i"BTv3BYiyV.],&I}[3cu>I5 SKfj9OG^ߵ?-9]u2VNFrfvrI촅̵u\w]}" !SgғR*99Y%2OZ 7??٢0.^75G" UV=S-8Z쫈Ð1q.Rn㮜" Tw/8Tzv$JUvG7c0iXA`~텈uW rU0po#+M/!X(~5wLn6CBҜ-E}QPu>]uwJM;"jyN¦a["`k }\|D Lt$eEHTmoѲѼ"RBUHPx+jy9PÎM J9Ob]LYJ^2'IAh"b‘G0Pe\bSΝjF>B!UuK.b3/qlEe ޗbd9b m|ꝇGZA-Q? Gig!eQHUoV~yk|;BG7c0iXA`~큣pbүݵ/yD% >ir]o:uYL jyN¦aÑYWk]) )چg : AתlA|vrUTgS\ZB`*z+PZgQ;*/ڨlw; s!Y5RM!Ig_%tְ-JyG82 xTR òWQ uN)=T4?ZvYylNXK )J9Lόe.8d:҄*r\UһS͖W6IRX\og뿼ogMHB+ȯYSʭ.v^r-uJIU#@H?Ὧ'B_A.%,ATsw`$ER٭0.^7Ӊ_i”a5("Wᵓƪ6W7c0iXA`~R?U{fzWzOj] 5JaSpV2OWaȒr~#FJY~ kYgg+=7*f̅e za*VmB 1Y)+HO]%G-+R#W$ɯFҺJ.Ee",l0.^7ͻzK`'JɶѮjQDSjI!--v=|aJ YJ{hHV]:IGB"?}fBp'o#+M/!Y">Ga!C?ҏ{ `#KZB Q/ לg=7*f̅e zaٚrCT#⹏'\~Шg.^K6_dJ&(j;KETK 7mJf6vl Da7z\q QʂIGOun=~PJڒr1QiI9 %bKG2yP|N JCZYQT="xN"j 4TCQNtEYIt D4so-N\^Le$q+&]~7x8iB"9e!qKE*"At+&]~71[/(nZ|| 1nGɑyL+&]~7#Fu”g %oG%/S=}VAPu~%(G2ҤmT< u'Jq_2d eS}\›\GW{Nv6|ږܦBmGmGmG ͜:j򤒷"U6K#9 2Wң8kUh\r)ZDd"OI?mK[TbԦ5oKdp6BA57 wMc;&X gq3koA57 wMc;&2Xo$j\!J05?4 ˵TUxbp ׫zAUeT)P`MJ5?UTTBzka7zWM 7o"=L.FYCwt޾0v.tˢDa7zWM 7o"=L.FYCwt޾0v.tˢDa7zWM 7o"=L.FYCwt޾0v.tˢDa7zWM 7o"=L.FYCwt޾0v.tˢDa7zWM 7o"=L.FYCwt޾0v.tˢDa7zWM 7o"=L.FYCwt޾0v.tˢDa7zWM 7o"=L.FYCwt޾0v.tˢDa7zWM 7o"=L.FYA": 74#sH74#sH74#sH74#sH74#sH74#sH74#sH74#sH74#sH74#sH74#sH74#sH748~ Ik ,cXmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHmHόV D-B D-B D-B D-B D-B D-B D-B D-B D-B D-B D-B D-B D-TJJB(- !Qa 1A0@Pq`p?!'!! ?KgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgU[UY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=Q #3UVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVz VzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY$p`0UVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgUVzUY=UgU,1'5Y@KbO3|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|8DKTACO3dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dp\g*TRJ*TRJ*TRJ*TRJ*TRJ*TRJ*S,Bz>e۶ցi&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓl`BoXIۆ32dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L0HM`lZ'+< h69$p_ ':J@Rgl.@9]@lyvd~$CċAYrpXhCǷpCv6:5.ch4`<yF 6#{؅"Vf2"PQzmj`"uA"0hAH,\# ;;Vcڔډ4{1#X˞<ׁ$jP8_M A0Kfo3s%&HHO"tlPMtpī̸5.x@w< twOdw8 ::x,e9SF4f&ܻ&-.}SX={ GR;4yr3-7|3'L\I#%3bE.p鯩w 4[Ico&:{.創^S;D}5.! Ee³ԤϠ"M0>N, 80dɉVEs4τM}K3@bw#Inr w#u)3D7K{<wpĝ/ho.qd AH) O2X: 'OBz?s[v@M[=,T&@ 0t/______Pv]|i oGF/֭ v=w' ` &CB^A=n~e3; Iz* 0ܽii+1JL{`N=$h Z}Z _`~J'@,G¦v,v $$0 z s G_ ō X!  +1?@ 6$b9ZLbhcKN 0` ?xA(,K',v(@D @7a1Pi5VSP㼁qVx4z8P$هUGq-!&hPZd@qHѷUi>w (YK.h]3Qϲ )Vb殀˚$WasŠU4s5+10f.j ,EvG> (YK.h]3Qϲ )Vb殀˚$WasŠU4s5+10f.j ,EvG> (YK.h]3Qϲ )Vb殀˚$WasŠU4s5+1F&.0YjJbx^'. )<Ǣ0O%Rڪ[UKjmU-TRڪ[UKjmU-hb#:B+?V(Hŀ>dp&K^z;]$#PMQPE$c3]Qp. (LG.g00;9Vc10KM{{9efYtƠcՄ/ IMVGd$x/(Yp}x@>i%䓸4ш8!@'1WaE苴_KnQbqܷ@L2#Ax#{G>|)\Q2548=p!T!횏̈́HU˺CqؖpuGEZSS*H Xy(0h/.!j>2X#,24Ṅ;GFpie.71ï%`.A$hCF@:`6 /*t˅H|xHB%>gj' #G u6 Eih@LG ᣣ7s˜=FnhȆ4ŋ^25ŝa`CYw(QIHh¥֗Z]iu֗Z]iul#s,=v3Yus/Aޙs<*EvePъv~݇(9y.C6 `v +7scy$ "3(u4Q/ @Jfn-Dqvwau) ~Τ𕘸GE l eкc p%@˃ v~,;G3DM$@6p`Gk{p+$2 l 9!NǰI+p=L}*Yo*Cv\U8 }CR̶eԢkPS,DFMS7HzW߰D. MSݷyLSb]3Hbr<0b4A_ 2@˃;:X3l MDZo;E #:%~É sFE+gwowЛ`wp՘gCߘCpC`eo Ár'Op#P_R0LMZo+W¯c K & ,=KGQϲac|Zq!!5hՊT0}9jR^v|R!&!Mr=j\+Q!vbJ,cKD~4IH.-9L1 ^X!t.Ƅz0cÛ xIp+aWȴnH^}3 :ۏ`^C=,Xf953-QI&wx5 Ɂ)p8z\#%2YPwؖ˘u]׾Զ.@aRL``pLk`G \!0xdoWp [ R}X}X-!#]},5݂ZYK<}3 i@:N,wOY7 I g>wMNuoYt"tm"`;%f Q+D% n[ ,QSy n+F`qE)DRoDɠzܐ,8ndqT;9Vc &$U@LzhqyZOx ,-R;Ef.цxk,1'XCOx@Խq3v/<&J57K.{ C1chH:6P5ۅ]},9I3;6XvFo#P65yG>|!8v81)Ұ FQ?tέ Z&`j)x?q+іmߙoC8\#F֛# rz#. ,  G׭ǟG "q[*Z# >r3i2i*3ΣPGkgb]ɭ+1/{iX;P7;pHh A spi&oB9ߕ-@>e@XA x+1p2Q *{\HZu. 1ˉ# )F i#y;pN35`dWa+u_KL\bq~hm80y%Ls0+6+1Xl d} "F+BG/c$.H8o׷{L#| cb^. \(`C4c0Z3bCEIqy./=D~. ;ld(#`] #LCi-cF0ov#cFHÁ\V ocGtD; $4B ͂/ !с7`DW7񽣟ef>^DIB Ny'l`X6-COYut@2hERS8IN9ga*%X4ІHߥK~/_{}ߥK~/_{}ߥK~/_{}ߥK~/_{}ߥK~/_{}ߥK~/_{}ߥI#!y鯥_K}/_K~/_K}/_K~/_K}/_K~/_K}/_K~/_K}/_K~/_K}/_K~/_K"!/yK/_o}K/_o}K/_o}K/_o}K/_o}K/_o}HP`0h5K/_W}_K/_W}_K/_W}_K/_W}_K/_W}_K/_W}_KÄ #1 5kW?}_@W?}_@W?}_@W?}_@W?}_@W?}_@W?-[qۙ#Y߷3)[ә:yVcTXy.5{s:k;ye+zs<XgO6j3Ƴ/ngXg~,oNg"YQateۙ唭Q`y<+1,<Η̽b5s<9P,3ef5EY3FngR3Etlƨ:\k2uwVyB(<ΞmgKf^αYJޜ(EͲqۙ#Y߷3)[ә:yVcTXy.5{s:k;ye+zs<XgO6j3Ƴ/ngXg~,oNg"YQateۙ唭Q`y<+1,<Η̽b5s<9P,3ef5EY3FngR3Etlƨ:\k2uwVyB(<Ξm /MC7`Ai8 " ̽  Wڣ (`f7^~hT OyEx k;D/_nsnX OGUEb(ŏh)[ӂ)\AeEsAz0`#jO}"O\]S(h(5\]UvU*EWhBýef5E>W`M%3G|@I!F=>Y~N&Aއ͈ = G OsHL̲:d8u;k;/pM.1mj?B8wA  /f',:;"Bڙ`b+<6}R\{1poBnbEX~]_V@V6JЀ(7(=yVcTXw@_]D/NT]-Lgm;Kf^y"Xgug5;`#! ``(/,#{Xg~Sޯ:1lJYqЛ\ 17]M@Kw㯷orVߛ0vnxl#0:@#\1.X8\ xs)Z|HXef5E|ޠ܃k/&$w2MP `/iq| c(}|D Yj:fF,1գ{Xg~&/B򝄝;c嶪Y8 E`X ,\UnbNX|rV%^Slzv,01\}]x5YB(;ULsw'y!s)YQaI`h`Cz/I8C}}63+Ƴ/m<^AC8E+&-A bc"gx5ɦAYa7F#[A'qN傜į&f^ M#=㨼Mѱ$j6;4b); QWp5aV.唭h=NYį$, 0p85YB(;I`=CK,+Jᅺ)ZK1 ]1mv۷ l 'iYQa7 (q'$F4"h ,i8G;V$`)r<.5{o+N0í@<`kX,fHEA5!0 A., 'v,\lmx;k;:_^maIpbP,bnzثlW#'E++w#)[xeg+~LfxЂ0.ma!`bP Cj=w xؒ<-*XJd.7,7U"`/0CCb->`,4IA@Xa:vhyVcTXwq>o(37GLJh27A\01miɍM78&Zp}Ƴ/mᣔN/f|bS֗NC5c,s_}/g 4 cyo+x(݄aPnC%`K` /H< ^ NcIKyܲ7M@\vRocaqc{mvKk7wFP*Q`w!{8 tR]J84ۂeB2E+O#8JyVcTXwl`tcDkn`6 =D`q `@C#d δdBbҚI uyKf^1&`|gnpN z 4 _kz8%E $Hd> ^ !8x: ,oM0KW n }6t,_|Sv@C0."N2\L H)WU"k@LL *"8"7&轛/2gl(iHl P4?l%}&";&J뀘tx yVcTXwT-)@;EZDu`83e=AzO$4w3{b5o.nCUK|O(k@P^0ixk4]Lr0}ܲ7PB@7 n1^dqIyظn'JDU"" Xml&*ŠV̜M^ b3va|ZbO<+1,< v-Tp}Ƴ/n[Jn&ff)蚏kہF¯4:CMG[8 .^}odp7u0lGU"P )Z|s2,7fFgwlƨ5طSa̽9lfҺa Dmc@m/C}Xg~ J6x7^⻐Ŭ7Q{"Lwk F,oN,C 4RN I22\<*1e7>ŠV@۠]ʴAHkf-Qt bꉘ. 7lƨ5طSa̽9lf\0M8 P 9$, J,'&l*n H9F]50tIu>8<+1,< v-Tp}Ƴ/n[.1 mO(3Xg~ J6x7 .`ie+zqBTaEo}6ՆebEpo)YQako¦Ã.5{p4r}<=z=Ȱ x @bLZel*Y֐L4P軰[ܲ9JQ`xaE+,읈`a <+1,< v-Tp}Ƴ/n[lyXg~ J6x,Ȁp򍙃 Le+zr *1e7>ŠVD K#rO0-\3SͲ"K>$H&e0'̎hAa"DubqہdyƐ87ZudrzpÇ8pÁ5 cw , ,\@-#؄3#_z0 >ʼD$T`E$817p)[Ӂwb$b:t陦 lSLEA7>XE.FH/qj!'ɷsȌ|t걚Wop>ׂAD aI}1 nYQateۙ唭Q`y<+1,<Η̽b5s<9P,3ef5EY3FngR3Etlƨ:\k2uwVyB(<ΞmgKf^αYJޜ(EͲqۙ#Y߷3)[ә:yVcTXy.5{s:k;ye+zs<XgO6j3Ƴ/ngXg~,oNg"YQateۙ唭Q`y<+1,<Η̽b5s<9P,3ef5EY3FngR3Etlƨ:\k2uwVyB(<ΞmgKf^αYJޜ(EͲqۙ#Y߷3)[ә:yVcTXy.5{s:k;ye+zs<XgO6 @BII%$RII%$RII%$RII%$RII%$RII%$RII%$RII%$RII%$RII%$RII%$RII%$RII$ܔ1ip@,JI)$JI)$JI)$JI)$JI)$JI)$JI)$JI)$JI)$JI)$JI)$JI)$JI(1inA +;RYIe%RYIe%RYIe%RYIe%RYIe%RYIe%RYIe%RYIe%RYIe%RYIe%RYIe%RYIe%Q.  tR)H"R)H"R)H"R)H"R)H"R)H"R)H"R)H"R)H"R)H"R)H"R)H"R)H Ȧ"ỨRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW%)\rRJW$1`p{:*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­*­+^Y'b"PT*U UBPT*U UBPT*U UBPT*U UBPT*U UBPT*U UBP 'kU"HER*TU"HER*TU"HER*TU"HER*TU"HER*TU"HER*TU" tC_ͫ19+-3e5u^36ƪ0t8γיI{\΂|ڳÙ:VS^g]%s: jjgCk1YMyt'ͫ19+-3e5u^36ƪ0t8γיI{\΂|ڳÙ:VS^g]%s: jjgCk1YMyt'ͫ19+-3e5u^36ƪ0t8γיI{\΂|ڳÙ:VS^g]%s: jjgCk1YMyt'ͫ19+-3e5u^36ƪ0t8γיI{\΂|ڳÙ:VS^g]%s: jj A&ü bc 3Rk=$"aιGAw81 ^!4Gi |_W+ |f+);tP*Q!ٯ;% <Fvx&u8v<t ]h=تmM ha'{cAI=FeSh'^ xC2=G& %M#ЃmJ]Py -&h k À5hb]N;w]+и$}$:l^mV EtG1n6>VbXJ^Aŗ2pnq;y3™ ~Dn3 I4В-v 0w^%䫞Yqr.7HM ^pDV!^z3ԝXP;@uvo74候{' ė[`0IHy4V_m`,#MsA%}U*dJϗl$&qy 8 j @bv%^S@5w %`qɖ!LAEe]U*B𛇑uۂ"~"pCuGc 517ȴt8cC#ѽdHG&z"+V"Dy0B`{;PA dIč{80csEq{c^`䞴9x/&/6v 8؊{7b\^W忬ƪ05񞋛o&\嘼%䘛UmabቕfDęEAU8Z݈!&>pc̚"D=KY+1YMw,ndc]vݥ̍6$p.1.ġ£q5rhGf͌˩XyWZ]wi~2Ғ5A(L"#S.x NuxFvvĈ1ak JRbMl z#2P?X7j Jj<ˈ  um2Y/PC s0%s`^I7)Oc%IID'I=ZZf%kbw8ֈV\No8E1M`->Nh [VZjՠj4pYx:2wւux ˗];/&$rՒm@I t<1 AxthM\ŋCg=y-+uWI{[ux ' .x ^LI"aBC,Q0P; `4`-ps:=cu- "ķ "+,C{A=*[~0/ A6 ݼ'D͇ΤX=p(֋$n`GVVh 8INm}s@ 2j R\sp= i5 @<YbzVR$RwFo@L  ɼ%B!a`k$m0&p! /%08 qYmw OA^vQ k X}zMFz{,/*1 f+),Wb % R.GAy&$sdod@Gsmi.vMYj.܎K= |08S0. ? /$g 9A27QUa:"Â6:NpH W  C7E5C|1k..iyd[{t1KNÍf`5bC6 AM.ß`$H |qvy;=]lٸ+SK@>V+vSkL'zH ւqv~;:8ZV[]B8bo=q.3z+ ^Q]%8?V3mX6pM6l:e5EFƏ ~^Q o90`Fr'ާqpB8cn9yM%<"c<^c{]%oH<04LJMlmpL90`ED8(@m"N?ߏyA>tl^jO6]uց Bu0fl4 A|k17iD>) Qvc ,.@ll"} le;`GۛL DyO?|:0 @y!b * `b 3_~gX{59hv㳨f>J/L5Nz!J-ehK kt(|]\`^h ( 7vfz2'vo&*7PV$D(Dok^ |ĔH< Sp9فƒD [" zdz- ܚx;m:xb6H=9v 6D+qbv1k175yZ$B*$Cy$v,Uz,^;}zZbN z40#%r'b^HX1vz@ (Ε;zcw^^ }w,<ȸVb M,pz\>wV@H-!]:1fP0XN# i#y&;/k|Xd`a:x Fԋ7^Mw`K3|7qr$Dvo'gz|I|/f18DV+nhjjhg|KAtT@C{Ckv wu3I6 D.&L7%iVbr #Y ˀ~lo`0!ȯyWI{\PM|1ҶHD )(]+EGA"M $ DJp@`| p%)/Ia0ztn71xV?KSe18DV+`T2F,aC䙂.uwR0SvjW<(qYmxnmǐ Kf]YkiNrry`AG LaC^5{h#G\  &8):b9APOkp1`1N v'm7 b/dXdVcUp4WN=b,'HbNu! S5@ĩS8t#[h7ׁ鑗X1 ɓI\z?S;i2nUBoFa3 t D%pNP4SbO"2~$v3&)H'^d "NIp ^\I61 ;P{ $M ƚ, 8:`|ƪ0iXCe]խF[ubO`[P9]1ܵ\n,y#øze5V ѫl ܈rWI{\j&k8L<+C,${,@O}H$y%'wy'8S׌XѕAy&$B3 j2G,9!Ϊb[wíۋ{~KrO+i'k1YMx9vz5+τ4K:Hxfb<ȳ2C 4į)-&İbƗIOx( 4=M@ (F@;AR+H"+H"͘AKyi;l3o@P07dOgR3+G\xsL7t!poj6 \{M3\Fti%{Ckv qv ix!QXtѮVbZr:Q,p-ݞf0. C&w^$pA1xZc258'.;'n.DE ~eo18DV+ 8<0  a#ذ|k=+- !/_]`0`5rmE>eb-G+&E-۶̾ JperH-ۻ(XUk^ /Tz.!9JqZ]Apjg5o뤽 g{J `V#&"؛2R6ȶh"^Gi2LT4͛6lٳf͛6 " q!/\: D+aML3t>)3o8cUs:V[^gYk뤽gA>mYTaqYmyf+)3f5Q3euκKt՘F·יb:/kOVcUs:V[^gYk뤽gA>mYTaqYmyf+)3f5Q3euκKt՘F·יb:/kOVcUs:V[^gYk뤽gA>mYTaqYmyf+)3f5Q3euκKt՘F·יb:/kOVcUs:V[^gYk뤽gA>mYTaqYmyf+)3f5Q3euκKtX$`,X* * * * * * * * * * * * * & &|1RJ*RJ*RJ*RJ*RJ*RJ*RJ*RJ*RJ*RJ*RJ*RJ*RJ S ߿~߿~߿~߿~߿~߿~O|O h}߿~߿~߿~߿~߿~߿~L5ꩅDa0 <> YVرbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,X+~psϮ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,XbŋDp@`tw4hѣF4hѣF4hѣF4hѣF4hѣF4hѣF RUgyjPK?>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ%((M0RcytӧN:tӧN:tӧN:tӧN:tӧN:tӧN:u",R_Dό0|g~w';43?_߁a # >Owigq~3H;F|0|g~w';43?_߁aWigq~3H;F|Dĺ O>nnjz~!h"WL=D Q9`-}85_p WuJ> h[_۴n42+⸡\>@M`ƀ_K 0I'[r$ry8a @ ͛5 h㺏CB57r sw@?(Phj쾄K [WM(Ma @\:m ԙ7BG7h제Gϳ|@0Epd:FGcB|4 UqWn PJ.m9Q,% iI4c#aWwUlF#} g2tu{w ;sTğn5GPΤL[uK̦b$*!U)_&f2 . b1ަ1]eC ,eOnq2ղaa;L䭳5CWwiݾ3 ($Ѱv(xrxT(٩ԼYbSZOP (qٝ2UP+FΘAY̊C"!* a0!nTǃU.s{F^A8d!1DՅ43lǜQRXfB@5CW'iݞ3g$X\a.V P@@"@q3*5+v҂P 01XHZH1F76n'9ÀPx)ȑ b>  d)E*<[@,-ӤffAbCVa&iWbg4"7~[Ŋ_0'ɋQl2^ Y6Sa_ (JhraT 5lMwP8UG?䤋k~23|g"Aфw0˖PJU[)lJ䖦CwAȪPR#F DJ!`e1n#5 ErSFyQ I ŠuW5# ( ) ^Į#M U[*/!r*L)E=͖*Z<׈zW++uLa;*4'dh6x@H~EYQ <د.ok[cDj!"l]&]Eݐ;B52͉][tO3] h -)Aj |åh3r #,LQעwǾm=鈔:O#Z'{a"Ѝ3!n-v*wMwX8s _sgs&QF 6߱U: 26R9ہƠ+-A?߯rQ#,A{Y`hI m}+UTTkN' .ylZC%5\gH0DeϙWDC'!p" yj)UJUWzttZ? MfM +!#Ch&8EŅ "5pHHQ(@7?4lҞ{ۯ !Su߀Sـ(VmJ*TRJ*8VU1] oa˝ՈbVhbWc\sU/3QbyB|0$2iRXYRFwZ| 0t[zq$Ny>M|`Į*U^(D+ovzO ƾr>y:0P|㢝&;43?_߁a # >Owigq~3H;F|0|g~w';43?_߁a # >Owigq~EF|0|g~w';HG7_Zo3ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|xTUYZYn`AA*]~|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|I@pʂUԩRJ*TRJ*TRJ*TRJ*TRJ*TRJ*T @$-18%Z %zׯ^zׯ^zׯ^zׯ^zׯ^zׯ^zBSxpTF@&Ӽ` 0` 0` 0` 0` 0` 0`3c +~?20V*7ׯ^zׯ^zׯ^zׯ^zׯ^zׯ^zת,P^Y/@, ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϛ*UA, hѣF4hѣF4hѣF4hѣF4hѣF4hѣF4hҘ`Z|%y` 0` 0` 0` 0` 0` 0`VUSӽ%Pgk,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ/Ӵb?#R+~F9W?>KsD'G׺}(\Nu5.Q^j\%s:>ԹG4Jt}{ߑrh_#R+~F9W?>KsD'G׺}(\Nu5.Q^j\%s:>ԹG4Jt}{l:t {a%ĝIEƍP'&J % |zׯ^z?RYUZ<p=yU>DgtoDǦ엝_>L(vբ`ChQ]NY:>r HM>" d:X2$l\1z!L#uu_{& ba%uRVXU HLy“R5'.XG4Z IPf-aSy -"L@dF́0pG$Wb|:4k\@Tb%gY+Qd-1Ka5*81O|_)VvR,ϯ*]\FJKU_"g>B8D6T|GTp%)fxgo!D~FH&J+u`Pa]^Jw*V)r9`9WM>J?PU8o_řc0 50΀b8 D`_LJa3.h;֗,h@FK8hLp>%d QF dCpκ +0} !; ̥n^ZҒz%/qt,,(f+,Sb|8 _?7<8<sXdA#2`kRIY2do5"Lm* o9KIurfev9,`M&r*į5ܘxݪ@8Prh=:ӿ#LVg_bFhg(U_@!kQ%VUYw.WuMAeDsIYR4 3TÒ'R`2G"҉\hH+ =`BԷ l!F~nIҮzDHDn=UW"ۄWm\ Uz*@1\#.r UbevLkU>@!#Dqm5K4YLUuLĘn\C:4!'$&:.ʊUlPsܣ tG5D.Є"dвRg $ÄȆOљI,jAؕԅ~F4z' h@wl~wԺT"5IQdS[X4Bd.r` a |' =س]~.baTll_dЀruL39~o'UhT<ӎ@ 1!xIr*ŝBE^PRU0 gJU[VRTs3SF5Vjlu.H  u`SA[{+6 Y=͔n«_W:/&s_~ VlBy[%Dh$)J6 rk9GRŅ dsP!n#g\ Pr%ݥj֊aq:Ut Ӓ kajZ J")c:Ք|q%BO/jKW^k_^g8mlΏ+gz?S:~FuP#h p•h-L{w-fI*:t+2ADfP pe٥irIE`ĝbt.9U@ƃNs[9ԅ J*dcu5 Uju4TB+`Ϊ8ҒT lbQm@F琐B4.Db3ugפYώW8-Kf5>,tο_(ryr ̈+[RPAQLڠݴ[]F F؞&O$q:g>Vpl,UTlwf"lKL&>[`c1:5F`(m,dE@tˢgDQ#.KZ%]4yIfȰױ|b+ ]/H]rv0AكA6M2H#X²X^4 u21ZmKlC UlW z"5r- {9ttX/]I%8ocuy@<'Hp#rɔB- iQElzTœ1 FUEd" jPtx>)9PyG0A&2+,|f9|^_^g3FG:h:c x2Q0FQXC8Vp#/B30|5!CuUJhfb ]iݑ_ޥ$iOm +Trx+݂N@zBW=Z @a[8㒇b-$g" M=ڰ4#ݯV#]>H@tDJTRP sDnr5P@= P3>0cp9fy(?䃈a37@l(`3_a{QÀUXĊµln $(s_ }xت`|wS,Wj$IͰ 1D hhxj)ŴReO5Y|xH±,198/3glI@xa՞1' 6NTUN \Iǫ9bq޽ \FpVTVv2 X0Z<&{Ζ{9; \9RpD 4e^`e:AȈ@5ZzPWMH3K%s!R x ۈ4/<6zlaC~W i083Nx:K盎M R!3(FwS,Wjǟ8R|P (<@KPHQ8,a2 s&^1pGR8*&(Mj=?7wjJv/,g<z"Qy'c}ܹA?q"%Y2StؖM<޿]9ܖr4lY.tHOZ"Up7U.VzQ93m/ТA2jKZZf\Q"ɰWDZЯA/H`ZbHV`b&})+>3IL N/20v"ev'f(DwhU 8"06ImlD ^2o_/̊KS )ڢ<< rS8]00Iq2evd@ K0lEn?:9v$U&NRN+D Yq\!2L<ׁF]-_[*8KsD'G׺}(\Nu5.Q^j\%s:>ԹG4Jt}{ߑrh_#R+~F9W?>KsD'G׺}(\Nu5.Q^j\%s:>ԹG4Jt}{ߑrh_#R+gv\RJ*TRJ*TRJ*TRJ*TRJ*TRJ*TRDյu^PT V^zׯ^zׯ^zׯ^zׯ^zׯ^zׯ]1C*pİJ7,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ0`"R5`-1]``y$H"D$H"D$H"D$H"D$H"D$H"D9p{JcU)pRIbwϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|<ј-BY5[:ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|;PBBeEp`a@K|<<.vS8> F;)\GRx#O N.#σ|<<.vS8> F;)\GRx#O N.#σ;6Vnr lj|͛6lٳfM`5<ޥσ*VV*kIx>Unf@y^ q5EM%{D>1 s#㳕U5WQpaFqSa fˇb2ͪqC 0J_T$-Ɇj:l)i+@18V$ r햅0 8y#@P)@>([4<''-nKO6%qC^HJ(/{)&W\짠"Vgj([_d)ĢP y-B(\7 B @n;,]SUEh#fUʪύ @5[bC^2R!+"k7+pk4!DiK#JHŞS}< H:kȕҰJ̕ <% D3W]Ep2-NG}f >ǯABP>#D0<;)殘(tUMC:Uf/zːW/:P7 %`Trt $b$0P"Hk(D$dymϟ%~ QoRσC. Z&~')UG ԂJ(lv;#AT Ȯ(:@Jy(Q)hm\E!Glw}g䦮3"Z0WЮ|/ |+ʇG]`ve8FBk25dm-Af1Z:;h}|2+aB+(@ ThUp% Iej"|WdT#;Mb1):i$53 0J4?t<>& !ks!RN2Uf$5`Wsphuaɀdsɡ?eC- .X o V )e6\J/_YG 7@BtX<{_ƺ_( 7cΝJV(LԽAenߧϛk|G89FL>-y"sjXז 둥f =X?r[pOX`0ڒ̊|]?O* k@ P9C w\(={H$h* uY>\,h8As3Gc|ٖwu2+9N\D;Y\P5U(u\ IbNvڑed',=A <Υ+v>|]?OuS&@rCFW4+V#J!܄A8fWgFىa5xa5B sXa.$*sz\x2+p P ww?p #! r _njj!T҅*"KB/7FCmS鬤W$ ͑9ɱd pOTp8KL#>ﭹ袚)*BMj;*gR~>nn D(J,cV' @:YVU_7jUNUkGG8]#J!g:G`ƅ/P|O7zN\D;vH^Q.9>GԽAenߧϛk|GSWzQvX0fUeUV|e\VSR™v҈i×BEZ&桪S#A+խj/-lPV8܀6" VB:o IP"<$H"DxWR!X (CD% n!ʱ % hSI)pF Gcћ> - :,nv!Y2áԽAenߧϛk|GKF1Yܟ@[YZ%A Wqb/b)(RDQ 'z8%)fO>eI&M!:0MX0[Ad8\+b,> -ONuqs3fӺ/w]:1XVrplk[Ln$ CIl[U`MP<]0%j^2o5>#%H҈i2K,3Oe7 gͧt^."<8qB;RIBHXKV|ƻ~:M鼑ЕuVYC6A7Y \U$mZT#pM™óigEZ& -8 Zd( CsԽ!bLq f?(H[/)9˜h>#3(3*dy3f͙չ ifc఩Dllٳf͛6lٳtI˘zI *I"v 0@vl2Q1M@RÏ 0^x`"`li,A#88nYx.vSЍdJ>|0LǘrtΛϟwh\Bg*uDUSw eT9IQjcfGd56![ Rx#O N.#σ|<<.vS8> F;)\GRx#O N.#σ|<<.vS8pƸ Rgɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɄ$J#NV[ͻv۷nݻv۷nݻv۷nݻv۷nݻv۷nݻv۷nݻzlR0Ꜹ%[pÇ8pÇ8pÇ8pÇ8pÇ8pÇ8p '6*HXfK,J*TRJ*TRJ*TRJ*TRJ*TRJ*TRHHA]`v,Az/J*TRJ*TRJ*TRJ*TRJ*TRJ*TRJJyLA 1XÏxq<8?LJÏxq<8?LJÏxq<8?LJÏxq<8?LJÏxq<8?LJÏxq<8?LJÏPKs&Rc?1wG;t~c?1wG;t~c?1wG;t~c?1wG;t~c?1wG;t~c?1wG;t~c?1V'%M111111111111111111111111111111111111111111111111111?pBR/3 0A/g,q{9c^Xr8/g,q{9c^Xr8/g,q{9c^Xr8/g,q{9c^Xr8/g,q{9c^Xr8/g,q{9c^Xr8/g,q{9bm Me?i}^:yxm}8mfӼuZ>Ϡqwϡͧy=xmڵ}AeCoNzϵkPo>o>;6j׎}Ӌ}vm;Ϲ[o>կC{-{ws׎}^:yN.[y?ٴ>oVu>]mi}^:yxm}8mfӼuZ>Ϡqwϡͧy=xmڵ}AeCoNzϵkPo>o>;6j׎}Ӌ}vm;Ϲ[o>կC{-{ws׎}^:yN.[y?ٴPc?wEXCj0c.3ap:$}#8pEqD٫>pV TS >#Djy^: wj"T>6, _Mcww;wXcww$&9^:T>-6D2/'uL ;1>ˈ7@2ĚS5tGL$$`YD_|zz*Fr"ՠya{+Nd$188(V~ }39%CzP1M{SjW8-ècNo &Z )S͠#:? 3'-f تaj#: ٴ Op J.,P ![O6i/V2,Ru / idz/t>oCK4rwX,B~b9'יڵ}#QUW*0hikMRyUlPNAttD@t+ٙ#Uc]'K6]ѹ9cRP4a>]췠j ZNÍ,y8( z<ʅ@%P\%y `@x0'x>^ZG1ڱ0WQZr-p3 !* seRT `Y]r6Rr˃W&8X#eW0\I@nIvaFh 0} * \).88A-a-FٴzKqXf )A=ӭ{6B>l!(QX D8pB8}+>o)@RT%A**̥g̑Sgj׎$~*Tń t"d+丕U,20z&ȓ(!XA@3,PR`{d0ľgAem_ id+&4%T(N}z#QyD09a *uTܻV hW˔`BTj)MGEPA K)2Q Muz(ϚF L7@(DZ ZriLQ4ps>oKHiJG`&݂$@WOykbj׎󤊔V RkX&B'*ˆJjqqp8c4A9&XUVBQKHpZbQpcgCbPh ꤥ4glt* AġF©i䤦`4 ܡ3mk[ 0zȐzLBTiPCBTP"\v y@ۊ XdƓU6'XCb$*rZSvZFP#8*"9"T'U~N^f2 R#+e+INU _(lZP(K)U("H#!lNķFRgw=xo1& "A@mѠ@*LtEU@$%ɝع.4H *#Z>dT Ր<<-   J ]R.<+'Ott|( 2))K 6# }Ӌ$ b Y |r VMC& e.9`H!C20YA!zڞ: JZqY8|-%P x܉yxdHLZ_o +^eeWqeDK8edb QFOBC"eU((%P!,хkĶ! FEbe q[yP|FhBQQ- %)|4T)XhI]I/#-d S,*{Vu)bŊ \ !? TC٨qy4p{ ;64ԉض`'fpg2r#a|5ZV`~ 4evF &IQh! U)qoLw[y.*#Q-hAA!h R?'f@اrf-կCOBX3'$J"`Fv$n`X @9l<,J"dLw(JjoAeD r_VؠLLB\ͧh (WSψZ73T%6Wmɑ9>os׎H&*@^ Y/bό\];POTxhdSZ>޲wH6VBt8h٢UvU) Xv yr#RW>]췪tHsKTd '0B"B%lH8`ô4hX ScE`@i10/%! ؠZWJQ6`fO'iZ!С>ʎ6nCu+#r׆؅aFlPM6!ԓa\Gqy^:pzO(q!bl8Q0ԟ??kPzԺaRF%ұ"nzУk ( 4)AyӋN -a"]MY=dy9#5yJb c3r;o^&IVWNMVsE̗dD1B8O ql%j `d[ʪdԘlҙ|Z!&WZq9(j{y^:O9 @5HE"K\gZ rBDb^:NI- u+p8 N@fV64ՙpWx*i:/:! !ÿy* ӋnJC}=1&C< !%{D)6 [y*'5zLlN恩\%Up >/(*]0O*}vu'aVxx;6UY$jѾU@jλ\V}3[zIv)Ex1(V6/Aڵ}Ud"}` Y3B=gZ9FD Um"/"œ*#/ۀݬS!h%S2i6c>]췬tkD%bD@!4Y& "q998<΃z@Rɀ1ԕpEL8*=]̰E\j t rINUMt?_ԚXcFYK ZKKbpi)yPʬ덃 V" f 6%o?fBQ +ÃbuwT.`p0|3[zIuIKJ6&3mxoUYo0jB+Uj&-@ qY(6,@1**vނF, yqD!DZO5E$ ZtffAN%JTPdH8X@lGPfLp w=xo[8+W~B e)bimOZ>ުz-4;;t#i> Y`} ΪHpctN.[pU%l8sO%@R Nc1jt?q44@%04 *j;YBpVUs|3--d]jI!6 >j0}j0Vq3֟yy;6W'P3Uٛ mxD!x֦j 9#š^RU&J~A@3-FJ_3[zIq.d*GF91b\DyLZ>ުys0lK\uˀ@X ۗ P|⡀%j0[W֬D,J s UhwmM%[JoQ~ *@,# ]^:VN dD &dL 'yg؆xU҆tN.[r:à"Ed)9 \8uZEfjVU< rU uB P/;6Y9}.)1vεVn!]k vĵ,$zʌ,gH,R8% 4'͂m0dhx!Vu rZQY y";!KB*K6`Q 7qwۉπ%Ujֲڍdj+;Pp^VRwCza暀QJ2L+\۶'ɲ\Uu-B!a+3 fU'X h ٳ#>,BMţmGPITC٬#aX^s׎&f ߿_ZD& Ul!$Jx^WA%gxoB5_ צ7Hdʙ(b-J- EX|JoqWÖZƼܚ%づHָmK0 ($h}8oB\k1 ۷aDAU2dAG9 {=w8+!tʼnx|_qdD$2@kzT X+۷nݻv۷nnrBBZSB@o ;+sΡF2K2O'&'pcȓY#9ED*+vm;Ϲ[o>կC{-{ws׎}^:yN.[y?ٴ>oVu>]mi}^:yxm}8mfӼuZ>Ϡqwϡͧy=xmڵ}AeCoNzϵkPo>o>;6j׎}Ӌ}vm;Ϲ[o>կC{-{ws׎}^:yN.[y?ٴ>oVu>]mi}^:yxm}8mfӼuZ>Ϡqwϡͧy=xmڵ}AeCoNzϵkPo>o> K\????????????????????????? k~#Шe@,*`Xeccccccccccccccccccccccccccccccccccccccccccccccccc`F>X"Y35pq?w;pq?w;pq?w;pq?w;pq?w;pq?w;pp=2{؉ @ fjpappl-1.0.3/testsuite/landscape-gray.png000066400000000000000000001341301403603036100202560ustar00rootroot00000000000000PNG  IHDR+e8(gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs.#.#x?vtIME 0B^orNTϢwIDATxuUsҍҒ"?VPEJRJE 9~=s{33gri[3+XbT ׼ȝI%@"CD:,Y@+(p͟RV<?7KmH-hQG-,iɵ Tӯ`MjaE[ޔO(T6H-ɫ zΧ’ZߚP +znr(!+ + + + +YYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYY@Jä%%&%&&%%FDDFDD&6gΥeYIڵsnyt+TL̓{xcgNI!LkTHYә.5(&7ؕewqj̴-:i ox#X0LiӦ D tpŊUU<~|%@,ȽJ7FR"MH[}@V07d򦶄oVx߾=2aSgUѽS)|wZVxd,oi+H8搛/YzG83GF]S@n#50es쵆w'/=$zJ2a/]jSEK 5 +Y+GTV*ZfSAU^z FfPY#_>tk{4%#h"bƣ[ld3pg}R/\ C;ffm f50w:5奻.PKm]NIM1#)df6v/$jk>nm{rnV}'̺vk}5o$Rcu_sYH_&I'p7+on{KJVn]gMz"50mV}k^+x>}3YIjyRY Wz̥ A+]OJשn]_Hyrf5 +$ЖyZ-4>=05+EKOJL:u6y 5 +ԛs/DOo$j骅HOtfS [vW^yo)/SeQ^cLAdz9RM> BhkU[=M'OQbmCe7M?+T<絪zޞAypiL dmk1M?}HAD-nQ nrj)^_qݧ\~5=Eu B2E0GqiP]t>޵3p K+<0 _^?1z#*=7Ŀ^dɗHs{ Fa-5ݏ8}3ծ.՟|.ݶ7Q_-w1}n'R 3dZ9+9WVyOY+[Ř)|!UW@W7JxMOmϥQYAkQS/Iא`|[W>R64L du6XֲL g6 {]pY̻w__X6eO{뜬@VPiJー_-؛~PcTpY+種6^)&(> n&2Gpve+?.MV~ WKzý=)k@q|c0mNj`2%+C5=m'R^ R~dbnƯ젗llB/M¦K4{6'MWԿL]3ig_V(2FfS 6 QE*iPV!D1xYSmy+dy,%ק.ϬAi|`@Y{UE/ߦ_Qzi_^[ë: !+ix Q S7S&8)@_^W,Ϣ'+(y 7.Oq f6V1GO )ӵsqBpjdcY&+pj|'mB0`w={Je3%$Ѫ],hR70@VA$ Bf dOVK#NGIs1&&+nf p–dluVqi*OXЬfZ'+p2dx*.lI(Jdʓ-5Bc%.C%(.e.&ˈznkOJQ"+zՒVyu Q{ ^߼?+8(jjTH7\=6-Y(+ԥydw5&+ief+.mG2K u>NZR_xIz<~XaM/Aۖ!YN(+ Bz9%qga,q}cuUo=mCe(/D4GqiTH/\r`!@V[웯o|a^?vH%2QS!^jIǫ7~1@V +ڄNFL_? -V^ЀcJKkB:9% Y7]+k3V0\Nh!rm?oߐԲ+ؐDJd#օd WK*{\Ÿqp=v^32`?9?JFY}OqǸղB˩iiYG˟?:`ٍ幘qy$YoQn~qS~ ê wEia@8y#q%8ٺ WuB\QzV2`;HtDÿ[Jt2q%GO!nέ5իn` z6\b\v4C̐2mI2oP&!>z +BFZɫ֯!5u !+]i2`aw% 3ά^5'Q(v|8ijg:JVP#}Æ{G2fu0TWR0nzeq[]-) +3+!.|}v}|u6( 6seI!I=83----=-Ǐ% TQ'?wyN0GFfȹQ~r{c34"rw/<Egc!?}q$j5c{aG3`/IHVTy&QA7_v|ڙ㲆}eO| K@Vy=+kѿUz%~oig+yzZpٵa/Q%)8ٮ ÑICrVL YGP%鸬E]-) +~,f% +,_>ATA?w譛-KlX=0YPd]+/FR&ki>sfs\&{le N?@VRLfTf\-)?ag[gq[E"+#6Z|ɔR_GrEo26 f[=P%w0j2t_ . ./mii 0øM>J!#ũty^3TLW˒m ks6 ::YCğ*VVjY.AytY~_.R~P'ܱculM1tsjByLS/̰w06pɟzӗ{ ,jxBdyWK:JOӫRv"c(rQ:*}᳝BQ~J5_:B\Y=r0Y m畤Pѱ,xKu(Ec)'.^~bfw*;]96([Bɂ7׾rѫ1bڍխv}=˵vWAޓ?ZRDάN Q}HOOOKOOO=~G_jz.uWΟ-Rp} F 0goۄNO69O'k`~Ad7o۔L@>`\d_糲&-P'cnOԠpwZ.b2]7wA 632a8yTu~SfyMKt#+g|JBEbc >Gd5kQ8N/{ҝOcF;Ԁ]8Q'S{TTѪ5EýzO P3Wa@E_Q#w˗'+WrBQɼхS j܊TӬ%@X%^.,cµ/[t*ܟ?G{ +ɚ4e2ժU!-;u5?sQMrL}[zConVӌ_qQY*Ó[G:x+_uPL ~{c|CtV<܌ Yj]pai]E\ɠ)rVzV6U]T>#_BjV\W6G]g୓]Qٲ坐*5k֬qۢ?RUزU/I_^+W.WhۖG iGVO8EGBWYz|M<ݷ-t$IټYqQGx` g:eB?2#%959'njҐbW^vP|MR}5 aSevx %q R3-7%+HJx&d^x15<3m~U5z)~}~W}@ /KV)/ Q#״oKV'[P.!+P%+#x1=qyxa{m x LG/웶Hsl;g(U/͵Ixd!QLlW2֧GigߛOLOaߜ8YK24/|H RWkWG*ڵ dɂ;t<w)|KF'/ pRɾU +c?|/>2cpoI;׀8_'4w/(ݡ>WEϿ{rڄۻd'ԩ#֯]٣;Te <}Ͽ=omnʲJۿuky͚n(f R<1/\2޳7A+*7hP;YYrfڨ>qȑG'K+_lZ!͚Ƌ!ݳwϩlI4/[\JQN9W/pȑԔ!B#K T@ l&DN…ΟprFFFfF"088$(4.>>.>X!gdWջ1j [?@!'/LmʳYYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYY @Vd@Vd@Vd@Vd@V + + + + +YYYY gQ K:}ŠN\>1’\@!l k:O-r5dj1jB"5 Y!PA k#ŰWWZXk'M6Pt9jaI[37)jaEG.ߔJQ,sW[9jaE/\oǩ%=r ܥ-u/[9 + + + + +YYYY d@Vd@Vd@Vd@Vd + + + + +YYYY d@Vd@Vd@Vd@Vd + + + +YYYYY d@Vd@Vd@Vd@Vd + + + +YYYYo?4;CeY)ɩiA!’2sӤ?]0ն>s.9{ٔ,! ?&:X!v9઄N&$g2e2h˧.^xrFFF~aaaJ 4wzoqp˖vfV8kׁ3o=fJBBK~$Wa醭*o,\9tK8;WH2d>tP?MM'+Yd)\T2ZoDq!B*TKVo]jwW>`تGuph%M"JzAh6t8Q!6Hڑ EdY[{۷bMXbsVV8ןy;9J}^ʦ7G4nQ({=9f)+Vװ Қ9:6&\pPV8ur6/;ۭc%KlC匑 j^^,m J֬:‚-Vh6R/8[PKpU'h@_/+3zumĩM\pT.˭.r7h3 ~;Pk~gG`ul~/8auMLwU'+xi. u垿8CQC=mz֣םSwE祕sT!ug]AO3|7Zβ^uplA7=OrG>ؕ^Uz}Wp|EO_Խ&^h:qz6o@xMw85]w(9s 黂s:@ƍlABT zI;.ּu»*z':GEoRƮb\#O7}vHʩ3"=Գcn+Xrg7b\!*k͆vH=5;c,*Z/9:GKc:!wYΎLE537m:1;vz.Ӣͩm'1w~7jZm i''u^楚t;eY}aN݀;qST -S~CXpߤ輆34(wE3*dӬKwR+RVͬAX‚z? " Jd)&{fnI UN'9mJMպf1M'GVPywqVN3HV囩:{>9:t֋&̡aǬ\ހ[N-ѥ}+(&׼ UeTבbxlpO:5?S[ la>|RʥO\hZ~]hx֌bܴkN_ZNq[Hee}ՀKdYUME(7{ tvJlgXlͿrg}&ngN T6_`DDjhR*O^zTW>Kd%_ Z^XUU 9zbRU<9z/vEKRO;Y.QpQ]cmq)r NdL7J5*4XWxlnk?ѝGf]V{U1+:rT/ >>>T^6}ʭ ԪQH<2:|)ZȧL@ժk>M2@*U#.*PK'QuyY꛷ϸ}\GCCB몟USvZ W-+H?cwRoȏ[獪~&}WT=80?M/]沼-©pr~]bԪ.8EVRe"=&Yy* mY)Ԑ2ީ[KESkoHwڏ$N^Yj=ob'C '#R·Jl񎼯?Qm \n>ʝې ҧ6d>pAqyW?j وf']?W^᪘"r0KQ^&?5}6 ~ Se/;5UIU4] vrThGb RX`gPb/?ay56c%ٖό b6ay/oUZ#f_Ōڱ#G`OܙۇR#o\׭BPrOSbo- ?bIݍ^M_7ϟdd$kwomvQ륌emRłvU%fblE'ؑdXQAJ eU^F=wLz bG{?$HŤCF!'>Q5_>Kʗ ,5nL.YA2Yv$wQ#]96c'uQqvFYKj&47v ZdmD[v ʧ VckFZqwLas%7KQj!(7cܸB1 ; a|)*Ukx~])6*/>t7e/nKyu ;*CVPf<2?EY8nPу}]| VidʗNֽոB YAy=35n8r*//HR+)~/)I+$ Yb7%rV duxy\XJ4;qErݡk$lY![ж֠FZi%YՒ?ސ\؂zAUw$ LNא澛"fwȞU.+HF2) >n6o_K$orVNiD9~1%sQW 0No EKU@qV9Y!҉Nc߰9ˣ[R#>XWn*{DzX.d8 U yոֺɨ8kd@{g<w#g;54tttt|r#5ix&GFrq :f=ԺYq`[Vdkr),lprq 7_iiW4pHwHk#0s,8e5WI-[ukdݰ Ij5G.î~&.^Xȿr*X[P#-6K+z !D̓SW]]tj !bBd&Y1E~VP|,!ٹëaX&s'n9+fUb;[g(A+o1޾eB?_Ɍ+qDia>ITG-;bCW}<%Q-0 t)&a҄zz*޸V 9n#Hỵ,s(,A/6JoKPɶ&d+$1"~_^a?%7(Jt!ЊBjDvkpOړ+^֎ Bi $L{?v6E.0'bd;/{h~ʨҲ5d]\TɔڶYAveYA<ezͰ5O}zsc\Ar-Bgj Ԇ2h\V-ň_yy̳`ٰW+óa|) dG=\\[ 7}C[+/G\]Dzm_cw⃺bo% 6ܹb͚lKg:g_g܌dɎLmGVBX_ո`VJޓ;~:_v%Q8_l<v;Oz_WP;/kgLV9K^m R=m,nQ9QAU9[? w@V0E%~^7.龆]6$ x¤1w؃ԜFXBi{v8ZxsW +qyFZ(AJyjdsSW}ˊ~YVwW(*2*(911ᘻ+Os+X$+_ӘwqaX|)sezKzk4.8މ:!3) X ]I 4F[L^O[-޸xY +! X߇xWȦ]yzKD/s{)4^!Ř>2O􄄄sƌgy[eI!S#m&ן{^:-6+YA '9^p {]ֶkzo6lz綑HWi?.[)B%| +X/+矬uVpoO֩ྯ6Y{x_cnI03ԠClG-~q35}r\6~Por H(ⵋy[ddybHO_9I?~E55q2YA!n炦_}kC]+i ZZhNѳYYA哹c>U0y;x_BԨu]g;_9OE5ӵ=ThsƓW;4'k zakͥl}mdHG3~ղ/ 񮃐J7EPcˍ+MdͲBw602qkJtTkj350ᵭTU+{BHJLVP=IfxR K/ipGEoյM3?O]Ҹ7Td)MMQV};3'ճ+rb?5i";^d*:,I.H7?Www^]Q3TV+{P5^A 'ɸ \6ݕW{@EqOK*$Ph=?RFE\U<%dVd5$椨Irdolʦ hC|M DBk²Roz9GVP#"rHqüf:xTP۸҂ҼW:;tZ:"$`bZeDol,{vFQ~jTwf@V0{]\__BLE1S1Y?+" !ݲ'~7)@rN*fj)AyG$adQ6?+ܯIcI'dӭQ$*`I3/)-bv. ȏr Yù꺑rdϖFB[]%io&0Qh [d4v5GKea3_A-7ewg*vE5͑4Aqq@kJdʋH.6I6ldzA攜H]YJ1SNj_7(.OM|>beV0&d]`m{$ ֧HeɬfFru\;jdY6d*.>(w0jde'}!cu1[֠E21+lp+XN$ɅX8g++ϗtg7{(YZY$ Ñum)Y!WfF& +X[Jm?"yu0;d~&i0I6f]Һޤ>"+Vq饽;ڢhJm)iQ&U2Y5^$cY!ו'eڲ1ߌ 7.?x7) Al*,!Rߑ]\J&g7h҃ZYAcҠvQzXyN 9Eҟ*qMهٷeA r>ߤeg7cɄr7r\s #@{TEi6iڹ KZ:.;wbV TYޝFrL>V!GvXK,u۶ZX曫lbLRjǎO+ g׭ܒEx7`AI壤WHej@uW!_ISo*Qx OBY"hX EmZX|||s?Vvy;sEU6_D݇7) -"5B@_䍎]U<2tD0=rNBD/vfFFFFFʅ /$[bT*ڨ "w!8ov |i nu]e엞Sԏ1Zչ3ʖ-WW0e?xuCs~S3Iwkm(eT,qLח^[{m8Sy+aA n2F;}J8 HKIMMQTb M ,/3G&+?5RUJwd;շJHp]B)>.P )`/o̠.] 7ɎJ s9Qj_LmEa\9oR- SjY&&'+VI&C\YT8u|gAZ?P+H ?іk~^xz㒤_WdGu{j̾i-=Z^p2^' 5C ȝ%kq\?zQZ_n`Zxq {B[ cta2Xg,Km$aUu-UO-jbз5,RxC:sKh!M8֙ZIٳ,+`P63eۍkaok/WZ0`>BZQX+\!`]GhMLMaj4Ew^'靣NĄ'Zk/>ϣTįtai1#i qkׂy*Y\:otNW*ST )?nu#~nMۆ8{ާ&Cx5 {TZ iXD:k9 ZGDϦ|Y_ߟ*uhzw:wOuD֢y U2G폦IOVҬG$E£?o ck-(xyyfAO4spzM@F=QWjv7y=~6]O|Yj|_k/tq i~un/Clqgw.S=x ֜OV_ߝKsuZ5(Vn1`O)SuFMM:އaܽyٟnvf_ҝtz)U6d_9 ۺ&9*Jխwu]ꂚUgDuXlO`Mcy{>YZDHӦ6<|\EفʕYBx2pՕ{F-[۹Gr]p uYvƍ3ݷϼU*W nNY>|~ʕ+[kYeY"eR} 2jE~+{v14\BRyj3>k"ÉIII9"0$$$4PBsP(SFS.#### B$p)99%%###3ظ(9NlF"ٳ332 ,_ Zy:\XfI@=đ"0QBNn`+_Z?<;Ъ3y,yp`v6 @Vd@Vd@Vd@Vd@V + + + + +YYYY @Vd@Vd@Vd@Vd@V + + + + +YYYY d@Vd@Vd@Vd@Vd@V + + + + +YYYY d gt!VtI-,) bYt^@ŠR2'SQP \1B jB5|)%ͺȢŠ^>%ɾ߰xjaI'mjaEn (ѕ-ڕZXј-ԎZXҏn oF-,ivԧV23PBVd@Vd@Vd@Vd@Vd + + + +YYYYY @Vd@Vd@Vd@Vd@Vd + + + +YYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYYY @Vd@Vd@Vd@Vd@V + + + + +R*233SeMtࠠNQ`….pSΝ;w dŊEGGGܲ ;9%ѣtYAW%\.:yq2.:B*+R8/J !߻ .}˗(V4Ҁ|DB?zl\.wqYA!DQ.PA].k_Z93_ݺug"7)`e߮+WT2>֯=I'c\.r/bS}}UzuRo[d!)VkCz["ժSU[*i_ Ta^QS'Qf~CJI;P@FzvܰY_`eoXι7Pt\A8O^Wd}6em!mL :Uf+(}-C z[܇VkCҳfrY~GZ 5SKJ^ɫ޻ٴ>Z cѠ_ugY:~.i:>Z;_z6ԭ TN QO_X3˧ ߡT>\olsG~|wPA%ڿ\е_(ҚեvZ~k'r`>Z\`POֆޣ{@RHէ& TaE]"WOK.)BѺKVmjmY7*լsV]EG7P>orpNw/97o05IJt6Z(+vC[w}Oˁ(h^woǷ[c|0, mnbF֫k[ćVkC1um}bGh>\FTi+s ]KBVN>Z*^uuhqA=k_k^mA}lT{U:Y"+hq{?Z e4螏}kY[ZSۧsK >'^޺sqVĝ}h6tn_:u_1ztc }̇kb_ymZڿWB؍Z݇VkCZfP,gy} YsA}jm(NzU*Jz|b [9i;a>GMf|q2^7 ^[ЇVkC w8}ӝ (ko[ f:1R*n_%>>nUR'\cB jmEqDQj<_T5tEZ:~~2QAܗ_f?4 *8bXܬТֆݧ+V鼡L>X'{Z Pՠ5!e7wϟ__EqjVkZ9&5k\ͥbEŻ՜ +|ę'akgE<)+dzS}#+CB{̆m]!5Tl͘K@EF2ru?9[w0|ӫ(%KռfciXp@~wi_A.辡6xQd*oijm(l`%טy|e$.\JK‡0:kY9O KthԲoԙ˴L]WIVp_~1f&ֆ 6+>tOw^vTq؈fv_ͅBlyZ5v %*dߨx>֮S†SDY!4!&ֆjeE'~tl~>uI+eK"2骛U8?e;ɘ%{\BaVu?f`\M~횘7Zʜo_phurԽ&/0Ge^Zu'r~]ދY$7~e`[{Pܓu T&a_گuWUFQ"W Q3}+.yUQa2ImTEo G02+VVMՋZ*ѣR?_>:e|)+xfM筼HtT U[?H#)Ng#n00jMɤPު*/BByف T:,w9fCUFn>.=P>Z=RYi1`;jdץR) zUbjق9:m $gJVf}*:)MUi?b1q wI!Ub"*unB)w_e7=b!t{ό+{Fn.N'\}U& 4q }NUsYB=ɤj`2w?en?EvEjc6h5hBAV0ija)О!|j6и:l g\`XV8+R}Μ5Bh|jjmTyk6kdd8'+ @ e=Sv O#Jd:W8ឝ+$ mZںLV վX9YAl\$i9f6dQ@ke%,z|6}3/>z zg.; nZ_qq=B/mVkC /ZO}fyKe箃]qq'¾\'@Ӷ(.N|kgs8&)OrŵW̎dw|QaqDŽ>_&~rT>a+V*q[mg~Tlf (NGeH.?O+N*,=.v g3idVH oU3VkC 7%+% Rv}}΂Hځ?r}6o3&+DhWS >C\=3c~&~jMy d}v9aS:e6mi?N$&: iAhxkuO숬`7yh\r^_ɤТyt3¢Y!P.8m-3|}ʚ(ߜM/]膩8O; ďÓoq9FçB}c_LZu}OXR$YmqI޾qKduԁ;P7nz~!, y^߸c´@V[-5 ZJ~fJU,? -2բ(EN܁R>y ɟ5W y\8d]73b2~sn:c-38h6tf J0•<"ہGqsOxGw;r: #ioA.DYA :o Axh^ˠ_ɤ 7^|p*+pڴߖ@ްb+L>|Ì bgWҀA~/fq?rVkC{TKWJp̃@sKɝcS, T\o@3Jɭ'ۦ% h‡XgNnzO X4)^Zkڕ=fv37 ]ujV'\@_~[eH "^̉9@W(z%1111FE S!2}{uVkCi?*ȸT-VVaR~.T1aevl݁R>&Hf;p79="WB$~"gFHWDŊ+}벌ģ۷ʼ|}HrŧVkCg w1ye[zz[vURVʍNށN|8S>tpysYӧ8~[ فAZCp\\{V šf pjmh&XRu Jlrڼo-S_׸Maiuav&}sy;&##_?4Z3@ H^RH߽riof O pjmc,_)?(tعhm@+Tj }0Q @3l2gpyzyR}r V򺇐Z]`m:zQVk?;_z$ V:ym +QB! ||;&}vu9Iۗ+!D:6P `e_ }j6dY m HHpݥsAAAAAqqqq*;iv jIΘNz3`Ԧ>w.i'Y"z4@:m{KH0e:V'BNw̞cdՔ4 dE'><]}ŏzKV0W{ZlL`YOαl7WKr$+mu4'5M ߐ~f,HsYTA/A#\Ɛl'F۬ GEyYWlLsW1WKNMs!YAQ  d-Nfǣ :ՒonǏj&= 6K߷3(e?ESȃ.4QwYY +v592Yʶ(ԛ¸(K_CV+!k9*YSUw9y7dgeQO ML{xӔAwa]~(.9>(iNrd ] F7Ւs?Q3E='VjH'aSs5lzǓ(t30> 獽X[)3@~ 32KB\>y[ uEKwPSEoȳ# }6YvRh.Q'_nM[.S } vdcPz.W¥mL(j7` 4jrѸTc('9FJdEТF6S(kͶL\. 6Yә,`piyuK)]OFQrџ$g H2lN 3F؃FfP(;}TA#]^{3py܁-LK?{.9%5%/<4,,,H۱qٓ`wՐ9=!BB, hz#̇39x3njQTRnIVnУ>>BلrdRrʿ)uz Bqvہ!yN!"4=;T&q% e5C˩~.O<&o.U67^Deu~#+R&sNIձEYJ] ~k?ZR=E ql ~Gw]A5S.֬<5cِj2W"+Z,c۷C"ܹ {ݔ#\Ns;ck?1<_كd@!D*bw)r\9"!˖)v_BЭE%R=iaYBѠMd %\.]KyloEֳ6YykG>!pBN~;uyXzp5T5xK_`1k>TݨzthvaX j C3 9 9jV R)s6<0Z>m>PSETϵ 2~lN 噠.o)CONVj-8}~ eQ#\LJIA`IUD_P+{53W/0D>//S([)A$E\J +O|LlIwN"".Y刈)"*NpXDV؁IXNm6ClAXHjDVTiUr)C kOC|Tt!uDY&$+7RNsX$DYpEvY>&3WS'bH+qn#+JJȣBMQYg gddM&Nd7*38dXG1yTHn?uraq'\)kqeMR`I{WxdyE|LyTHu~Lx`GȚ\Ã%P\v|Lҹ~ύIN6XEU\/k@Vo)z$Q'+<P9YNyT82.:MHOyi òϤďMd*!*=t8U z}g9a8yT>):ްK6xU"mmRQi8TlN'.xI,7oa\ zHT0HΑw54@hWveJ`F|70ƳY~r?hYU3)9wn 7TI=^^寤9J*pF2 Y&yP-L jtm |2VM3 .i;ACmYMa<*?# 09*~,XFLURM[{_^H} н'׮ݭz|#UU5Y4*Z:Zrm|I j,! E{n&5Ct{rْ`jT'mS-/WfJo~RőQPvgk.}N*>Pd<OltǏ9rJa&fjv%!j rAVjd()#D#G]Ii4/p"EJ/T'jU!2&$%&'&%%%eG,Sܭgޓ@Tti!NNIINII  +vG{PL^vpg#]L@Aj`ky|x,#Ƽ0 *{ ]Y9rgS3c]Y9gf5v}d;1F\Ke L3*|˂ L075< m2|d[ Zi: ͢gՄ,wRxf)AXR3&ZCeNM#+0S#9hҚ lxdf0g?H\Ҽ$qrd29ՓiCZAt r;i M[ >ٲ%m5bY"5oټDr!WR+ {с?6r>ؗ;v?2!+Ppa+1g76Ii?Wkа/4 d!*T, G7m>I ˲n_aJn2k?G0+djm(w6PΝ"L B;r+v;P F8~|fTreS[ΧtZ i6 Rct7k>[@ *7ͽxcGy_a6ko_ [L2$# G=r0RUr/\-  ?!22ϟ!%yxq~b  "333#3=ńN_/NVඤn .@u|??3YFӯ#0bXRb J-( `XS?(ZXRڵ k@ E$j`e(XZN52>RV8F BVH@!+DQJP"p{&CQ Kp֠HjaE amܾom*iŰWYZX̫ -J-,`R K:C 9YYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYYY @Vd@Vd@Vd@Vd@V + + + + +YYYY @Vd@Vd@Vd@Vd@V + + + + +YYYY @Ef6eE OOLLLJBEEEE%'%%%%9oJ#+ NMJLJL:x.Y ,\`xHhphHH"7++3+3J¥˗.8CW//***_??LLJLLif/;zoͼvPRR~>/rٳs5YE^i~֒L*qahOݱ].{tk߮S0IsVN vg`U-wKBE9qB[T#_cN^ tA k*lyym5׹M}O`~MTw}fM%jyg]+?ט?0H]z;_&+;=5kѲ_7ʂ^K[jw]̜s-smyAETC!77Ds%͛:#EyǍ]4٧1-FU5t};P/ ~^[פpЂܳO-zkzzvT0^P?{ۡF{Ԉj{,Ľ}_?0"ͯPCB!*DK7SרPg =za@sߕ mz.ڳxw5IDAT_; 7#/A*=rʀ(>{tfԱ^*]S񢭃=y[y<0ӳв 5dAOv4pm%G4pKs(7Ыtﮧ_='~z`_[kgʉBU|Cn!7q!5{x |f`T6>6`i3*6*>,ˇOљzF O\'6B{y[>P B# *<`iEF=ag3*6䑅Nh_/Ux^UY +;$մj|l4[$ojvh1+_>6}w̅apƝ mVkIZ=qGy']Cjzi%/hȟЩU#&٬~sÛOS+oF꺁}y|/_u_;2,4Jn MVi#gFjifCW@/r}wES$H;}wVr^w]˝8stUkx)g?˃hR`:_KxI: yWO˅>&s3; VE=€w $H<Eatا>s ]kڟa38F/xB/[Gv&LucWgx>;cy+ i73:{6Ϙu>kgK^)*P쫖 n*};`eu{POo2ty ףƏ9i+4˲񪚗sX=hnVh4k+ĨM,瀿s7P_Kn=tvWp>hV?o4\ 4zԛb祡Ý06۱@%lxnW"S/WKVjg@O<)>D~>n iu)-nÝ~ gB͌\{Z+G /;ug "u86L_Ф_y5<SeLrжxJݟ(-?=>üP⍒uww_}ԩ3)iiiJ?(?'n_rIv&5"i6uT;k.T?4l>uP[?;_BR. (zx M06)3}ؘɇї_r?_Ʋ6wjV{I#Vjį-SK͐?\q97lLK#_*/53$Kyڜ7`AO-}BƮ*/Oc529:*D$zd"#+>heКjx%n"yq @>7^*Vȝrя=.?N&NAOs{j% _ۺ0k#J^]>UvͿ~$;xN!SYڰUI}$yWΙ;PeP1<{0>Y'm+=tϗF2x̏*n/Y%wK_|ehqڽWFGqi<|%ޡ9p1Ͱ$?8Lϥ+XwοD3'wvyqU>wueum5.SqB^c[?#FVkaF.vbwmޣxN6Sw]p)m?(Otulw1j/&딋%MwϽ'$vjX᎜+wwSp'wڐ?~QF^nxVzgǥf֤ Bvu+g;DÍ\o$&QyyO.SR8"ˋpֶIv"Z]r'i!=d|foΕ23:+wHkTyǟ)?~_ ?N;mU!&wN>U2l]ՊZ쒊ם\" ^r}q.ɝmaf_6) dWٓ%@OeK.1޵ah_#._'`pgm2rnl>AxNuýٯ]u$Z=~ #oF^IQ~q@n|繁Bz~82 B!,kS 1U;vT>ǐ%_Sr9>U=hFOvnc鞺PvNBuZ<ZO624'?ǺʏQi_k2[%%!4Iյ$?|l_Uus5;}KgS3w^qU}KIƼ)/IUqu ._kT%+Ȧ}\d`qO6.+Bթ_ $x!Ql'2GKFHgd9i9fHŨUizVuף>)5&8A4 !H8ȝ9ˆb<ʋ/Knl弬 a&>1ݯ!l"2O|r8}6 @_R4ͯ6 >}ROlԖ,WuGɁkc8$!6ѭW]=$q`5%9ӽae3,+{ˍVh=Cq󔗹W*uk7B$׿,JpRw'Bq|*戏qZvۧt4[yyC<{zgy $rN}.+f/BnƝ#%\ wo u6IQ1oK|] !ȸP5υZuLT^SE8AudW9,u ݰp]iwgakJKw&$\5zmPVܵa6@ˇ ).=aPg?5Vh$mSͶl@ed2KMabq6.,i)G -3,^FayEi6Т1 5 sʳ\ݦt/_H?0USHrH`3 yQ"4aYQLS_U1+L?/o&<s8 񟊋/ 6\|ZY얺V>;Y_X#+&W\|p\)FWSB>q PXzښ* c K,,jV^>qcFOQXzF= lPbQa]kI&g qhB! bJf>伬 ^t}'_E ˙ϸ;xH&<:mWgCd5YY90+zNxO$M 7j}:Y ҒMq Cm M\uA*\pϞ'\mKzH"G$Td e9c3gK9IVS:s,6'?{euW͙ 輏\ 4Oyq [HfpFZY>eE#\B!p?+\8qhVun[ˬYou^ZV,'>YߵD Rr.yG;n, hQaZe+fdN]:3B^7y+ 5emy qS7P7.}甶Y!{r\Ŷ$eMsƥ}$ u@VPd5J]TYO՚z%g on(Pÿo}2I 1UvJ|FOuɞ^c&R~aGwGi}$YAǛ$+F E|1KYYA~˙R_MRAino| 4+7ǺIZ4Źh6Hc߼4-6w8j7SrS>g7na}%qZuG ,INzf1++2 o7r$Wbo߮XźoOZ-엑ʙg7:_#yV~z"+!V^w;Y*;QyLaRVǵ(J.U?i%rBBBM}k]ߙ{<Tɜu?g:y}Z[W+:wHBX%ɞ$&|^ʞIFdN\]Icj`1w+)ܿ5^y7\c,mNvo>lYrsBک;WvXhH^uI%T# ΜqmX){ ng:8'Rv9V˗*A62#t}~}Ȳϵ5 +H|kbVqzE ˳Bsݛ%gq>㘝J~l D6ߕ4HFdb}5+hvTT<@"߿n|Bds|M *Z@:GBmURt_0F2Y{Y׬GE|B/k{o/jcB| z|Hgl(Ei`dyUzVA֧q3hvazvi3mO)鴁RةA/Ͽ爴r%RIێ&>~zv>;6dW{s}ѡǩN1̤ ;z^Gmt&fLyQx6vs8ώ+hF>4!n~(Y>&o;ʨMi4uhd->N)]5ԬƗBٲ/|=BvaV )Bd³^OYe_ZHgힽC]|Z/m#kU;\f5Y1NxԞe cbwpVPR߾V Ys5_zpkR;ˏ [S՗H?}5Sޯ"*.^&s5]gԯOΦrZ;Cfgt o6yejw Y; ZI6o穑f'S5SѦ޼o=i1{U=1&?dg-TʧKpä1*TpsOosT4]w?s Wz~!m6$ۋp1+:wKD/zInۼS]|Iw~΢zMp;x…BjѬG;׸Y%8!?Q#E<8(ă|oۨƩ~]7B96?x?G97`2P'\~Fo%B<{es"BcvT_lE*/];^crpV5`Jqe?Ss(=|9u( ֵВ Grnnoicsn_{bBBbHttt1s2+,%ugS#xW//wRK\X~n}YdY@z!+h.nXijv[U/ko?HIF ja*#+h.bA-y.P{~!+hNiٛ7)n?ye:5%1+UrRc;uQ1H@w}$ fS$ݴ~U]ΞUMꪕ鳽y"(ST取Lv?V S>W.Vݟ;\ W09+1J|PN6H/T_9uKu5Þ\ I!(GVB=O>geY!Ze?8Yaß Xʒ^.-M~ ;vswۯjP5az9n( jOzY7uH&d f$a˨^_3-[{~0|X3t-46z<7w nnݚ-^AmV`\pʎHG1Md_Vw8$>daWAWK'H,8:SJ 3O U WZx66X,D/랯\I\`ɸBQrrV[k즌ds-yzE2)+\t'Nb(i*>ksOA5Iz^$m0K.ܞmP$'rnWٍ\NBJͤHfeKq`Io+.?oѲ&ɝxU=Io2dS׍dŒKsqOHW)J C9 v]mٵbos.Md +8<+l,⪪@t@d}ԕݧrdEҕ!5I-"`nowWYZPzVk[(9"Rn^[Rp@2H[kׇjqk˸| قk9'jedMִn`?wŔdsAe_VPt._e[H$ΣHff; h<6^wIZq]qYaY.iBԲ+vo,yZnsxVMEd=% مsR)OW S`rC9.ɜΚ?Gr7^)췼M"d.{)rJb;z$iͣ vHyVxFb'NdM~SfI{.(~>Z#6  zy j c3+BǡW)o}_G0RI >^0}+V8#o;ӫ1u-,.m?s&xv)K֮Y}]zQ#}?+/lIjvՋ>fMBku ϷT4v$]Vov8ݹէݜ.ٜ33h׿M3udY!Z<}SMfؠq*R"^: :r˖-[ :M/9*_9-n&66[V'?s 6<*K~ Kq1{ kx"%Ǫ]c =JKKҪU֙|ZSf:ey7$>7K=BV9I,v}VH'f޶őG}O}R~D"ɾ\XP4)@~`7W,ϨqܺJ]QBNd`T,[l1h: Kxω>h6]jLV|ٕ~ Ñ΅ᢖ! jU}/[񧈯 G"!>|S z^~= KnEpHVf͈ڇ[pnm5)Aom\!=]qY}U^ɻ\ ȈgE6)R=SǵmT4 EτGUY-+gžۋm{8%t?OؘNaYmӤgT8 y==lq3Y!aXX4Қ {i1*&Λx}4Ѳr'+ÖFՒjvCS<> qCi¦IQw!fE7 e)9ˢh }<5>CW=8JQ4-swQ?#,+3KY!mVݼt˷cҡwXj{_:>oEM>IpF}D A}|DfCN@劰j"e_o]gG8Evz UHSAyz0k^-^al⒬شbUʎy}»W޾K=Sӌ՘6e\Qe7hW ?>䇵pec֔; {P1 TaO}_, A#wѮ~leMea-t:V]q۶\q^ʑ=y|n^.W:nOǨo۰aʮ}6vmw\sK!y \c9+5s,U l[Umpxsk ^Q_о݂U´X7a.UȎ=k붟Qٝf&]2:[:CXjU)9{uL;Ӌ˦m7<~d|èK=ruYUa*[K_=rs6ڥsL[Wreyvg}?mKL//Zn..>`^8h,ZԢ2NVؠP *j܀{}  + + + + + + + + + + + + + +  + + + + + + + + + + + + + +  + _J+F*UY^-h"ܳC-RF PU1ҝvA3C+B ޜdY_xP )/Vn$h}eXIfMM*V^(if,,+?*%tEXtdate:create2020-12-03T15:47:23+00:00o%tEXtdate:modify2020-12-03T15:47:23+00:002putEXtexif:ColorSpace1ItEXtexif:ExifOffset102sB) tEXtexif:PhotometricInterpretation2+tEXtexif:PixelXDimension2091sԵtEXtexif:PixelYDimension16742IENDB`pappl-1.0.3/testsuite/portrait-color.jpg000066400000000000000000006065401403603036100203510ustar00rootroot00000000000000JFIF,,ExifMM*V^(ifUUUU+8Photoshop 3.08BIM8BIM%ُ B~C  C +"  :SAvqo]Avqo]Avqo]Avqo]Avqo]Avqo]]'f"ƂSSSSSSSSSSSSSSSSSSSSSSSS>I*#rZB)j ZB)j ZB)j ZB)j ZB)j ZB)j S,jO]-BtsI'KG4-tsI'KG4-tsI'KG4-tsI'KG4-tsI'KG4-tsI'KG4-ؤdEo~MHt;r TeUE^NhYQ c[lmڛ+ SčNc.!{2_-MY\E_nT[QUD慝25Vݝo8Ht;r TeUE^NhYQ c[lmڛ+ SčNc.!{2_-MY\E_nT[QUD慝25Vݝo8Ht;r TeUE^NhYQ c[lmڛ+ SčNc.!{2_-MY\E_nT[QUD慝25Vݝo8Ht;r TeUE^NhYQ c[lmڛ+ SƤYEkn]BdBQI΀|U6f5su}ݽM6f1θ,؜x2QmEWvtόf|S2ܼ{5Vݝo8 pf?MvXčNc.!{27כL=8PCjC 蠇pTeUrU=̖X`>G~ 6bSA U{Q9gaGL)} rJókU1JM{})m`l9C@MvXčNc.!{2x,7(4YHs< rʜ-_(J@!ƻܐ@+岩1 עrk}zնQmo7:!/@욀=>JY:Il`GU{Q9gaGLmayH}JókU1JM{})m`$g=3MvXčNc.!{2`_]6^(ydX F$T9WABRx&ֆʦ,Ʈn@~G W[rJaP]qbCc)ډ ; :dPі 2+[[1Vƶ*۳6WA lh惲$n:vv ِWB>RdV;U!\ρ pr|U6f5su}N^ ^l 6Kp}0L5jmoȇπ*-sBŽ>A5[J!G@FX7VZ6oJD8[?5Vݝo8&P`D/4q#qӷ˰^̀ =~%zW]4^_ذrTeUVҋhR>jѨQmEWvt@ "Q f&pR)mUgjloN#k!I8 eHt;r 扶U[T6"=prmAɷY&=f:g-_-MY\E_nX] @WJj z괒CIPÍ6:טBP j*г@Aߞ fp*3E*8ډ ; :d+}E/=S-mRjLkm;Se{}JqX-=7;qܻ둯/|J+˟ "+Ȱ,2 ]90@+岩1[ YoU_{o_wa!E^NhYQ :2<ƶ*۳6WAn]Bd[*lډ ; :dLkm;Se{}JqX8یv/f@+岩1 j*г@ƶ*۳6WAn]Bd[*lډ ; :dLkm;Se{}JqX8یv/f@+岩1 j*г@ƶ*۳6WAn]Bd[*lډ ; :dLkm;Se{}JqX8یv/f@+岩1 j*г@ƶ*۳6WAn]Bd[*lډ ; :dLkm;Sd7O}X:>zp:>9p:>9p:>9p:>9p:>9p:>9Gn\F)C8泥#Δk:R9H泥#Δk:R9H泥#Δk:R9H泥#Δk:R9H泥#Δk:R9H泥#Δk:R9H&c#k H"(j B(j B(j B(j B(j B(j Cy}UKxDj kx* kx* kx* kx* kx* kx)6.c'ܯbxؚrG uˀq piPQ REwv$^ ͗dMT )]'݉ k\W_Jr+Y$R?fNl% h ONWGէ=ޝŧ ŧ ŧ ŧ ŧ ŧl]_ ͗dMd3 =j91x_Px@@SSOWX0\ReǎwҺ884(rMQKa3?15=a1P9KwiO,)z`3Ow0@/eRrUslQ2 'z?Ld#dy&U=| *>[ 9^۱5u uˀ8^$ؿ،I$: C*WW_J<ޚyCnduJzqc* h'Ij0o* ^g= t$sБe2Hr/Ц+~͐zJ&AT >y\?ne)w`uXnj\s[mExjEofoh^(@SSOWX0\3ݏX^󯢚 ǫ@/AD`1<[=Q x|@'Ij o5^;Uw [mߪtxb| !9L@"Ky\wXœ4.QzXըfĄ͚nuT.FT )]'݉ k\)SyeP~~x]M;BF=_N * #CJY^ΛP Hg#gJ8㭛7G[HydK֠YnhXr\lUOhUٲ/ID*Nn6'='Kt iR,tEhD_6Yҋ6A|uU }jrbjfw5v_ {Igd^ ?W#/'䮯nԩj~4O6ïLON%ntT r+Y$R ?۔%9F~aٲ/ID*|9K7 "ĵoZ+f[ Xr9@SSOWX0\ro5d_j,x$=Wzs7Z ǫ@/AD`8c~V|[ׅjh#]-{̫+ /:KB9KwiO,)z锂")p$0sJl96Bse(S@~i|P-k-+..0D0)f&F۩;S;ڴP av+؞;v&6ap @~789zTF5;' oaѥ`9юZZlnq^]um_p'Ij9'c-Iv^UQXWQ0 ͗dMI⽳Sm}>{?x#X:~4NViŏ[,) @SSOWX0\74+X^~0½=5P/\i4J6-(|Hvq{m޻i$0zTFs Κ.,=HU g L:˄ӱu]pY-ݤWwag3f`CREwv$^̛OR/@,4x  ͗dMоhK 7m&ؠz[HS.6T )]'݉ k\LNi+M[ 9 [^ڮ$B-Kz*ފ-Kz*ފ-Kz*ފ-Kz*ފ-Kz*ފ-Kz*ފka)VEuBibXb.)bXb.)bXb.)bXb.)bXb.)bXb.9[Ƨ[((b/X((b/X((b/X((b/X((b/X((b/X珻p~,hȝ\Z1WUţqh\Z1WUţqh\Z1WUţqh\Z1WUţqh\Z1WUţqh\Z1WUţqhDJTn;=9w߳Iw7/e KMv缝MSVRk(;=؉P;%sNd&ө+D)5”~ezWUlD@vAwGDd]9'F2vgaiJ?dC6"T ;n }#}wA~bi; ` MpY^sǡy*~]>~srQ; d4`{ьXZu0h&R,C͈ۿH.zH_@9{(_Xk=Nl-:B\)GWuu^fJmߤ~Ͻ$q߯ܽNH/,M5tc'a6~VLZ!IK+x:3b%B{RB49s gޒ8x^ׄ>?gX8sGZ=5!? wA~bix}G=ax=\keRtc'a6~VL疰u)1y+sI5BI~(ƫkx1`=r*bJ\{j $D MpY^sǡy*(|a? akqzoyHm߿>'m7r><;yS^-qq,uqs 2[=U|=QYYsF̪r>n^'{$&O #:ypz_<}uom e= me %jۿH.zH_@6B|LoBYg eıs1l;&"O'jY|dΨuGwA~bi e# ?W7x}J]ZU l:ypz U m,\lmIU~\as5K{ьXZu0lFv> 'f_M`!V^J'[\mϯ!'“W~(jukVZ!IK+x:3b%B z׻v%bb:8+M2[De[3^c'C^mߤ~Ͻ$q߯Usn-,sZT:ckq,uqs V-R!ۭ ^Zlo@Kd]O:n@9{(_XkΩW\Jwˇ?`@*5y$<Ά˜SL:u@sֶ\Ou,9'F2vgaiCPDgIp}mʟ14lmr>d';fӐYo~r Q%wmb,[Gpß0eK.YU@sֶ\Ou,9'F2vgai],]t#3)+sI|eL)M$v4I^ݡc_Z!IK+x:3b%BxW@q95 4zdeޠx3֎Awz~]>~\K\lw:՞, 8%Ȧ] Tzakio,swA~biZ@61-t2Fa [hk[.'; `.R ck$GP?xka;,XeSbTZ!IK+x:3b%Bu>x_Te ݓilΏW:;n }#})sp2:u9 9EAq,}p~5D9'-~esHwA~biZ@61-t2Fa [hk[.'; `.R ck$M5cl]鐟h&R,C͈+/`׻+}+fg/i>78 Z;n }#})sp2:u9??=fO~4:e&2÷ޢVT}iܽNH/,M5T܆"Ŵw\9^5Z m>\keRtc'a6~VLAJ_a32lmr>5>aj%54!mç.4 eVEϦ?3Zq=԰缝MSEtuҗ`̧[\mϯ!'ȪX 6ԫknP2+D)5”~ezWUlD@"t~Ӿx篃㢤Kr l8mߤ~Ͻ$q߯]7XfB&N``ǀ>ك=Aun_COs0wA~bi1^.uGׁѕg)P c'~KF 2pp缝MSQhQU^Z*CU^Zn_WUyhE^ZWAӉCZ!IK+x:3b%Bs gޒ8n^'{$&y:1?+ N Q%w~srQ; d4`{ьXZu0h&R,C͈ۿH.zH_@9{(_Xk=Nl-:B\)GG|侮*FL2ʌS*1L2ʌS*1L2ʌS*1L2ʌS*1L2ʌS*1L2ʌS*"GceML1MMMMMMMMMMMMMMMMMMMMMMMMM9"R=Kf$ebFYf$ebFYf$ebFYf$ebFYf$ebFYf$ebFYS D2߆̖55555555555555555555555#٩~ϖT#.wa]dM\^Nd}݅hD15 9eXE>b=c1lI9ˋ:ь=b>Lh@1Uvǰlf4Mc)7['9q{@":1v5Dz{TV|ɕc\0 ƀile&d./|_'F2vn"Buϐ2}W|1U 6MlN\րCQSYY&U]p *c63ɱ|ٻ c =*k+>@sʱ]|Wl{c@462ust/;_{7asZ {!GMeg|V>u>y?riIwۋBcQ'+c`h2 {$;8@#2Oqwɱd {<}c { 1Dz}_'F2vn>~|/9m?<|^?e;Ayښ|3bq~?a@@j5XS|:Ao$ؠ+c`h>B1UK8>|}Pw&<_06NfNHBySnT. xlI9ˋ:Bm5I.$IOmeQ 4r?Etc'kf.k@u֎bcwkN@sʱ?`S0J|@Px^h =N g/? Vm^vrEH.]cf*+c`h fL}=7sZ!|}Pw&<_0mnFBʁܩ(\462usttjRQx@R)%ۤI <45Ro? .6rfFOَD=b>Lh@ F'=qFL6&WnJg?0  ǀ۵Gw!yU+5U6&@>b=c1 Dl'/鿚N5Tɏ S33l&HpٚessaySnT. xlI9ˋ:jXtiP)?ʹ>z9wRKI6@`Rx]8spd!N\րV3wժQ9ZKSX*!nFQďJ}~[Gus$Dz{TV|ɕc\ ðPs' 5|'Ziջt09 ǀd8j.R"fYH@dnHp]\0 ƀz,'%Gh ?~M@ G5Tɏ|DwU5q밳?8RY(G=VNaBǀɱYہ,kcb쵷gR)%ۤI lUԸD|th{Մ郞y e|yukk$܀ь,EQ8Y{O! o[6XYoԪ)TeI 9>DZ.=L>~rQ0a ʀDz{TV|ɕc\3<13,_jiy!e[sv*츯?RQw@Cwa]:o*iB<6Lx&`?]JlЩ,zS' S%ܩ(\462ust7f]Z R)%ۤI O"Do~":V7X#Y":1v5Z{PTA V o[6EwQ\%M4=>Plԭҟӿ 1^''}жOpCQSYY&U]p Ϫ`!}; ID/;u: z:--%Cxp<_..ԁwa]:o*iB<6Lx&d^ rH?t]؛*@*zH Wւܸile&d./|nn&:RKI6@` &6y>dr濾Zuf>]\r[V ̓[/;_{7asZ uD`liKE[y_tKu>7LT"]>Klu CQSYY&U]p Ϫ`!};z<ozuFKe@1Uvǰlf4EvM! ꃹ1qZ 3&燽bile&d./|nn&:RKI6@`"ycNta-/;_{7asZ uD`liWZEUmT7&PQ@F=*!a-P` {!GMeg|V>u 3>\Px^h =2Cw!VȦg iK9uL}% zn#WMͶ0 ƀi!^|}Pw&<_6ܢB}=]rCQI ׆~q[!vcBcdM\^ݚMvuj>/Hnm$'>/Fv'Eb9 i?udtg,$N!(@":1v5Z{PTA V o[6teViGbu*^vURX9?NfcJŵQvw&Dz{TV|ɕc\3<13,_jiײJK> zwu|Wv<\ *c63t"T;yzyPmAܘM|?_yl)ySnT. xlI9ˋ:[IέGMtm`GdcN\րhmBQ*9X*!nFDp=@Erԁ\SKrwUCQSYY&U]p H ]#ydC|\&Yj"{sut'-)d|Ӥ'LAwa]-,Tѧ9X 'PmFG*&>puɱb("ju؊M=At˄;)^/GT)oy `Etc'kf.k@'zH`ڒ1I%7Ao|Ac]0U0Ja)̀CQSYY&U]p *c63ɱ|ٻ c =*k+>@sʱ]|Wl{c@462ust/;_{7asZ {!GMeg|V>u+c`h&RnNr΀Etc'kf.k@!d(Xϓ*.wa]dM\^Nd}݅hD15 9eXE>b=c1lI9ˋ:ь=b>Lh@1Uvǰlf4Mc)7['9q{@":1v5Dz{TV|ɕc\0 ƀile&d./|_'F2vn"Bulԫ]ққққққққққққққққққққққққ}_ ҒMRRRRRRRRRRRRRRRRRRRRRRRRۛ+)s>4S~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#~h#508ŷrB۽*TOt%% =VfFn(tlm֖>VKWfY!mހ*{'3t#7V^:6ԶKKA+jꥫn,@ = ImO}U /dj[nuRٷH[wJI'*Յ5-RGںjۋ$-Op$BR[@`fnf‹F@۩}ih#em]TvmŒR{)-I 7B3uaE샣` KmԾ2Z6 nS =Д$YAѰ@_ZZ[WU-]qdzTJKh{6;˚a'vz 7B3uaE샣`]];?@۩}ih#=#dHg~"$2Z6 n]M-Ʃ r1~*:;^JkYӾ!VRv e؁b]v OҤA/{8IpJI'Ac}g1&vFˀ| #]u E_٤1q- 7B3uaE샣`z;']3cUVۊ :# Xε?g00vWڻLW/S,5$m֖>&b(wL! iCQ th}@'BTںjۋ$- aֿW@S~w`C:MjXX_*|bС'Yg pOp$BR[@`1H ZobF@Lz|զNtk|y!AIm 7B3uaE샣`f6[Юx cT~68Ka?qhtknC]hpk@5-RG7Rs;ƩMj cq'%7]0[WU-]qdz aB鱲9l 1%ߴc32VKL'`6d = ImO}"N//^c=x\N>Q2S柲XڻHnZfnxqY?_a ;jagnfnf‹F'>eFncQB/psT~684"ϏDB}-s_rcώlnso3_ZZJg Y1f\9WoJ; RF`A܍s8[WU-]qdz?Vl]WU>3:Xp7]D0)h;fdYYB!-s#_[6*{' ';Q6s=hBT@u`1Q m%0-jk#.u49LtkAb@۩}ih#˩`uKV[t k)ѫήsF#em]TvmŒV }еRl~f@0y&{3s9{l^<R{)-IԠD&F.l- ߮t!}+hE.\yiJVfFn(tlfHps_VZn*?`XI]G`ZN8^|~48ϝj[n cfفQҲ^z1{B9.K@ O͞sD ͸B۽ աo@aS~w` }EZW]Xm%=ܛ%*{' PJ tBobF@LX_6 BO\Uj {Nz?cjכl93t#7V^:6vޤ[~u89-G, ' r=xVokH*Z`ԶKKAVʟx:z4}+C)/6 Ϡ2Z6 n+VZߩYMA߶?m3 08E:&wOp$BR[@`}yAM]([h)1T0 TLp7WX@AvV&BTVfFn(tlfHps_VZn*?`X@s-b=L͔Ν+OԶKKAM)yWvGg@I:s:NH[WU-]qdzBt-oԀ,ƣb }TJKh{B/5(1 Q m%0AaA%[s9jLhO^6 +j{eU /dYoR-:Ֆۊɼx>%H4sS W7q@۩}ih# 筧vӝAur!Pcͮq <ʕ&K$0zsGuRٷH[wZ-BH >olMD֭B<*%A*{' PJ tBobF@L?c箨c{j(b~VfFn(tlfHps_VZn*?`X@Kօ_^u9iO/D8 z5-RG' Y-(Ik6-2Z6 n F!5aYɨmO721xZ³Ȥ&Rې R{)-I+ ]tct-؁ߒ4KCo^Dڧ?UF)egVfFn(tl:XW@Uu8O+y^ W'&m֖>/t#@9:#@G^~z x:gEPuRٷH[wJI'*Յ5-RGںjۋ$-Op$BR[@`fnf‹F@۩}ih#em]TvmŒR{)-I 7B3uaE샣` KmԾ2Z6 nS =Д$YAѰ@_ZZ[WU-]qdzTJKh{ЌXQ{ Ru/-| ͸B۽*TOt%% =VfFn(tlm֖>VKWfY!mހ*{'3t#7V^:6ԶKKA+b,RWE fWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEWEٹ ."PEFfPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE׷h~HF3rB7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7B7<`d4456P0@ 3$%1!"#2&ppU㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::f::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮:-6j+z㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::㣮::#fqqqqqqqqqqqqqqqqqqqqqqqqqqp{:7bDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbDv$AؑbD;%)Ι"LDzr.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.:r.e В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В !В k,D kL0vkL0vkL0vkL0vkL0vkL0vkL0vkL0vkL0vkL0vkL0vkL0v.]DΡhu9#:u˻`u D/!ηt^IӮ]/SZ'$iyHuNr%yz=B9'KrGtw+<7Z^C=n輓\^^gPNIwE:J0{:rO ֗g[/$.oW-xn${:y'Nw{LΡhu9#:u˻`u D/!ηt^IӮ]/SZ'$iyHuNr%yz=B9'KrGtw+<7Z^C=n輓\^^gPNIwE:J0{:rO ֗g[/$.oW-YN~e,xUu=R޸LY*&[d7`TL wp&J)dzԆmdȔ;,PLkDji<}gNw{QR&Vm+H_M\o/SZ'P QEX]/!馥V0wil&IyO3a*dwEȜbbk2l(H?(8U% e :u˻{P?yz=B=U%2}iyI8ra$^RޡwE _Ĵ%Xqəu˻ї]//SZ'w9݊Σޞu=&3Zo4"nK*n&LuDN h's2vmU0x I"+nB?j7܈|Oۉ\5 PEB*PT("A PEB*P>;D$c m_ւT& P?y?Ṽ̝mػy I)\jm߂9Cm*_vm[tG .Ee;~%%3+ҌJ/P? =GVdoQA@YGK?9Cm*_vmv8LQGᚌbx=E7^SunfNN^S={&Dj9'rʱ ޣq46ߢYdfoO=#- ṧbUV;:Y+ PXm4gQtL3A80~f,?\`Go C:3'kzw0RpFB.\,sn&fKl%([l %([lvQ);] YezO='|e7k# 0DIF ›l*juGx|w{nޑRQ=9L #4i6hmjտn+#龷;DP&7y?V|X%N5{:NId%mչ;[Ҽ.F5 sn&NDT`Jj/龷;Dul6VR˟/Թe`>juG?گ71{V|I]!^CL,h^s2vzg_e8nL$JXs#!Xv8bUnz&ńř>q46߳x_JsGch'CrE셷şdx#=DrSzw]Ҽ&htWVdoIgtʑaKjdS&L3T٢3ϙK6zm~ɐIlh$z=3ċ[D4gA8ڙ=K.^]>s2v7FUv8\FqKۉV`%usOPB8$#R6EQӋZ?nzNm~[-cw]Om{L- f * "QBK=YQP?zCkS['4Éy/lы6_sn& ݝtTKLY'5.L)>RY fKl$ΥeJ5{:W n7VdoJ,XJ7>㰨91o鐽'6hmZ\n+뾷;DbKX"~;NmozW][BK mcxpӉSKW̝^bWXAqL`sJ@N JjYezNm~ڔ s#lx42H='$d.8F%Tܫ-6EP?x'-%w,%;5h?zsp:lSIN麷3'kzSJ5!}"j(nvRof/zs4lr% nm~+qn+P[zO;Ry߉a߶T6-<)AB|jYd6zW]ҼM=O̝N4!D76hmPIčt6:?mVMD3 a(j vޝjuG%)FK=w[ޣMb:4}ᚄAOsn&/[%+/=5krUQM:9Y"R5{:QC pUL:WhYv;Dv.uJ0u͒.\Yչ;[6L*w^dB;vB \vy*P%깷Cm6۲}chP`YK:IkdȒ^V5{:Nܓq46$m @΢s2v$M >;DP?$̝9CmO9+.:3'krNm|sNJ5{:Nܓq46$m @΢s2v$M >;DP?$̝9CmO9+.:3'krNm|sNJ5{:Nܓq46$m @΢s2v$M >;DP?$̝9CmO9+.:3'krNm|sNJ5{:Nܓq46$m @΢s2v$M >;DP?$̝9CmO9+mw*0«s *0«s *0«s *0«s *0«s *0«s *0«s *0«s *0«s *0«s *0«s *0«s *0t&^[+*q *gBq *gBq *gBq *gBq *gBq *gBq *gBq *gBq *gBq *gBq *gBq *gBq &͙>drҪPtT+] WEB*PtT+] WEB*PtT+] WEB*PtT+] WEB*PtT+] WEB*PtT+] WEB*P']IDdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwőG|YdwŐd٣-F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F”o Q)F^Lꭶ!JI)&छn I)&छn I)&छn I)&छn I)&छn I)&छn I)&छn I)&छn I)&छn I)&छn I)&छn I)&छn I)&Y,eu m =(УBm =(УBm =(УBm =(УBm =(УBm =(УBm =(УBm =(УBm =(УBm =(УBm =(УBm =(УBm ='NSE(E8)QN"qSE(E8)QN"qSE(E8)QN"qSE(E8)QN"qSE(E8)QN"qSE(E8)QN"qSE%w$v$k}v=u7N䗵?mrO'xΦܒۜ\gI?$;^;spk>C;G{:rKGnrN q'gh<gStI{[hI3$ {n/km85~{O|=Mӹ%m9'\t3~Iw$v$k}v=u7N䗵?mrO'xΦܒۜ\gI?$;^;spk>C;G{:rKGnrN q'gh<gStI{[hI3$ {n/km85~{O|=Mӽc*nr ]q۞P^ԗIq&xc.T&oX ~b\hgm?mz1 QaM3i&K}v6T6|3 PdĉZj-Sp6T||=MӽEljT\*Vڎ8A3!Ws>"};mCbYxRVH"pk:׆8igghB(,$f &|Lɴd^{:zndPm"͑>I>۞eien.NGwfuEQŁ2ѝ4Te#q7^;sw~2$N391};n)lVR3KVF i+&[eۂ9S< qoGqEqa 1GZb27c'!Nq7)xݖ}vrg(,ح>jN,mnM*e( l+ C$x{:z.( d&~ l:Hť۞7jłm㊆[r2.ݾ},޿sA3"?mx KgI^h,TVdays_bŒmӊ[MRWolnM>C;GVd$N35)4=_GHo|=Mӽ,?iC||=Mӽ#%e zT`ߕ~۞jXݾW𼨿akм7c^nigghŏVV$$4jy[{n(Xu3d\bId)=8YeR yܿkm^F ˗n?̏cw273סyln5O|ϽӋ,,H.p1n6{n!h(&cwE?zkGnzcvw$b.7o?|3Nj&O~X]\AsL1[mͅxΦnF1nޚ۞jXݾsPqgTИ[N5~/?-WvZo?qc%eb%Ν0c`dM$,^{:{e9^rqi$ڨ/km^F l*a VufZfSqlEskм7c^nigghŏU<gStN7r}+VkX5x7ʕ2t,(ޢ۞jXݾDRc%o$mlك1-pk^~[x7S3t3A RR=O+\gBݎky7e{?z^F{n骵W?}rD-Ȅsh+wH0>۞or?\3EKw%Ze/lMD-f3ũtRM)+kxNd]WGUkoN<.WGUt37y{4s*QD%T7L8RUE(QD%TQ*UJUE(QD&<gStI{[hI3$ {n/km85~{O|=Mӹ%m9'\t3~Iw$v$k}v=u7N䗵?mrO'xΦܒۜ\gI?$;^;spk>C;G{:rKGnrN q'gh<gStI{[hI3$ {n/km85~{O|=Mӹ%m9'\t3~Iw$v$k}v=u7N䗵?mrO'xΦܒۜ\gI?$;^;spk>C;Gn/n[&]hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8hV1Z8h4n(r+(r+(r+(r+(r+(r+(r+(r+(r+(r+(r+(r+(wt +@t +@t +@t +@t +@t +@t +@t +@t +@t +@t +@t +@t)$&\RગT*U. pUK\RગT*U. pUK\RગT*U. pUK\RગT*U. pUK\RગT*U. pUK\RગT*U. pUK\RJoG))g2<0gKe#O%7#䔳IŘ쏧rJY $~HvGMH9%,Gqfl${;#rq#8t_=S~9NIK8Y{:[/y)i$@N,=-gd}<ߴSR dy'`ΖG>JoG))g2<0gKe錎Ȋ 1Givx5~쏧Ѱl*l|ќc2H=Kΐ ͕4:CC4:CC4:CC4:CC4:CB-"q#"|VtGLdG*l!rGMMGc%hLj 9bNl2%|ۓV[2XQ<~*) nI%L3{;#˗2te@W|:ڝKٞYiһ%* /c'@%[ȥ@ ,SA. /7+(LWo60EeYe |,=-#[2 #ܟ"":p ]Mi &KT!쏧̔qOl_HK4U 7ٞYiC'4e7kıy1bOl2|o7+mN/LI #yZϑŘVDD&'!P9KجgɎߍBUC{;#nM?+jvDl,ߴSʆ4k7 oc'XռY  ً狗RGlȔ! ^Vqfl)a>,Gn72d0,^xO[2lɱ*\m wi7 odSZ-$A: VK^SO#&YM3.T_NK?ٞYiɳG"3n_o :WFPC5R d|̘5egxnWǑL^ИRu7[eb;.tY{:[/0v/iτɐʁa$U^TZ{;#.{Gu[&Ĭ/Eі эl,ߴSк|7Ŀ𫣒[$mCȥ@er XĒ,7s8t_`_ۓ۟6&B  hvyOgd}>hZg"vIӆ goG)]>kXbɖ[dV-P!eN7BE,Gл,7]?|# p~L9-9:ύŘmܟ4gĿىG֓SOP(^kygd}>hlr+&]Lexgd}>hE2&;^N ?#~s p-bI2H\Iitoc'*fq3%xƥ@er or> #vpx$0gKe<"l'$|g>B|oW [kY"uq#]gg:'/ |Em0O0gKe> ,KI'ҔR|ɦ|M}g{;#.{Gu|RK, ?a3&p8~g8(K) -&w7#.5d $PSYu6LY3 ,l8R d} y_1EH&a0 5,T(ғ񼳖FkAnOmd'-B#uExȻUXeߣa]J#ygd}>hhnofyfr汽*fћL]?cq&je!\rex+%yB8nWwK_f' P.v8.nؐةS12 P.)Dqflx'6tE:\1} _6H= c{?gqxԉpOp)B8nW&tӛk_8`qflx'ʵª)8vG\6goG)]>k)~l%-Prg2>fqܯ5HQ7CI8c)yY{:[/6^hP1% 9K+|%̙&2V± / hămI!&PQM6|1쏧aҚ?o69A Ik?Ch~me.~9OA ڝQ?j7F{>).@BYa[lO-#zjȰ#IV,8R d}{~o1% ~CA OՄJ^%lBs V,;V,8t_=S~9NIK8Y{:[/y)i$@N,=-gd}<ߴSR dy'`ΖG>JoG))g2<0gKe#O%7#䔳IŘ쏧rJY $~HvGMH9%,Gqfl${;#rq#8t_=S~9NIK8Y{:[/y)i$@N,=-gd}<ߴSR dy'`ΖG>JoG))g2<0gKe#O%7#䔳IŘ쏧rJY $~HvGMH9%,GqfȬ Ơ6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*blgMpCLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLdLd)JSv;ݧVfSyNISRE%-A#M5>TtSTԑtIKPHy%O='5$]/R4IsSOgI9%MIK䔵7\SyNISRE%-A#M5>TtSTԑtIKPHy%O='5$]/R4IsSOgI9%MIK䔵7\SyNISRE%-A#M5>TtSTԑtIKPHy%O='5$]/R4IsSOgI9%MIK䔵7\SyNISRE%-A#M5>TtSTԑtIKPHy%O='5$]/R4IsSOgI9%MIK&PD"&ʙj o(5DijrW5?@SFu^Xe%e~|wp>;|wkO>OSREUdݣ4:A2d(\'\aU@Ϳ-K(DlȬj ogWdi*To0H~p݄ZIYeف'lS+!".j~RL߹C&)\raMc$ i&,۵P`+n᪹cVZ<%%+.NQҾ~#GjH_d%@[+>YiJJfx.d`)j ob&c|/++&itY3%KjD\&B{m-t5DW7Tkw/K2\"syO*dg ߷҂+ 5$]/pr2U (ke~.C1-BeSH;oȥ$iR$ n4"C񼬯łd%E\mHӖɖ*L @\(Te *oOl-gI< ϙmi_VE!`,R BHX),,AS#"VE|j.SXaS5 'Lt )$H! 貌.oy7Ȅ<9!!e~ DbSQ. Rp?m2sr|Dn:J/A"$"Ԕw3^CЬlאcl#08K%bzhU{'&SXOdz5%ˎljI'LŵyGzB y"׿آ+pmt2qke~, "U`Rɿ" iUT7]_WzV%MO$aJZvdV-5Oem?,_,"h)+1&.yYlVŽ E̡*5$L IR"d†|7?ŧ%6Umi^EiQIt$<._Ip.9"BH:WrONk-*fT'Kј ;fG,R)j oՖ(Kש,me~i/'şlk 9mcxl\L ^7:ɯp%8-Li|.Bo,@ΓxȬ[I:zjY)'}S֕Z;А ZTԑt"WM2FMNK.u}ص1/38\gMVrx"GKPH|g7>Q4mn!ve~mm7e8>smʓ\8a+Z]G8ї7gZ? 3qw{:O)0^I2d//.kJN'qmU5$]/MY%gIbRtʛ6Dq̎lX"j/R4%e* ave~i27F/𻴨S\;jF؀~\8tGc~22|򞃗VŵxNXD'a|.:sX#`L&'7ej ovo++aROIeAxm4v9r7o6g*$p,u:gߔ_>%oSyOA˫bҼN?//̩"~5[+>Y>-NqN$Δb_KPH} yY_ NM=)L1f6&U%0ɏsSQT -Vf7n;7\t)<ձmi^F (^lL-"<ʚ.:sXuXS͛2tK%7s%Gz ;#ȥ$iqŘ$͖%cysj~RQy~7ڑ2d1c~0(c}Γz][֕u5a:-l I4baC Zߗ*pc)OO HE-A#M.3e~MǑhSV2l08@җ67?W|k10[,D7x2Ln67+_Xߔ~SyOA˫bҼfA&Y13 d>uMIK9Z_ŰŶg(a?6Nj ovo++lx05aN.܏v+MM:u$#^<3N3;ug7[ +?{:O)9ul[ZWw%(z .:sXL_8ef-XԵ7л\7I44Q0q(vo"NN:u$%T)B8lY-juͯ&ژX}='-+mCbOżKO5$]/tke~&S0%V>5-A#M.3e|r11cxjgȹ*c Icx`شq6X4[\˲T(DɉOgI=.kJqJ'U5$]/tke~r̶)Y n-Pa%l6nqwKPH} yY_Jd)&BK)8yڔ+QΚfwsS"mLQODH~jQY?K5Gf,u t.eaU\m:rιNNaVcL7Ŀa?1<ձmi_5qZĿ"~5[+&2MNȢ[mQSmSn8MM56㏄ێ>Sn8MM56㏄ێ>Sn8MM56㏄ێ>Sn8MM56㏄ێ>Sn8MM56㏄ێ>Sn8MM56㏄ێ>S?:LwE_"޾nSvQ6A;Wa_Ne)oRWA}Hw[ݷ{}N Uzӭxn=Ծ'tU?_R*=}]e7mj_t*/vv^2q/ ~OԊ_Wzu/M}Gڗ' 8 D(21C4bR#7Mryn"ޱ?NEp c^BnQGU:,e)l8*i, ,A{}N KZ% WQrh̢}A7`d Ta@Q=R(W8(0 <,r*=}]oi.gDBn^Qʬu TqiK pEj v^2*b>Rs?`mKwE_]~;PgzJ.yX~Z8ugQquM,ݗ [t{3%9;,R*=}]vH=rkmޱb;:0mgAAJ;,d"^XjBnN xn$n(NcDgRs?`mKwE_t5pmP  4ǗM:/`j~5*&%)bWaYM=O )(R)xnʔ4R gEA>P*6I:Lcq/ ~OXE'DRz.wԶa &Pqi7]v)v?)E`cQ9ˑ3j&bԻ'S<,9ZIH 't'[ݶ9Q7[d2lYȮ^jRs?`mKwE_eE`4Q_"%/{r0L9v0.@4i+ϴ>*D;ghGAL3˨:ݗS ց/xTT38y/*)94VP;*t<#J D^JFQBXmKwE_E B35(hfD>0g KD/?Htr7Y$Uzj((_.9fT/˭,Ne)l1*M3;lQ6A;D~w mC”;9yY<;W}|-"h釃*& f _WzylE˥o~NA|CYO4J"]aT8.\߿,Ne)l+"U $=m9ŷ4kӟZ-uI!4Y䩨0Gڗ' _nwAo,R*=}]sU'EBNa#04J`Y*I;;XImXnSvcQJRs?`mKwE_I 9v,ہbmb`ر[p0lX6,Vɧ#Dx8,R*=}]RpGU2!Co:Y) -OYnSvUH-AÁFIj_t*/vv^2q/ ~OԊ_Wzu/M}Gڗ' E]}:ݗmKwE_"޾nSvQ6A;Wa_Ne)oRWA}Hw[ݷ{}N Uzӭxn=dKMOލ?;z4Oލ?;z4Oލ?;z4Oލ?;z4Oލ?;z4Oލ?;z45D0F[Gkn }#V>۫xŸHmռaOv0;[uoS)[Gkn }#V>۫xŸHmռaOv0;[uoS)[Gkn }#V>۫xŸHmռaOv0;[uoS)[GkD#dP\Dxy'jtO嫑}?GIZ'jtO嫑}?GIZ'jtO嫑}?GIZ'jtO嫑}?GIZ'jtO嫑}?GIZ'jtO嫑}?GIZ'h#?y5|-^WoM[Z y]5mj|*jvTյkWSV֯+¦^WoM[Z y]5mj|*jvTյkWSV֯+¦^WoM[Z y]5mj|*jvTյkWSV֯+¦^WoM[Z+^^W,mj`ϫkW}[ZX3rV֯+ y\gյ>^W,mj`ϫkW}[ZX3rV֯+ y\gյ>^W,mj`ϫkW}[ZPx{okD5Bv7}OZP kW>jCp']^(n Avx.֯7}OZP kW>jCp']^(n Avx.֯7}OZP kW>jCp']^(n Avx.֯7}ҖV:9𘘧` Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ ͠Z\ &s"0r۹iva8]N-kn}۹iva8]N-kn}۹iva8]N-kn}۹iva8]N-kn}۹iva8]N-kn}۹iva8]N-kn}۹iv!7b,f(5=˾Q𾨣q]r7.Rosd.Gu^TTnK32ݾmG'Qܻ 7){}QRe/po˯ʟv1-FOr|/(W\ EK锽?.*}Frx=˾Q𾨣q]r7.Rosd.Gu^TTnK32ݾmG'Qܻ 7){}QRe/po˯ʟv1-FOr|,głt1E?&)FE딽6Bk{ 5Br;,&b(RFEK锽6cCw@duS@6&dDØYI>@@ 3 Frx=˾QI>I ~BCRUjq (to8\^IH6F/plJE=ET=Tٶs@d5 YǔrҢx䴙^*QE@rz)H"Z> hw;<EK锽IIVl܁zMIgHZ>I8yGԨv{ApM0E5 3H'?!y/8J7LF]~Tx)RyG wpOɢGiK먦n& =z;tt LM8/KJ(8ELmmG'Qܻ qD=sƔC=A@8[;B(검~N~Y 0N $weyu Tȩl0بq]r;C5&z~SSl cSE0I"PD+)O4Xs JTIav@Po`ŤjP' 98iQZNˏ=t-Re/pwDfy{Ql:drԔ9)ZB\K8i ]$7sxӢS){l_>KH&YOX:Y4,S7`Rrz*XOgag]4ϔLt ̉ښ/^:4ҋCwsAQw6#Ũ]򏅆]4@44e͝xQ(&0ƕ xp<)ن׍**>*(W\A8 rOhGnФ}e|GDDV{$3]D+)@|qJPD|gɭq0m^NY1J38=r0gr= ۰5.R}#\R&x* m4.j6;]oTVk}=ЧB!e0{a?>~M L0+81\UN?:Qf⩔˯ʟw}&uM|,D&iA)T F"۟-lDiK먦n CpvT~Nf59Ƿ}#nb9)+9B)GBgE3 t@zV9&8"iYؿ7QF/pl4>@ܴ)}uGEPH'0ӜxTcRYN<6 sd.G70E^&MX jTYNܛsҖ@|,Z`*(W\ 'Ңxĝ@`hiKG!x<$ 8|)4 ͜wP Tp#}EK锽ЪwOt7%I7e*,U2uSd O?hҗQL݁ME ZJdA>a2>(a1-FOr|,P P:ݦ<, @C/kIzk?>NTA$ :(W\ 'Ңxl"'aҕt.|I812zD RR#1Ug2yTnK ?WxiQZC{B[CvRS){l_>@ܴ)}uΩ4qT *aqcUM#M#M#p[, #4sd.GA~;w\9J%SfWx]cu^h}qx*!]N@K1< Fӵ^Y0UNQ ooi (rKb1Wmtp26\4~Ң[i*N[А|TB :oBARu ބ I+zV$'PH>*N[А|TB :oBARu ބ I+zV$'PH>*N[А|TB :oBARu ބw| $E1Bn.;O]v*TpU>v6!ت}RmCTۀbKScn.;O]v*TpU>v6!ت}RmCTۀbKScn.;O]v*TpU>v6!ت}RmCTۀbKScn.udzDT0r i0 Z`'65kIO@lj֓խ&=ZLzcVƭi0 Z`'65kIO@lj֓խ&=ZLzcVƭi0 Z`'65kIO@lj֓խ&=ZLzcVƭi0 ; <֚X 羗}Bm澤6{QX 羗}Bm澤6{QX 羗}Bm澤6{QX 羗}Bm澤6{QX 羗}Bm澤6{QX 羗}Bm澤6{QX 羗}Bm澤6{QX 簀5@SDvS t2+ k \,SX&BmOi{aj`i0vAzLw򏎨O=Qg^vtsMT&iLI˨ dӺ\'c0S} zh$mr~.Lݖwa;$& O~wG &kL8Yeqi8{X7c|XN'UJjRS?n3)Su n̬z_?0S:Ka<:x`ɖb6G`m-S܇=JeeTPt3uތn@E(p!mBቺ ?MQX ߒT,W&;aZSiLNO=M`w'o(q#7I!~&na0 O~iN3;PYz6҈aͿYĦ5S03N) /H`( ?o6}FK$-<;" `6nRS?m''SH 7)3 ~Y<Rc4..gN6QX xy[)w>^p)8sZgcja߯ɛ“߼nXƘ']jNdiD}m氿ѤD3ҚIFKqgʾ<|ݔ&~R9)I5@/+;9,̑gg3ATc5'ӻ X-J>:Ka<OO Rq29FR@mt1%S[Z)ȱ5Ba!(a rf찺{fVoTj38&%(IӀ11}Bm氿Ѥ,RZ KpRDӔ}%e_>nRS?m)OT~pQggR"@M0)JWҒj9~IҏO=œiL | [\4A3€۾_7e݅'x4Mz4=o\i;Kqgʾ<|ݔ&~Q|'0g-NP#3qJ>:Ka<OO Rq2( kԵ3=I &@G-)WNJKT'5eTL<3vX]=R{INzH#7΋8{{2fwRo5ލ't<D=+bx;/ %$Tj` 7[=U%83ӌ J +1#1T:&YCϯf]S*{cJQ{ '')EҙFY*RQ`hw^n~.LݖOv҈aaI%=ޔ#03Gn>lWǏ$N;+|ZgU?P&䙓Ã|}Pd7x/ݷ+.nt!we!}Pd7|/Yusw_ xT3/Y m!}J˨7}zn_T/oY V]GCW7 Cw}zn_R=| Bz@gv^ȧsgdQS8{ CwUp%`eez>|`O_h@щHo<[-%# wCBok tzA| A-[ Hi @LJw3/Y NĪ6?JȂUSLot87ӕR|>h+N/4*ISQ"/gzS@r(- "I&oCJZ?SƘLJҠYusw_ x拓g `txҒEx liH曻%f%nk^#g0}c㪕w=TskͿznoȿ<懏7,?aEĔy-PLPn@4$QdwCƔ|aWI7m)+s]RohASiP 䝌p={,3/Y ||aWI7m)+s]Ro#b(n@,e*xEP;fԈ$TSPC;)HiQQ+S0iTU MUSXNy)O-D(n">ɂR/x 6 CwE T-}]=8T@0tY+  R"L ,a_fd vDaΎE""KE Yusw_ xeUxJJ+G*#iP 9pꇍwe!a]/R7'Ң;Va<:HIJXcW7g<*TKK)A'<ٴq3-ni_v޲(ݥRmK 7Au37 .a.9 9=nZQ5G T6ǔv*IUb*OgYusw_ xeUxJJ/v@ Z(NӼuԶaH0-7b]vݗ_]wJ@w|#ITr E/ TNP!<5 J$wBF-B n*_Wxiюo$}b&V G&L䓲EAR˨7}x99{ai-r+w%)ǠeL ^ VIЪ8:<0y&  Cwޕ970Z!-GT@0T'5E(۰g ##AQG!fP5@J7Um!aP"gp0hBn/Š0DΥtDDJ۷3S;N,@&?>֬,G mhENcTDnƫ _9}Їޯ|}Pd7x/ݷ+.nt!we!}Pd7|/Yusw_ xT3/Y m!}J˨7}zn_T/oY V]GCW7 Cw}zn_R=| Bz@gv^ CwQ7xo\iD]OS;n-[wQl8ۺaV[:6qձuÎulmEcn-[wQl8ۺaV[:6qձuÎulmEcn-[wQl8ۺaV[:6qձuÎulmEcn-[{uT˻c mEձ}V_[w|`ulmEձ}V_[w|`ulmEձ}V_[w|`ulmEձ}V_[w|`ulmEձ}V_[w|`ulmEձ}V_[w|`ulmEձEL&5ŒcW 3\(0:5p8ŒcW 3\(0:5p8ŒcW 3\(0:5p8ŒcW 3\(0:5p8ŒcW 3\(0:5p8ŒcW 3\(0:5p8Œc<e 9oQ  q05@S !1234AQR6Tab"Pr#B$%CsDcE?¢P0d5L^CY1y d5L^CY1y d5L^CY1y d5L^CY1y d5L^CY1y d5L^CY1y d4b6a,'g+Yx|(r'g+Yx|(r'g+Yx|(r'g+Yx|(r'g+Yx|(r'g+Yx|(r'tq#y֥ r5_&XK9|b/[],@kkMmtɭ r5_&XK9|b/[],@kkMmtɭ r5_&XK9|b/[],@kkMmtɭ r4$yEMĴRy%Hf¥G W ; i¥JͅK+C6"Zw)7ӅKM! Wl*E^(.Rn% *C6, = ThQ8\KN-7Tl*Y^zzУpI*Zo$ T(3aR*Gxrq-8TIRexQf¤UB&Zn`Q`d/#z瓙rU=ny%Hf*̑DhdHgS:!tWkICD}`OTUUB&ZnP1G+;&Lj"RiZo$ )7%ݔH ~M"4$ QIQQ&?PrWkM΄X5kZ_^AsW ; iA"`iGeU)DqgHPLR+B˱Z,&.Ry%HfI(ߣ?BIHL3yzN:ŀ@3LO`kJpM@|)exQfH81 7;Pb+sv"Zw)7r)pg~G(EZo$ iCg֕ dK+C5(T ZNu*1hhUr0d_´+sv"Zw)7uE(`!vYL1qgǍ5S)r*C5UL l g)Fu?2@ ]rϙnRsę҉6֎>|ԳneFbC"D#<)ǭ hQ8\KMХ0@#mG_ s*C5@mzAt:` c(3\Tg`j]9OADIv~#垆W {Kҷ7h.r*Gxrq-72DfBAџm^1-Ii3\_B}$N(_C賬}LPЈIZYBʂ6 , = 7CQOd&Wt"ѱzc~|Xjsq(ei5!.*Q͜ E'HօMĴ܀'BtfĎZo$ iAT7RtܥG$5rfhQ8\KM M>gN4$4c^mc^mc^mz9)Ry%HfK:A3aRJ#G{UpvUQ+%/CG8Twz W;ڮ&ªy])/`p}z=£ϻLTp7uUHakqED7-'AoQ> / zH50T{I{ut/&5,&,?s3ē#j.r;t{#qoV<͐[Տ8dc,X6AoV<͐[Տ8dUdsC}ނf;^M!f-". BI+܍W ړw\(ONVΨҒ%{P ldZ !a TF6sWu(9}z=5uM G}wPrK I #ws JI+98B$r4w\7jMrz_y? Ҧ.pL"!>pq5x25x24sJ$8fX@yCs=tI/dv'>%AOI(pEj]GXi}z=ae$!O (HQ9C>=<#r>A3\p\ 3*tEqڞnOj+KR"y& p7uʢM Tg-hMf|ymijz&nc E(`rX@xZ;'iezR^ܤ wO˯7!E #ITG҃鄤 ! qg 6YĢ:4/CG7Z}!O6=~~Hz;]|.qg&k+C;<Tp|sI1Ib WL?ҖRxdnRJ#G{Upv*]'ÍP fiL"ZSI"nv[xwX*0ܪy])/`nN)8ȩ5p~;< _sCuX=W)}z=781>cbK#s>A3\'po*} G6+"eiCztSDop7umpgՖg)%4 IhbN)M>ql֪Ә%9T{I{rLӔJĞ̩qjQí6;֫ (-"rסۣctAI=ucМrX9.g&kCʇ)ezn_L4Ju> ' I0U܊SP'iIOG\.RJ#G{Upvj;b!XE!@ r=tIxpAP(y֋8GsV u8+UPDIwyܥvFS7Hi]y|.g&kM"$33º~ e2:Z@7D6?POZ C$r4w\7jMrBsC ?6CM78s[ Jcsͤ_TaD/kuhTR ʪRNqDc =uQ+% ʤ:p8\(>Iv6JqB)MpBJ wg8V`~+Y0딾ӟHvF&hLn<:ULs Ͳ7H5O=I(YBvHT;DNb@_L3&-#qVCLl}I^hjnԛ$V!nA0jSD-TJE|ឍQ+% Ur 3%M0ЌpÌ5] USs7)}z= \+UF19N q}wRJTz w1kHӆ;)]Ks~l4AG7)%{;Rn>,mQ :Ub4LeԀwLV-A3aRJ#G{UpvUQ+%/CG8Twz W;ڮ&ªy])/`p}z=£ϻLTp7vT{I{K}ނf¤FIWJK*_^ndp6$r4w\7jM݅UR^R;t{#G}w%{;Rn*Ғ סۣ*;ͅI+܍Uܠ]<.`p@}%oKb+z\;I[؀JޗVv >ñ.p@}%oKb+z\;I[؀JޗVv >ñ.p@}%oKb+z\;I[؀JޗVv >ñ.p@}%awIR ީu 7]qE \Qrz\ީu 7]qE \Qrz\ީu 7]qE \Qrz\ީu 7]qE \Qrz\ީu 7]qE \Qrz\ީu 7]qE \Qrz\ީu 3 CLma'XA9$NI2y5LMa'XA9$NI2y5LMa'XA9$NI2y5LMa'XA9$NI2y5LMa'XA9$NI2y5LMa'XA9$NI2y5LMa'9IIX]mn=fLMk3|Q#9׎JН4?Qb:6JE݆tM6f򄏥uMiK%"cY4# qgyDc՞݊bC2L%иTݍF'*_eU1͢0ׁobyz"EZ#:6kw\$?K>?*:H3<<S 5~'>zH1CtTRSl$Yŝ#u 9B9b1M8NMiK%h3g:ZGBw'mp7T@!]igr[صi u.QMmz 1}Ϫwcf0 yI)li Ϟbz2>4B؜k85RҗjKPY=F(qWhҔ54ܝ́ɽjU]3cH.j-Ze\4:D}4{{ h gOŝȑ4`#<٫HN|LTvx)a8ڹMiK%MHYE (I€Ρb iJ:;m I\cՠ[غ 1}q٫HN|"U6 zrMiK%s|%Br eSH'8=^<~'mp7w| {SZB&/Y25i ϟPD*(34`;Ii/4/!@wSo8RItENZ!%kиTݍE;MZoa!p,a͚]uȑE`5,jJs<<qSmJ].N+>× ' x73 :%F m I\cՠ[غ 1}q٫HN|XNq ;% ~N5"n ӒnNIZ9&hTU7f6W.ԗ)5>(!=کMiK% wq+Y!,iFAdWmp kݍEџe@Hlj^]4oP;%&L_C_˫ʢ3Ŭ-Ljrn)gb}k qan) )v¤j01}υJmJ]0mp7*L_CRҗjL)h\*Mƣ ,Tp4ړ v {¤?K>) )v¤j01}υJmJ]0mp7*L_CRy*PLTC֤{H'3ZOf# jGA?9ԏ|~s5kR= ֤{H'3ZOf# jGA?9ԏ|~s5kR= ֤{H'3ZOf# jGA?9ԏ|~s5kR= ֤{2I wGiE iؿ˗ɭ;r5b._&_֝|ӱ/Zv/kN\Miؿ˗ɭ;r5b._&_֝|ӱ/Zv/kN\Miؿ˗ɭ;r5b._&_֝|ӱ/Zv/kN\Miؿ˗ɠrqGgaŞ}yfsP5#q> k0>k\G1Z9|`}CָcsP5#q> k0>k\G1Z9|`}CָcsP5#q> k0>k\G1Z9|`}CָcsP4: wi,C}}}}}}}}}}}}}}}}}}}}}}}}}wUy:O9gQ@wU'OTUN/KT;XT Rz =zݠªq~]¤累4¥^jnUIS'=yN-oTu^ªO~7h0_l09+*vk}~R{AATaIi^iSK[=(W;ߧ * *N{JJ*ZEF߅T=PnaU8o}.aRsWTaR/Op5J7Uew 0"ƔB2̚.5'OTd[O'ϱ16EqL{vclN6خclim8mqN:9+*v"` K3j;HY wA]4 k}}TK!SŢe 8lVjшnNxP`hPDA!3(:#^*_\4q~PbS%GD]c~pjO~7h.\:SA/4~Q=Dލ?puy]J2 -.%QϢCH֨S K{v)c--9`#iC)^JZw.hkՅ_hsvx;rsWT\% w'Tg7pţ Z>|㨔' |)+ Aש-z{Q@wmPJ#9MqS*a9qb;Yt 3-)b~ ЪCF`g'Zkk}M(K|^\Jo@w%ccDgWzSJw&Yʥo T FZ,xi; |WD㫖z{Q@w#ZVG->#h*A1@f&/s vL)Y6>84#vbh8ɯ=zݠKGay7 " ly+8~d9]CQt*VvuU*mbǎe23NgW7uWD E!@ TE i1{f/.iS:᭰o^03Jv="I,RJȂ7xO~7h5qZ9D0Fb# HbUgN+]4S2ʌ qic)Ԋv LbSe7>iڭ˹` 9TV{%aTZŜwH@XR 3tC^^+0V%UL"@ #n&% A>!9*@Q1W'=yN֮5SwyB?:9`%D٫T'ϜkE޴UVGOx;BaM1M/8U510 x4R;DS":!z{Q@we=xC3j#[ol^TB;<^s3A ]IƨSGxת q <\?E*)64I܊]l5[wǾEoZ]]N/K_z=S;ΗK5壒D2D>!=c"pSXw'vTq<~:9+*v5Bf ZQW״N֩DpsQшV\t^m:_l.5L*{!Zw.hk8M-rΰtdS ҖB9?:Te2gϲՠ/1@zRsWT\j}Uyz5Vh0_v3N(b4L&)q)]tzɇ$kHB&6PT~!CZdaK[=(W;2!ZL^KhN`聡SHuӛgyq P')I_ (N+v5EMDòo N]RɆfw@L`mUIƨSGx֕r.r -iHf4KZҐi!/j'xCi;D SꞹŜs֛v鞎cm'TaqeW 3tCZ"Cds4 =539>ZCv4OҞ B8qa֚ЈiSvOdD.j累4ڸ ͘+T'Ϝkx8 ´"H~ Ç|#/aDSLj+Q 와'PtqIפuU-oTu^{=i1{fTR,-"d_y;>zߧ Qo=ZWڎU~{TV{%aqeW 3tCZ*U#R€%xi4U'=yNƨW^lZ>|^WIrD9vYge1Q!$Y}{,P[=(W;/Y@3쵭CHEE=Մ'~p׍s%052-q<.gjCq1&h|!4.n'OTMaoz\DmvoPĜԻE"\bW&PnIq___X;u8o}.\bZ6VQq@څx>tB~= R(qsO<*O LgPLjGwW2pҼҧjN/:!CPtvɠx 1ԩԩ#aM9: k}~R{AATaIi^iSK[=(W;ߧ * *N{JJ*ZEF߅T=PnaU8o}.aRsWTaR/Op5J7U*v {v Ҽҧk z{Q@wU'OTUN/KT;XT Rz =zݠªq~]¤累4¥^jnUIS'=yN-oIDp<;nX+i29JLR)cnX+i29JLR)cnX+i29JLR)cnX+i29JLR)cnX+i29JLR)cnX+i29JLR)cnX+B_3'K(e5?tZ¡n|SS]j oչO;sSMwE *Vυ5?tZ¡n|SS]j oչT,UJ;qyۚjk-@\#ґ~spD/Jiҕ IHfTuB{VVչc5nXoM[9[VVչcǜuP>{,gDam4m >,Y]EK`Z#uuo;sSsڪk-@j4#k2tICX+@>~m[?tZGmL ^[W ߫sB7+rDTgKG놭njnc?tZGmLi ]gSVdVqքorV0D\ 2|L% tPkhDPR6\ڷU5.2/=i=Q!Q~UCslC܌&ȋ;;$ʑ6 ߫sB7+Fлjp:!uZ܂:U5.2/=i=Q!QTHs/ST;ٸ5 ߫sB7+F;jЧAIͰ,8NAx"a9f>ib5-(v?TtZGjeA#,}gǣ.>Ȳi.ȴ@83an|=hF%cpZ% 26L$TQ@9o;sSs=MwE t]S,cs  k.Y9v Wˆֹr(x4:C,U&9.P>{ %'0XcD>1p&vw pyۚjk-@aP>|)njuC7𧝹֦ ߫sžvZPT3~ϟ yۚjk-@aP>|)njuC7𧝹֦ ߫sžvWèik-㇩#؎8zb8k-㇩#؎8zb8k-㇩#؎8zb8k-㇩#؎8zb8k-㇩#؎8zb8k-㇩#؎8zb8k-㇩|PuŽlo[vOr|?tCj#P~'H9>F5Or|?tCj#P~'H9>F5Or|?tCj#P~'H9>F5Or|?tCj#P~'H9>F5Or|?tCj#P~'H9>F5Or|?tCj#P~'H9>F5Or|?tB4ILSDVFgKqI8Rq)8CN!'ⓈqI8Rq)8CN!'ⓈqI8Rq)8CN!'ⓈqI8Rq)8CN!'ⓈqI8Rq)8CN!'ⓈqI8Rq)8CN!'ⓈqI8X*[(R;vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI!'o$vHI I%%S`z$*aUeI#>$g!"HBD#9 Fr$$IH3$g!"HBD#9 Fr$$IH3$g!"HBD#9 Fr$$IH3$g!"HBD#9 Fr$$IH3$g!"HBD#9 Fr$$IH3$g!"HBD#9 Fr$$IH3$g!"HBD*aKDmz%Kb#vJяk{<7-Bo>r[8L{EN(VΌ{[l߿TM!`~؈*qCҶtc0fmiFSfyn[78l3H|p"0Pgk43rٿ~-fC/EDZT;YlǵaN!ot7 9z-&="?+gF= ܶoߪq {ٿfl0?qDa8i[:1ogf~S[64^g b#vJяk{<7-Bo>r[8L{EN(VΌ{[l߿TM!`~؈*qCҶtc0fmiFSfyn[78l3H|p"0Pgk43rٿ~-fC/EDZT;YlǵaN!ot7 9z-&="?+gF= ܶoߪq {ٿfl0?qDa8is% 5ݥ%.wJ;hl S$c0m[%LMߪLy I??=g樁qu'cTGí+pB)*fbaֽGB.SV3XVHFcG<ѡۇHEL[*n>nC/G:86V:㕐+!CVBk4˅75DZT;Yk:ə(OY})غGL|hVOяk{<79C@4n/KU_w+ &Н䤩q +|T¢sșNy&5~S[<̙axMu?LT2o㯩YS#0n]ޯ@EB˚fiL+Z(&="?.l$0{9 `iWrUm iz1ogf3Sf ҮĂlAc33ܕD J/$.sW8"SeGҤvO]G) H:F]4^s^y^:V/sXL{EN(\*lE?DIm']B|ތ{[Bbi"%GEZ IVr)OJ"1S)OZJ/ "IyDS,|N!ot4E0{d>8+,]4^CwHJ*%Z/.iP:E$D&i笙XB}etr.%UN:th&="?.hC*b_vcGQU+yǵa (gM&])baR_@۟d[=tq=$<ʦ!BE,N!otO¾~aWXs UfU(,д3rX^_I,sG 9z9GH?ʱlﮢ_+氘"0Pgk4(D81=NJgIk:DEdQJ9FJo|SVP!7ynsFIhea Pto8v'SֵY3;&ucyT?~-kLs)2oFx>rsp2Of2*p Oje|`~؈*qCjiԒвd{F?Ft6(̈́@PMIN}DJ&pQs2XC>oF= n=Kj'̫!\z$zBqEd<aStG 9z9eڶYAq/G{a5N8di{vO"e:%k b#vK=C(*xYuvr!a?2~LUYآXC>oF= jt9d:I؅Eٚ4~S[rsU8ԥ%LmZ.ο~ؤUKZ4]q Ue_w5DZT;Y( a43s\/ʳtx$ %6^2wz|Gߪq {4CS5$$Re}CW"4WZzĪQsW 9z9(EY;/x'^X; 7tOW%ʥ*DED!Ue_w5DZT;Y( a43s %=fV}*WYIU6!ne/ߪq {3xLҕGRSQU dp4Ft>rsED]IV yUgLL)ZΑtCoV؅}YDWa0?qDa8is+,⬡XC>oF= E[E}8.9ңXt._p/6~S[<ٛ_' 8M}k>rs5f}AQ;̣jzŧ?h-\7uK}FSf82*fC/G2 ;vtP.a9@(w]Ô 7.MOE>jS.GN*E*uI2p"0Pgk4qVP!7yns6cm+RϠTKu\N!ot8[;1-%ELq>LAhPջ|TL+Ih 459]G4!󗣝BӬ_+氘"0Pgk4qVP!7yns:fBpw:W\CZΙ4!󗣝BӬ_+氘"0Pgk4qVP!7yns:fBlA:uXRNnrsCF-hKj*":{$cG|FR?`2߂[4iuXL{EN(VΌ{[l߿TM!`~؈*qCҶtc0fmiFSfyn[78l3H|p"0Pgk43rٿ~-fC/EDZT;YlǵaN!ot7 9z-&="?+gF= ܶoߪq {ٿfl0?qDa8i[:1ogf~S[64^g b#vJяk{<7-Bo>r[8L{EN(VΌ{[l߿TM!`~؈*qCҶtc0fmiFSfyn[78l3H|p"0Pgk43rٿ~-fC/EDZT;YlǵaN!ot7 9z-&="?+gF= ܶoߪq {ٿfl0?qDa8i[:1ogf~S[64^g aRNTZ)(AR"'H"/X刾z,E9b/^}Xr_|c"/X刾z,E9b/^}Xr_|c"/X刾z,E9b/^}Xr_|c"/X刾z,E9b/^}Xr_|c"/X刾z,E9b/^}Xr_|c"/X刾z,E9b/^}Xr_|c"/X\DB\3Rfgb$]c^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF8הc^Q=yF J:f`j%!;J2!,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳrK1,w,ıܳ7}'dǑuoB̈r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(r(?(_[M5>FSGReU4j95լrj1XcVɨƭcQZ&MF5kj95լrj1XcVɨƭcQZ&MF5kj95լrj1XcVɨƭcQZ&MF5kj95լrj1XcVɨƭcQZ&MF5kj95լrj1XcVɨƭcQZ&MF5kj95լrj1XcVɨƭcQZ&MF5kj95լQXd4CJz`AE%\IfvlhcVƭcэZ'OF5kj9=լrz1XcVƭcэZ'OF5kj9=լrz1XcVƭcэZ'OF5kj9=լrz1XcVƭcэZ'OF5kj9=լrz1XcVƭcэZ'OF5kj9=լrz1XcVƭcэZ'OF5kj9=լrz1XcVƭb>5 Iu(BEEB%\MfȐc=bBgHQ 1!F3$(ząXc=bBgHQ 1!F3$(ząXc=bBgHQ 1!F3$(ząXc=bBgHQ 1!F3$(ząXc=bBgHQ 1!F3$(ząXc=bBgHQ 1!F3$(ząXc=b" MTRIvyxrq(tč8X4=bFgHӌq#N3$izč8X4=bFgHӌq#N3$izč8X4=bFgHӌq#N3$izč8X4=bFgHӌq#N3$izč8X4=bFgHӌq#N3$izč8X4=bFgHӌq#N3$izč8X 747L]؈*qk4P;gD798lZv"0@+gE}>?iw8 v.y>j[8l"8J_j{8?iw8 v.y>j[8l"8J_j{8?iw8 v.y>j[8l"8J_j{8?iw8 v.y>j[8l"8J_j{8 wx wx wx wx wx wx wZԼ߬\.y>jss(eN!I>QˉHmX{u-$40I.o ӱTisH4TN_j{8<`*I& 6[?AxYbhk<s@̣0.f5z9]|hEOes7v7CG4 4Y?Q:GL|mêpD 4Hl"8KQ'}x=ʉ߷1Z+Og|oCmRI`?4NöUԒbt553Ս:5P׽ɪPIJ-pi˯?iw8 |3n^=M]Dgq5Z9pZIv-NU5I]V6M5SEF *F[up^{C`Da85\׽u=(˪:S"IE9'#rw~9'"zlS|gh=bqDeTE:qV~LYBnK{XFNKHITekD79%4SJʑaֿ$_Zp}kE ֿ$_Zp}kE܂/4V*.]ct:/R-qw!USEYR*$ R[[*R[Ge!Zv"0@.fޢEXۊjt+Og|6Mj!oz(gjꤺB `QP1o;~qÜ4ߜrJ*mNssH?TQVG'xՑ"mb괣.i ӱTis:& aE=Ce)#thmܽ-g5caҰߛn5aO:wӏ~>v=@(j.mL*wn8uJU3ngPlp*_4i؈*qk4iz~C"vo&ASKa]OÛQ_j{8@U a~yFə3LLe)'Hhu2n;hb>r\: mK%XReT". v֣w!U.)5My7JpЯp(h4UiE}|5.k ӱTis2O"=*aP>gGtu(ZQj*TֳRz]mU+A#-2"ں] J'.9Ϝhfokʫ>mq5Z9N6Uzi]c]ү~lDy6 NFSYO6(LY)QfuM,-*5~iT>=+Wj|J(&˝]d}54QeWOxs@^Ii;FS~T{.=͢7CG4S3= 4oHu 3:b*D7QpB. aZv"0@.hA8X4v2ӧSUV d}20 -wG9 ٴGRAQ ߩy%Wڞ1C8ݬDU yf]'Bol"xUZ) H:F]̔WH5%eqEO?iw8 |mti*YYSi8{ϛo0VgTCJϬVy&x_O=Z[\jM'GB".Tмd}G!Zv"0@.w GYM#ptMa'G4qxyGd< |⭍Jw2s,e W "蟴b>ptمipa8^r0o0Vf&T g z 4d݃lAbқ GJG4i؈*qk4oK(=bq:"2XN$^HnjOzys@މIu{F~uVO='%""..oq5Z9dOd*Q ~m]'X*IãVݻճLxP;<b#pT)'y*w*Ec'FE5>iE}>$kUNa4'W𭏉A&#kv 7NMgD79rhoV?F.n|.y>js8y~UήW>J}J>g ӱTis8:b 4RYSAHO&?WVHN.IQN+jk%%U4U{kMɞ.iE}>󏙦  ~4<.A0'pEe__*MW?q);>s:'.9ϛ?j]j=\:,p̕7Q%7L1]?8a5'*]AR] c>UtiMr#>mVɑZv"0@.gDnZv)PXOmqojs6KjjN?gU$*Q]dY*6))?8hTX;4ԢX7{nĖ[Zv"0@.gDnZw"B D>iT'w_j{8S{{ a ~Єj5-2T_Q?iw8 | b:nt\$Rj\FIIS3!g_4i؈*qk4ere [So>BC޵z) 2?AmE}>)VЮkDY6} }5dy8IpJYMK>kD79Vh3G\J t%Eo0ViR\-CkA6k>`_?-k ӱTis3Bʩ'F _+0ugC4mpެ"U[Ó|ֿp۴WLk\ JV_=Jix bw"7qxy͎1ҽ2~q6|Gei?,A8cm ]q ~r) Y$ Lm"+\ډK osh#yTy*t]9K)/2"J%OIS$T*|D>"J%OIS$T*|D>"J%OIS$T*|D>"J%OIS$T*|D>"J%OIS$T*|D>"J%O2FɴΕ݈"$MlgӜù osqw!Da8"kgs8@ޜlxsab#. [8;;gD0gÜ]|hp=;EN!pH79:!s8@l7C+E؈*q D?p9 ;;gq1Z->NFS\&qw3 vΈa798lzv"0B5ù oNstCp9o0VgӱT~zsw3 v.y>b[8|"$MlgӜù osqw!Da8"kgs8@ޜlxsab#. [8;;gD0gÜ]|hp=;EN!pH79:!s8@l7C+E؈*q D?p9 ;;gq1Z->NFS\&qw3 vΈa798lzv"0B5ù oNstCp9o0VgӱT(>QS)a{hYhiGUH\?p9ϙR7qCN2ТU . AzכLXSeŢ@l Ohxs2S#8eQCͬpJFqQCFQ mswaʒDwGcN1QƩgHGG3q/)%}M3VKOZN0؈*q DP.?ϾQ l];,Qty%<ytZ*F\?p9Ϙ%)f{AUR;ޫX]JD AQX7OHϩ;p+K/1jC; =ވa79@PEU9 7QJf{xr7Q%)f{AhoyOtd^7C+G1T% :WgT3يM}\[=I  ʵ T[Y M‹X(U{>NFS\&拡T-tXu-;2jOڐup5ù oNsAc>Bfp=k+bS\ZIǺUYy֭=˰tOB")vn=;EN!pH԰t"6.ZT" JX;=Ο}\|Lj. K+ Ȱt':ږk>¦*VFn.U.4uDaw+%}% ;=| G*-u׈<~R!\|Lr. Knq1Z7o- K}Bөl(-⮊MjCR-'O,O7X|"$Mޤ{H4ƳqFm'}g*`i{Ԓ}TŸi(w.2M A2>%$#eIwſ#gӜUAPNoҍfq3=.6~tHzt`&aM;ĒCF 6ЏR~=B\=|[nCp9u8vi?~BoEƸn8U׈C)=jKz*"%f㋲j=/ۻo0Vo ")uk:*A,{$3GX#|!{ Mö!R3jlFfxH;&*ĸ2N%c!zv"0B72v![$ՈVMVOeFO(^+REHFY+6YCv }G4ù oNsrF(umb!kqgHDUTLG7:M",uX])^+3b ,7 TH e%ױe*N,m:DʝOD0gÜCP647OM`ֳR%%LxTB:S5jTv{k#9=Ay*qgaG{]AH]"*&#M",ux_waB$3p3=3T~5W`l/E]Wbh#+uY#k2+wH|"$ṂĢbЪW[ U+5.is8@ޜ~2?dwR=A7xyCJ-] .huZU?nڗ魍d/m #|i+Q ;=Ҥ&%-+A[gID}[luv $IΏU=oYxλiB&(u[g:(zzȼ/ۻo0VҤ>ݔ+@SNJt 5[@b9wAE^v2!+?&mb#. s(lcE%eN 0H;=Tn6?)2^M=cH(J>݈" + ]+$RBq[WVEch֟;ù osN@'-%餴<7K> KTÕ[{稶 Ӛ:zMYLa<7Oxu4 )z>bTx_waTؽbC&] -SY1~*;Np[*aN.ʔtedoN0INn=;EN!pHF2+5 ,tZUdđ4 *>is8@ޜ纚TTXA6w_i譀o+k:yEL|XA67[0VE~.y>bnƖH#:v م/Ԫȫ=gM]^QՋ ?u؈*q D&ʗY\M Ino>!KI!L4(:e0SQAQVu4ù oNs0/"+aK5榶K+#c(xs1\YxsYwxa/Yz0t\+VLQ S[Hpz}'Da8"nfi68}j4`]ӧ\~XϚG79\uX]Þjkh}>i(xs1\YxsYwxa/Yz}|>UZdfD,ݡzv"0B73\;m¤d *~m}eEe<:j[d ?A:|?p9Ϙ㬊譃_[fX%=O}[.rWZ6D0gÜ*N{&:ȼ/ۻo0Vc |ȫUj::U-u1ﶃ>b#. sEBŠm{`ޡ*aeD[+dQR٨i&A3i~Ϭ[ uTS…§O4ù oNs0/"+`mds^xꭇof5c?D0gÜ*N{&:ȼ/ۻo0Vc |ȫ>Qͳ# URX!DSB>NFS\&$;#o,KeJL]:JJNd\qez9~zs1}YZKd\?%dZKyCSrXպQ ;>c~: / w!_**;sl UR[4ɘLuM7ҏVo1؈*q D4*t}kVCC(.wʳ#gӜ. :Ȭ. UmVBGI.?xtDgRuН-6-҈a79 U׈8Lux_waUWYf+QlUJU"UQ=ЏCDa8"nsL(B; t{)ueM}>ks8@ޜap_qEatV1{./ RNn&yd].;'ʿ1b:O߽Y -UVYhxs1\YxsYwxa/Yz,=&aQ1*E[)i-VDF("3؈*q DhERijMRUN#tN"5 A+PG/SR" ڧ%T""3 `켽w3 |dVEsTen}P+V(A~6iWOeHhd)wZrə;E-D^3uw3 |sdu'=d^7C+G1UdUkN)= .DĪQVyhET.*t:N)SJ~:N)S&)*RH|"$M#0gnR`QҸ)kw3 |dVEsTۥRapO-+O^F#}za5-DanCp9Ϙ_bD`~㬋]|h0ʬMkxbVMCDa8"nis:J <\/K79\uX]ڭ<QKQpUp, gMPMG_Jvw3 |sdu'=d^7C+G1UdUk[jT^mb#. sH kC-Di2;wE3j ^2ZJIiP~5Jqs{$t8ù oNs,HKh:2#t>RhGb-hZ]QTGipm@ܡOǿ8:PXТ?Yj1bz fIZE3x?QTߘ͓MxTww3 |%dg ~Თ 8lkJH]"؈q%TYt>)qK_-N. ϣwaŔ) ffuTn5jghYƯ\JЯ(DERgtJT8q5jghYƯ8q5jghYƯ8q5jghYƯ4mLn=;EN!pH79:!s8@l7C+E؈*q D?p9 ;;gq1Z->NFS\&qw3 vΈa798lzv"0B5ù oNstCp9o0VgӱT~zsw3 v.y>b[8|"$MlgӜù osqw!Da8"kgs8@ޜlxsab#. [8;;gD0gÜ]|hp=;EN!pH79:!s8@l7C+E؈*q D?p9 ;;gq1Z->NFS\&qw3 vΈa798lzv"0B5ù oNstCp9o0VgӱT~zsw3 v.y>b[8|"$MlgӜù osqw!Da8"kgs8@ޜlxsaaJ~!-7I(""'xU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}%_B5 WЍBU#P}xtJ>iU 9JF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*.jѨJF*./Sԣ0,ECMXIT$!+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.jѨJ˻F+.j ][˥Lo.xGjN*9ģN(J>:N(J>:N(J>:N(J>:N(J>:N(J>:N(J>:N(J>:N(J>:N(J>:N(J>:N(J>:N(%թ%HMT :j"=c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c0IB(GH#t:GH#t:GH#t:GH#t:GH#t:GH#t:GHqT:qc:qc:qc:qc:qc:qc:qc:qc:qc:qc:qc:qc:q6UtX1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c03N" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-Wv"05zS[78MUft;f" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-Wv"05zS[78MUft;f" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-Wv"05zS[78MUft;f" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-Wv"05zSs.)XqJcV#+**PT6`]i/LI7VcFY UţX.~y2efGLq 1/$'PhT^%y&8bm)$]&G*n2CJ2>JLq+1iTWc^I%y&8Wc^I%y&8Wc^IG0U%\R╈ǜAnaǯjm`+E ֏6{aS+YOujNFSJn`hT.$ch'Ƚmc׈#T"#]ZGSKZmc+ ߠ^ iG`v~SMkY"+&fDi%}7 p{N 髩kNk(;sQ>C[cWEI$!6lU9Z!pgt>b6Iu\i$6x`o1n% Ѻ 0m'V5z۟V YZӱTқw<7K> KTUo+zVqW;  )*D]#P[UzߥAM:U+A2>-NnD]f} !QV\Ob586hlSqEgA[FGgvS*nXF\IHD}n}>suOlqw3u_ahT/y$D)=Esk`o1nSIОx?IWOprVtDa8j݉i[WbuA6MӮzVASmҦUt$8\:樓EI1EIW['vy(Toau(:/YgY@8?q];ojwe] ݿ YP]W +QvWPUgyCsù{o"p|h`W =E]~t ՗L"m""Co]Y [J(]YZӱTқtA1q5(K='}U)M"zWKWOkWu;@١hSk0uQFOki5Vj3G Az '*o7au\:)ӨV;'uEl@;wR{quB6M2RJS`yǹUE883G: ou_vUԢ_[_bu4M4:~l-5i^QyB3d8l~d&%_q0*㉆WL6Jxv%5.0u(^JuA6Mu7똫a5N:diZM:CWLC߆GՏW>=Vϭ'H[IR#YujNFSJmѺ#iQeeݬ\dbGR!y4Ƅ'SM^1 [m'IsM!*AXzAx_T; (Tee >ΊE8M4SRxQ? 8O$MS*dqfBӲ} OJf&JvISOQjwe] Qu*oɭgR30 ʕW뮩%ce\],j-UL># ǬdH.B )D[&"-+04UIX쬁\\tD_OXCR\.]|ѫ;EN)DXyQ&~DbQ+u-'o' >1V=xVELJxg:JX)u~SWsS+${ NM'QI$T؁AWlŠ+MLҎ"5~'_U?<ߺgNUGڝWCw7JST#G][nRT]${ pi& O>⬡똫DcMBq JC;a.}USII¢bM0pPTڃ"UU7똫Y2U~7" qêRFtDa8jƫl-$ ={OjEƸn8Okznlq[ĐR8tNUCm5z8%V]I-N7EdC,5bĬ%}@(TEDOMY˴E+ടYgZ *Ej35,(;nA*z ~&={ g*OɰEN؎?z~=YĖ"k( }dJ":[)2Qu1NUD^zEDl;I~`t=ԨLr,~AF(SPx k=l\[ _q)bo.nbӰN6ǒl'vAUi!6{ZSm24U%8wjNFSJm/mY% kpJU3=VDۃ~Y5U>N/r_9!M+صR/p,%߻m}G`LOgf Ih:&Ÿ^tcͬq׳C!UZDh|ja5)-ZΑ0fJY.{(YYD~Wݕt7sv%:$"- e\\xx%;II=;$lp8J BQuL>#݉q.)IJ-sl15TIYd%m$]m!1DQB/u;$aQ4!NF˜}t|VtDa8j,߮zǯ67Nzx6!SnN-T{I-w~S?U8:3R%?It#۳%肦߫MwbCsQjwe] 4Fβ߮be_[KFU%[ғ ޲t-=[u':Uz6IoDJV?-z-%{b#W71fs=x qĒ}d¸~V>o"z/v~S?U8:P#C#/K˴I&VO tZyFiPhnz#N쫡ƈ79PU1uJ_UJ3I:d{J:D)Kty:>iASq=u_a1DY~=]IDZkqD|xZT_+ΐ¼ERzGګVٝ[wս5{b#W71fs=xx!ZM#2HIhwܟ))յ~LSCԽUS;ڍ'tG8O1Y<&UJU"0It9IOWR}*MouKQӨ6-_qjWTB!'VJU:EEи3Is\2iDij"]./q>!}4nL>#1ר o1Vǧ(X,~%t:1V=BTLm^?" ^śX&PGR̔GAI:RVRHG8O1Y<㬡qs] 4Fβ߮bݙ#p:'Vt>czβskFU=ޣpR&"7FtDa8j,߮zǯ6ꖛ*jY+ר,U}O0G8O1Y<㬡qs] 4Fβ߮bݠ_ L/ Ȕ$}4L>#1ר o1V{]A{ZST)~ZӔCKMV7GaLn=QxR`颟UiRV t~S?U8:8m<ڔ}f9E9 S9E9 S9E9 S9E9 8X[(Ԯɟ< RULtQNBh/hse\[ T! ̺*7h}(հNm>meJ*G\ML GMj?OR[&ΒRTTC)Ept>czβsi-'Hxu6o|vM *dv '6iV*.%5/:GVB@'I5`ӱTқ~Dտ5zĒ}dW:ࣩv8%m]a_h_?1eMZFŗa?$ R[U>G\m+V 1 p{ 笢?+ʺhse\[C='F K] Dlu-$c0wj@{h/ғVNFT^XKL"+UAb8'ªlˠdQ:G@Mk㌺ ׺{b#W71fs=xMYU3M;=x}+*UOOnߪpcJyǺPhnz#N쫡ƈ79PU9IJb33EN5nY)Go'YL>#1ר o1W6 u+N#.[M^r#x+)EγRdwVtDa8j-E4I頓J8~Dd¥ImIIme:q[GH!nRKL#ԎΫ j׼N,Q~:Ś xcIWLB#}ZO!a~c0nqj53߿T<"4uʲd*Tt/!IĚ/!CD6ieDt3 jnԭDD}M$yM"T uFY+Gջ*n1r9,JH]2x~'Gx~ nRo32oj#4R:_Iu!mPQg(7|bRzF=*%*b݈Wm$'Inz)q;&{Kqd$"#%q(㈈G (άId QL|2iBcL~:9{MS P,.!]D?m:`݂" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-Wv"05zS[78MUft;f" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-Wv"05zS[78MUft;f" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-Wv"05zS[78MUft;f" ^Nlt7rٯ0ٵ{b#W5~S5] ܶkL>#m^؈*pMl߿T6WCw-WvQo8J ڕ.ccccccccccccccccccccccccccccccccccccccccccccccccccan}gO$Cx 7o!Cx 7o!Cx 7o!Cx 7o!Cx 7o!Cx 7o!CxiJi$pR8) G#HpR8) G#HpR8) G#HpR8) G#HpR8) G#HpR8) G#HpR8)yғ*twUȾT{2XnXEN!iݼ]&6Vy5_8 oVL:\*aD]tUIӺ8bX&A>'MU֢S]bznK}5mT]P6{f֝߆m>7RYŸe=4 iiʒ[aFOH!S[T鸯UtMI)Itn.EeT#зl;f6{ǯw*pfMo8A֭:_*zWDDDDDDDDBڪG`I5(n>xXEN!ij ntϪG^an}fgdASR#2{ZT2j^+6=*u,v7X" \jX*SpY!'Y}7똫aSΟ*vb(%#oKHr==K4"IS껷/ ۥb[duO %7s-߆mտsn.Qhã W`O''S:$mSiJf!T&;$H7BirO:|{ˆP$0وΧF+ɠF5MXGH{kWax;DAQRx},֌X Ib2TԂ>tctm{n֗cxENLۡP󤞕 J .5Gd|Sm'jW:k%Ec8X"4 •ptiaopާJ"`x!)'>$^gbp1stLta8:}%ҘaM*(L&[5}DdTd *bL0݋5:†aU.ŝO\-UC*Ǝf=PR|i1[,^fi* EX-.ChLTqXRh o!JUAxPSOJdbȆhة+&b $~Y?y!D~lΡm<{0ZU:^t1TJ}*eC,6*AGЫ:GDa8f3nD!j|2&aTj۾{ sEUR~mt6ٖ[e]=+=;×Odi5JV'6x$CbV=}Eq0[:iU\C t$ݺ8bhgt k:əMo-ޕv'D o1Vǻ֤*zcc vR}Ecq%ʝؚ6N4Bxè72ʥYEbҦF PuGUR-'I^DW\QT*xVQ%K3ENLۡELU9-XAp{<u]'wE[T:h^+=;/|SI>M/NɎF򶪻Yշd:)!W]Ebդ']aŹ{zbp1suMU5EF8M i^JlEe=d *DXg+_. @zZqպ; )*fbRS-]WQ "Ya?;N§&DChFNnyeK,=NBnENL۪}5HP&MBE"SItyu?FTpV MJvYX2SJk૮2B}'XQ1EI/[u*p֙X2T{*E3@߮bg͞ud-IX9izT~]O; #t=TʟA|;"\A҈/ ƽ™cZuVP}gԬ5UYՒg?ջDa8f3s.&j_*}Ȏ+zVL[/H"ݢCYXe$8_Ue-@}׻8b1DY~ ؈Bhv'b>R)OjpU[&*؇+5akA-4$d9YfhEHsu MK,">>٦vTf+:=V'*=#7q3V"SiAjʯ啕Ssw*qsO1fMc+Y$%T&|=oHuUKv*pczβskq %QicບwՆ)'bP SM)T$+mZRTB7"fM\ĒQLYuޗNyJJIO^5:(5؏7O=5>+v*pf.\LՈT AS y*Y=2 gHp nXEN!i,izEuJ,몪RΰeYاpnҢ:ʶI0OvX" \1ר o1W~ 1T=Q]w:)'L /ϼ~TEQXsV@3671j>A9헝Q.á5D&]&H#/}:kY3;&fuMm */O2MlzcYVPug;FSo71rfEܑJX"4o4=oAS s}gIIS3qU->e]kt*pczβsyBD!z E@(]yD'|T-?*Ssa k:8>#9e70jP:DE9 v5u:CnhWoQ*)f뮝R&fuL!UiIꭢi9FUyX7h" &nbXOPT9=DcUXEN!i,izEkpӳq5p#IVT67;+UTዜ:d -™ c\]gULR8U'Pq7IhQJ3؊J+ "i>'2*2SJX/ ߆mݪC7xo EI;XAN%pnaq 7pꔥY33#:D84/Żmϳ|ud!UZVT\| ;Ҭ%mG1*qsO1fMc+ZOR)CDZ}FXVD#x8ENcQ9@߮bP)qéIu`JʒT}j=:K$ԦV uwvj:II/:ڟ=c.7yKyS]RdhD3ͬ%VE2GDT*/ԝ|J *K|LUAI:id|"?{FSo71rfE) GAMެJ[wǣa8y/k*V?cQ-'9u':a&Y3"TZ|-uPjS3n@@4o>JHS:OG^uߵ=9@ӎ J;zϢ3`"iqy/zRsɫUL5"HW_Ru}~XTTH" \1ר o1W"IJGHս`~F,;M9 Ԥ`D%dSJ}geG\[W}e<3RW~F [7xRkx_=;Cx2JvLfZ?LfZ?LfZ?LfZ?LfZ?LfZ?LfZ?) Rl֒RSL _O>gk^XDSc} .A_s>jxq7Id/r)? qv\u\%*#G稁Ea8f3s.&j_*}kIM¬Tbl.eTbǯV8+*APƶkj^e'9u':U{#P<{~!ͣH=R׻;ͺPϛRZ-VQo2ZT]A""t#sո4 R?E^m[Oo#7q3V"S׬{w67 ["QXEN!i,izEu#ڸ DB)uA$g SR8ENcQ9@߮b +D;յ. ߆m҆|l 6n:-:u v$ Ԣ\dseD?Sn4tˠ2\#7vdViiGԝcqRu,eӿ8:'X> EB&3*qsO1mpՒSK|q}I8:'Xq(5*% UB8mQ!՗1Il? RTM;I:s[yeobp1s*Ĥ3޸F%D7?hySmJ5mT[H~)i%tLyqdbE7_B.Sr9:` +h~J 5`gf hs3*HJj"gc{S9O`?^{S9O`?^{S9O`?^AGGh҄'J4ҭ(wK==[WBXQšf,0G E~ՉtSiĮqd :)tyBRJ4PV笯6ߎlZMpcTlⰊClp1rٻm03xVS{mNS.[7~FSo5*qsM*pf5*pfqXEN!iq8ENlfENL+=6'- #7a88bwDa8f3[8"48" \n3[h" &kgTgTዖ߆kmTlⰊClp1rٻm03xVS{mNS.[7~FSo5*qsM*pf5*pfqXEN!iq8ENlfENL+=6'- #7a88bwDa8f3[8"48" \n3[h" hFЈzģq(J#O8)>#O8)>#O8)>#O8)>#O8)>#O8)>#O8)>#O8)>#O8)>#O8)>#O8)>#O8)>!Q *FmD؆EC~V+H+ !Q1APa 0@qp?!'!! =U9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=Q #1SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzhFb=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜CASsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsNzU9=T窜SsMz'p $4NI-6ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|p'M$ _&M|5kɯ_&M|5kɯ_&M|5kɯ_&M|5kɯ_&M|5kɯ_&M|5kɯ_&:Hd)E9=碜SsNz)E9=碜SsNz)E9=碜SsNz)E9=碜SsNz)E9=碜SsNz)E9=碜SsNz)E9EqLBSVjMZ)E5hբSVjMZ)E5hբSVjMZ)E5hբSVjMZ)E5hբSVjMZ)E5hբSVjMZ)E5hբSVjH6Nc$36kO ,s&9ijZxTdu6_1ͨ&%¥K#61jIfmA1,֞*Yi;WLs3j fRMl'1ڿcPLK5JGZlca9ڂbY<*T:c vfiR֛Nc$36kO ,s&9ijZxTdu6_1ͨ&%¥K#61jIfmA1,֞*Yi;WLs3j fRMl'1ڿcPLK5JGZlca9ڂbY<*T:c |a(b@JS^Ua$i!ØIfyk;M|BLQrp5"frR*PeACݏ0&%9J`F!+֋'Urm2.+Bt{_ , y*^K){DX!H^ |`RIMu3[n %i;rx/ 'g!LEas~Zi4/;7uty9Q1,ܛ%̨++EYL+AKMGb&l1RȑR7Yn , н DPt[.W6_0 7>4枂@8UϩIfy0ccUj1n_J^{5Uq`.DGhM;(eXۈi4NUijra4@: 2-QZ>QYF~x$F)I7=ôl9L*T;0\~Fo`_Nc?<ɱB@]H+ ^I;A IDIqJLs34%R*G FAI]IYʄijr$ᠿw{?9F*Y˪c nͷr1~ hAO^MucMv5VK5 5M08&9&SxnN^erbY0{Cr`?@aRܱ“r*i6P2 + |O Dx)lsp_ &:]y sRcm`ai#q.̰r1,4@ԅ<$ c&o;H=7VZi*i$S ,C*X%OVӑ'UClBk|ca9bۜԤ3<Ѣ1"1҇@6bm@&W*&%"ovaSA0X:cru!RܪSݦv-P hbԞ7,+ vRcewSt1WHJ6(mܰnZiZ0Od hunCW¥K#Ad++>C|IiAEmC<׼& ;Wu)10Pu*rʟDˉf妐oJC5oC⭬0JWv.W6_Ԥ3]r 9ۭ61d?㮹p֛ϲs\vMg9\r;u3u.cNc:\i'1uˀ.@n} sZlc>]r 9ۭ61d?㮹p֛ϲs*^9|r/_9=CmApK2*ǘčN jT.R#m} /H0y$;U'jTRvNIک;U'jTBBC aHh˩ 6(lAȼ ։X)#29|c>|2~1mIpPeyz|uK6j\q@vTĝ wdSrI8czFǺ#Tt@v9\c>|d祪2PDxt8\q@vP&WT) cBI )DtWP/r}WUڂ\U$з 5DA`4 B)@&>C4`,\qxiEPn#ZSnu.p"V{;5Q(jGFr}WUm:ECHU{ .;\ MB[[i0ADn {D&dvG+5k\ֹsZb~?0it<68rn>3Fq]lC@0*h3 m۞9^Sϲs(꧱:l4ї #۪mp8m E;"Pď%eYrLc @n9m訏2AɌ(7(5:?[Ro$/8yLc>|%7hwzhr@%X2rF `ԍnph a4Wk dvX\UB:@Yy#vm)RH #1Xb DEYcNҍLJ78Ǖ3:p؏-Y# V2}I%䓾NB0A!0j .g1C#FR+v`ʩo`B/́wMԖ;bRQ*IcNc=cAxv, 6I l?n(g!x' 0\;K,\׎7z 4{ptܡ-M \#Xqovw:;w(a%Y(4>z#i%@h΃ׅ%X q'1򮁚؀DO D17@m`/D2r,aҠaA6Cᷫ g2Km'տH#ChX.B;0*xLpCΤn?e]Fv~`ܘh#2]aAޚ6K(#@U{ @:n8"yRcRQ9Lc>|㦯P {6n|,˃v[ipCJA%CܹfJjIb9 c[Hw9srg`΍> & Xg Q*ʢ)H U3+({Կ9Lc>|&s&^> #x^q Z7iH{]#e6Tv7ם? P{] IpCLTه W<)Cm= RyPj'1Q5\Nʇ0\(,{nLݻZ(!E‰aee󺛹J: c̃Af ދM`1Nc4ǃĖ!D`PHWYuVPq zWӸ D?^)% Qfơ.'R'n@2fpP.LE/5|r)RHv} %`Uv-CO2vM A絍 4Ћ8v\QZ4]p^!=eqT{75-R\7q!Iy4G.Qh xMp-Exqh͑y0B!1#L ㏁iXx?X.T 7{?L9srjmz+gcK=Wb$1t9a LZJAp^Q.b0 |hIQéwkUD[dȡ% P75J A)Nu bWP.G코&M i( y*ɨ)[^) Maj1i$z_\1d>Q&13"DZudgytz]ˁ ɥ\ZB0C*tCf |K!@rMp;Z{ c${<9۹@@;#l<X<P8$lU]9\c>|4j!MB_ X72& 0ݤMۻH ; KhQފ 2*M[g`\E13W tz S0@ 0&.VZQ a@]|9۹P ;CA gg5 4e=L Yf,Jf1A`T@v1d?㮹p֛ϲs\vMg9\r;u3u.cNc:\i'1uˀ.@n} sZlc>]r 9ۭ61d?㮹p֛ϲs\vMg9\r;u3u.cNc:\i'1uˀ.@n} sZlc>ƮwPZl2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&LHJ` L @ @ @ @ @ @"_Q@I&P+/jTR^Kک{U/jTR^Kک{U/jTR^Kک{U/jTR^Kک{U/jTR^Kک{U/jTR^Kک{U/jS;I} zFMT5RTJSU*jMT5RTJSU*jMT5RTJSU*jMT5RTJSU*jMT5RTJSU*jMT5RTJSU*jMT hLƎ[ٯI… (PB (PB (PB (PB (PB (PB2A'elTm9RJ*TRJ*TRJ*TRJ*TRJ*TRJ*TRTDdlTm:TRJ*TRJ*TRJ*TRJ*TRJ*TRJ*Tp'RʦRa{/쾷{/쾷{/쾷{/쾷{/쾷{/쾷{/쾷{/쾷{/쾷{/쾷{/쾷{/쾷Ɉt<+9IaZqZ]i/ƤrARZ@.NcRXm9V\V sZi'1,6+PT+V94ԖNA*WH֚bjK +դbM1d5%ӐEj j1usi"JiL_9IaZqZ]i/ƤrARZ@.NcRXm9V\V sZi'1,6+PT+V94ԖNA*WH֚bjK +դbM1d5%ӐEj j1us˔ j]eN vXpy\aEݫ,˃LP w** PՈʲU4@9 \\"p ;X` :X)fAD@ * M  h#h<6Yfw G!1d5%tG@2ctXř`G: _h r"RI)u#S+n|\ Y .}A7A̓xkJzǕ$ҙ|76bI4O9$ I%S5jPz":JRci8 -J PpnJZ0WSi' b)$zt C~y\%zvE9/B&7vS75;z1/N Ʊ-L_9Ia?FDvi7aGp;m~ێb`@ÔEO"I;Li"(xTK`bY Jpq팁ݻ:̾8~'䞔k!MzP*e>N@TH둣 j$Jtr Y'{(\VDvT0n17*T7.,.HV?awzXϓ0xݺIJi$n>N@܊S<2!Ȧ=^I;Yyjny1d5% TE']" W6RA$+0QnJ&WI' Spr.R O R( hI];_1d5%'V ,`c,/zAP מRA$+0 '[Ѻ4ynUB^I8b1w"nO"xNF+ &Z9L.r^B2q/ƤRLQDk Us;}Nк8NU)@UP#|VJeez0m;}NS ~]ӑϋw@W#v @soWI' Spr.R OH4y@*rl ǘsR Sky刻] 4aJŧ KՓ_(E ?.Gy=*}'BN@܊S<;3@VWYT_ i$>zL_9IaHIB O H!5@J:7E ?.ҁCi3"BtK!Hm%^ dž ެg~,+N]ȥ0ȝr>@VUEn;T&5IBlM 5^%1d5%KM0b`**X#G CR?wC gRp_+ ~]UcRЁ^hR˼ETH YG3`I0|9ryKUv@0QL,_}}59Ij\7. ` %i!S*t &U<%䃟3hmx J͢Gr8Қ q)J)CD(5x@ &(h 4RPJ)CE(h Cut4 H'b>ORw˱!G@: TlC :k'p UXRQ)$La#/Kx1jAPKl5n}NB~(Gs pc)-l(fVY#?`d;.B*i) t0( ;j}ډ&C&Qf^I)(C"yŠ[[Z4yPЬ(7qC&G̐Vwc)T>EY)$Ocrf\v۷a VsN+^ (iL7LO @( ʖ+i(' EDZ;oG Rkӟŕsg"L@ 'NjdV4 $w&0@ `<- w?,eşD@ ziiPMGSt/4׷[U瓕sj]RPҙ:ԫ~MɌ<^d! _ϢX^#Aid׹yCESd+0 *VK(w%}URK{'Jj DB`h7 JGt 8wn|Kgx$򒷶Nc,tCT %b7Nh 62$V[xl ="hĘF|)[mxi1 =NZ&h<'*x8I~4b\'Jj+êF_YDl%A'v`#;pmP"dBۈQCq7:09 _okྲ T |Уlp.!:{*3ִ^NJ,E)JXߏ+{d>24-V!$CI$`X](k Wr%>xXԤDV é&$Y J1T7Uq406 ?|tAx(ud0/8!layr%8כT7ܴ*7m_&BaVDjE4MVAFJj pPH\R9DpуvqD[+{d>Iil{xTFxwwC8$%p^ZSWx.Pe|uRNh\ (`}2;u7r9ĕs$=ɌayO!q4\Ab@hYTnƦ7Ősg(i7pxlu+{d>Iil{wHR< HDOz"SIcXI‘͛c@(ЅUαL: Vp#6SK{_L@Zj`.GÙOQB}-]b5ې:jЍ;lmE@o}pʍ&)h$2f2y6gBʃ X"e&@2HtO dY)(8{-$[ 3HU"]TuR.EHU"]QH#F/HXnE4$[)RPZ؛HjvNVajDhv&; Uִx#OVثG(xSIJ)[DUy,{i$%Q}]1i{`v pb\Bu v.y[;$>'^ҝpMova{҈]ܣC=+Cn1P70hB 5MJ.SCyTY%5zPh h! a5Ѱ)Nyg1tq]PxLHŔ$q,G7k M۸MT{Ixtu1<“== bXg|$ $ | I@CQHkt U06z-Ga}<\.OՀ+!QO 7'ˠzoXk, nucLNpfp8 ) GREǜƥq^(XX~|IK: Xs'{ɼJ4I`y+pݣ9E,%xEPJJd=Z@Ѻ$ =s?;͠&W6Arx ʝ4Xdj8I,ŅoF8^ Csa 6ǜƥq]z7yFXCiF /p'9s n0(,kE$q,G7ê^o7H*{/svdЁW@6 7!cG,%x>};0EBĀ즠UL(K @HaWPzZ.l<0@VIwq؅HJk62+"1oP8\-W QmFMAa7"JJ:5mfB"1Bܧܜ]Kފ1gCsy\E 1/P8 HK heR璒## 5!@6^}[ =0cϫ9Kc{ryK^2o$sн0PB"GAC#@VF:f`hp b=ƒ ֦s#^&'j&<=&" 0 *'m-(4*ci5d*̜ƥs1N_55 yX8^q,G7#){sɼth撝 ;TƸaԂx#@2r‡@;pJ[h :24M$ !u;#vEs>ӘԺ|Fckr/<3$q,G7#){sɺRQzE%@tNǰIAXW}xiPQ#0[7U؁x#$@#YE#}^J¼6LKPOfq8#3!PhN1)5ˋ /.bKVNy\vCV76@@ sASk3- `i( $|QHXnFRtȱ숑n"Z< 5龯qrY0#!b9vD0@(莸Y\e]=Fþ/Ɂiϸ8\>b1&$HsMo@mcMu )HM$1y(wC gq h hcƜƥs1+ y9n<>_ۑı܌&O/[w-wqx[w~ zq$1,G3aHtAah.A\.OI6| +!$OdWvLg7:0״|Qa6uw&DCƜƥs0fƞx5x9K^2n"9P>S KcA쀂b8% I;MQ q⁕I Kr 3!joJ9z6YlS 'l'sSUH#; D V  PÏ9KYȩ@]uL)<7ʢ`0\X}@XnEcLCrI$pi~5$4Si*p,8"@4T ^l<Ѓxi+%[Ui K-{$[ <MKAS]i0ʎpQS SWT !j: II"Farx4ËAAAOp0wkx> 0o!D4檐y+A ' lE{t&/r3Ժ NGsZr#65.ӑı֜%m<.M9Kq,G5!b9O kNcR-9KibXfӘԺ NGsZr#65.ӑı֜%m<.M9Kq,G5!b9O kNcR-9KibXfӘԺ NGsZr#65.ӑı֜%m<.M9Kq,G5!b9O kNcR-9KibXfӘԺ NGsZr#65.ӑı֜%m<.M9Kq,G5!b9O kMcDIȩ?e'R~OI)?e'R~OI)?e'R~OI)?e'R~OI)?e'R~OI)?e'R~OI)?e'ЀP-m-RSJ{)Oe)=RSJ{)Oe)=RSJ{)Oe)=RSJ{)Oe)=RSJ{)Oe)=RSJ{)Oe)=RSEIE*OI)?e'R~OI)?e'R~OI)?e'R~OI)?e'R~OI)?e'R~OI)?e'R~OI!fuk E y?M1/ s<<<<<<̽}}}}}}qqqqqq@>P |@)P@>P |@)cP< @1@@; >dPP C2*f 0 |@@*0#X)"PԈT`@@X>H\PPd 0 |@@P)P3<@P=@@ 6X@@@&j >\@Ps<(Ҁ |@)P@>P |@)P@>B      l<<<<<<\ 0 0 0 0 0 0ȽP@ 0(@T}P@ 0(@M4T 0 08}0P 8PT @Jp sM4рx h @ 090(P* @pTN0 0}@KAP@q 0@x #*@@D T l" p0 (P@ZT WL}n@PX @,0D @@9[՞48v*)Ujfʥeǩy#bT3Kq|k û 2iI9N@{2$rPNISH%,Oܡ6N)' A ^.,< !K"sí@ (o6";kF"ɀRdnƇU*#DRԲԼ؁$#F%GeQWG}7YCJƱJ--)uDf4\m=m %E\>#$&0&/͓&I#CQ^R9 aW%^x5ȩxH~"I0i{q)*MD(%InF^g>F^g>F^g>F^g>F^g>F^g>F)J0F?g #`30F?g #`30F?g #`30CE-HQ$H"D$H"D$H"D$YrH.6H#d6H#d6H#d6H#d6H#d6H#d4vS:fiyC!}>yC!}>yC!}>y.g1)|ix^+%Ax3Veu^(Y^+׊x?fPix^+%Ax3Veu^(Y]ѓRxwxw~zA}ۼr#?#?#?#?#?#?"rC5gwWtpDݑх%ύA=euDcuZnwjQA|Dܖ%ܙ_O?Ӵ"JV4v"V$ǘČ=(vR+H(4'NuL# B]7~!RD!)Cf3y.lF=T>UvnZl2ir;gwJnK'ɰ$%4SX!G8?,6kDGv&#P^ .D׮u%;tZ]Hg)RVd-QPY]Z& &J$=YMNr`ʜeuDa&DAYNAurkZc Ef{TB<%ޔ;ol[uwy]Ԇ-Z1TI0Yw#CLzZ%$9999'D WP'JjW̠JMZ(vx: 5 MLh(Ti7Y.w΄& H--Wt`&>DR w#L"k見]-etDHDalu*6:UYWK(ңLĻoBn?E.--+h$ʕ(J:.܀ V%ΐs$jO*kJazRڬqT7=e|j d]׃FFbᲀu krڞWڃ3֎IZa &/9RڬcBɠ$2䋧uAW(_8}q$fdFnwFYIdNK'1%݈{_z",gQPua\]WtΑ0J+XW2I̥DR6{Xba"25Q1@ Ζ ̅ij'ٺ̠{Ԅ؜w3^E fG8E^%ݒsWvo)l;MosI)6TRR 1G4}< ettEHKc~A)[]أ2(:\(C 7.쑔*B'P$J7˓.ZH 1jXHS$};USBཾqgĮ`QJҳے^TuȂNR^61 * 97fY:…V+im۔ـc!1]Ie;V] a bA9:MIR01¢ vrJe<ۑՎp"JW4L3*JOʘ 6 9ttZdIe4Mr`1i1j-Jb(]`f]%'*JG D5kVz.FdTh@M`8GM tDԚ]dK(UJak &1r)(d9ڪ̖J-f)@+4ZT$Id+ $+PYU]1HBZe%d:J[/IDN@`֝MUs*XC4Xfڭ;\<й̵Gr''vRP'`-,tL''1a(m9 26jf,I{ . Jg-2;fl?MʠbdY esU*%ZfKhPUdF!#h[WXk5.^*:5-бE *UpPY_fSw RXt~Y7n0w4#&b):e\<ĹI@ w˶D|j[W˶p͇%*wc3e>:f n /Vuka`\US>EE'2FЗv2 oHT+ba&`r,(-X'+;e `f}lb_{Uv%]; Ògٲ <6luQ9-Qnݝ $B kKВ/9c7 Na> alĦN'Av T 3,qJ9#_,fvl܊U -֠^֩l BPj=7n5V8Vnh03K@%l (( E;e!JMsCLNTM X'& ,)3@t'r_Wx3;6Sfg-XE+ 0Nhɓ"Hd-t]̵Gtw)*dK`E?Vj# am˔V YzIh,ԐwBЦ^+@l9/Wx )d:;97n.`B3BYħK~H` xV>&VxǪ)6DGK]eL20A];J+…U!)x_>S+ITXRXȑ@r>+&I$=݅X7*ca" 9|Oz?z?z?z?z?z?A!@!_j#w̵G fZA3-Qn7PaTy0e<@c2j+Dbo"9_Y|fg49HͧZP-~A@ sPaޮӄRxny6"Ng#J=P` |?W->s mfkp0>&9}-|ϙ2y\׸wbZjmq[#ߡ(pX*Ń 5:̰h>@ Yccc܅E+sBa5_`he}QeNZ&GWWgp"eߟ9KX5de-@Z ca#YQDKjjmI'@6oX!s`ժu4#9*Ow+Ն5`Kƣg3\=aYs8R{’I|?2& M?- 태Vf[:|"`ȅFg>;G:R&L嵟:T ˧oE Muh TԄꠔNq7Gw>bXRz%0+U>yB3 ώykJE/&K>ac<ϞGv?-T곔f6eߟAFV̦آkRnr3'{iN$]'ˊaÜ.sv`'_lЌs6yk@ `HFF=Ul"FGv?-)\"Xr[2aY_lJ('t4٨ifEOHd͊k79 Mr% )rH R0Nbw\lRZ ͚n|u-y-DZ53|;lϟ?iQ̗/vHnr3"KL؏9v?mZ>&66hFsA< 5ʚ)bH2:k(n @Z^@H-+֟Gv?-!)0?g F̀ zĪR^N5A3+\[m ,eƦĝ I=i*W2T EL&1]Z(b#BdBK͚n|u-~)V7}mkwgfeߟAF2Zfĭ/[Yhljm&P}8946 )1>)Z؋j4#9ܬĀ( Ʌyx2]S&Oe}!&Q:j7fT#?"Hvl7.Ŝ`NNRrLF-ܙ,ҜTKb IlgMgHJx|̞pFa#bOK͚qY(t+6hFsAdxӊk8١'N+-fg4VG<8͚qY(t+6hFsAdxӊk8١'N+-f*I:-Kr˗.\r˗.\r˗.\fܪ TYY@SN%Zѵ"L*`;¤QC㷚}ki2ϩ,KO? ӜTԠʙ5Y\?AY ѨM_gY2 -CDd[oA^Q@@X`si֢TӖO-H<& (^Bv3Hb0MʳP˔X)pÝ6@Xm MMz-\r+[jv[QFs{Ce鲐\F 1&3HN}X)3 e1G+ &>Am@R~=sA8&R56)n zJLm)2%&N(>)ѐIVj[) ffJc'8;P Rh#RAPAy$T dQA jPRUZUgLU}M}—Ґ,kBj;_t\Rr/%f2"4"\ŲӐ T4`%3IP)) E*(H@D7g*uш6P$KoY]%HVեibzYhk'5 .jT\&3nUL@/dgV̒˥2h qQTԚPglZ1<.YZY{KN9ESƆ&Yg~oܼzI3>E *sz)(d h[ʆaTZmخlΑ9אJpIı(e e,nR'M5:0!X@AJA&)tOTˀc5taғ];~/l>5]#bN\< ,al$V)WZX{"@R%9T[  /m65Ng1&[`kA-c@'{RNMLjmۊĉUnvN_y/9{g*/TO :C*ެ׫&BאMp HQd `֤wLug̐cdR2la 6 D0" |Mkoe _qD5B枍#s@kl#v^|*}li} ^ I6boj&zl*:PIIrlE{B Ek, mtra/ Qr\J;m7W ͑Xȗl#PX-iC%]PV&lZSoܼzƘXb͋QQNx ;SzuON I6I#R4#̄v]Z+!ԡn(72o?}M%H֭+KOC<9evxHTe9 U" *BSUaN T(5!iJݚXR@[qIrCw+;" $ՠ~ȑKBB*)^cHS@VE[B{e3gv W6# `?o;$Ty"3IvH RE@g*je*eUcMAzh=4MAzh=4MAzh=4c>͛Y*ǤA zH=$IA zH=$IA zH=$IkM)fJRqqz8^/Gqz8^/Gqz8^/Gz^*!1AQa0@q P?⩊d#1]c1]c1]c1]c1]c1]c1]c1]c1]c1]c1]c1]cPa!-G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~G~BfI,땜a0z=fYa0z=fYa0z=fYa(NK9Rqz+Hj1Yj0x"gYⴋVqf/Z|VyŚ+Hj1Yj0x"gYⴋVqfvĉ~>(s,NLC>\c BmH* j0wFITE6g{-sXX"gNkj9O{9twZ{[+x_0 aq (seV5;&j*߶4 YYHg3l\K l\@T3}V7uǻAXABZֈtW#zs,` +d7ޗvӴuo)ynT7z{5%wۦP`$k墄fs,`֟wF0ALnR7w_ݸL(p=gV%7l=ǚ%إ"Tˀ,%$M]+K d4-IVThSdPy OԊ@W#uqft ֟w[)I8Υ.H:6s9zj1Lqftn`>}lqMl-eT[֑t坳&YSoe,@ RfC:닱Tis7Z{ C$01p1p1h2K%GeGd24"TD̎ S`J7Yj0wYcY/ 2@IaIgJEҢi[֣4qft@tcSNH$x  [} 1Tm˥Yo5%d{s$EIYⴋVqf/Z|VyŚ+Hj1Yj0x"gYⴋVqf/Z|VyŚ+Hj1Y/hKBA_z=|_z=|_z=|T`Ш A 0` 0` 0`` & PW.\r˗.\r˗.\r˗x"JB(1~c1~c1~c1~c1~c1~c$$ Rt 0g;B"Ю4+/< 0g;B"Ю4+J1+Mi_|FW)YZKLz("gJ]B(g@"5W^#UxU5W^"Y YJ ˞sywFv8cmHP`Vɕ!&^ @ް4+ۼi:4pIJm tૺ IIR yۤ{;*$*՛hJĴrJ(EYJ[miI rQ̃s;DL&(œ&Lț)ȕA)U+^@Llߢ PzMCB_Lz+ Vc8P> II/=Tr/ \MPޭ1+:[S]JƸX-ӉH.xsv:Q*+;BtaNhQQ@&@dgu/x&$flidML0V@"CƁn*&r/p)W.)Zr ڊb"%PJ"]prn'2.adOz)pkR-*leV֒D EΓ4+GBI#A*3192eM7Z\`- :E,&QEj|D܈dS@t3PA4y ZP6Js Mq*)Rf N$Lܖ%Iei.qyG"L[ 5}`rIЎ6O $`X9Kr{7Y[ǥJ"[tb OA``4+K|‡8f@0T-U$UA>R#$oCX }}P`3"ͯwݩrHSes٭쨐Q`K.` OSK<0RNV0wB$Ю4+/< 0g;B"Ю4+/< 0g;²      "A(#QxE5^#QxE5^#QxE5^#QxE5^#QxE5^#QxE5^#Qx |z=RTC!Hz=RTC!Hz=RTC!Hz}O&L0"`D&L0"`D&L0"`DA}њ|qY#8;qZ N+M|qY#8;qZ N+M|qY#8;qZ N+M|qY#8iL~orGnq=΃rƁO$ S`܍% 3Mlf8vFi<Nk(߸*TGm6.h175QkcEw~AlOV<3*in&nǤ"P[7ڨ+p(-lN] 6ͷkWrv y|nh>HJT4A:~$ȵK&+?[k!6AC8uSݽwjyc7,0mʽl:,[{[Z]efsb$`l' -Ҿ#&2g&xlUu-> g>73 :X^:Rc?uxd'`C']h jgu٘Y+kdZ)e^ۍ6|xb"!dSQ.`foU3eM -m[J[t1QBa o|ngEce9Bl?ݟ^dۓ6viK !뻵/f0sfU`uU-^];z֬:k;bg#70ꮕ6 8 W>Yò3O\+غjDs|@Tltu;n]a|˱s}WMsl>5ت5{oK60 9E<uL{![ҽ %$җ>Ik;u;n]a|˱s}WMsl>))=bՄtmgj>7c*H5/՟{#F$vܻ w]w;ʽlcfo jS%ŝUD"neո|n*u@hzL-!E_wd<]y#mM 17=cguN2mƛ'ٛlMn" qg9$ 7)=t>] dpu;kq.j|C]w;ʽlcfo:|ò3O3snzWAqGjaw#Q/h17(7yڹ0ްh1inh)JxpʟI2======%igK/ϖp'g>8ۊbqZlVp'g>8ۊbqZlVp'g>8ۊbqZlVp'gb(n~Cߐ=~Cߐ=~Cߐ=~Cߐ=~Cߐ=~Cߐ=~BRVi Rʣ ^1z ^1z ^1z ^1z ^1z ^1zN:)!M2D$H"D$H"D$H"L^: YcSO5?cSO5?cSO5?cSO5?cSO5?cSO5?cSDٙ m-DdJPYADdJPYADdJPYADd|`$τ\IꝘ3rJ`zbjgʧq@sn@R/R # Ԍ/R0H瀰F&|#إrs~ydJDb0IpͧfsbIMM(J՛7tl# KՏӿ+) 64XcJ֫= j&5'ܯ) DT: :䞨)*t"֗a<)I?đ_agNY$ 1jDսvw6H"\V^Ts*qc|b/xP$>TTSE'D&ETncf=c\Q|RMFW*WC&XsF;%z'#)R5 W J}#Rd\euApPs{sVjM2HtZUo`rVB `GE+NXA RRr;|5F dEhT`ooj1hh(٭AfMj,v5ʛSg EV6H$ bWmVj~h3/Wʄ-UVjs$ʎS(n%X- 9AL띳$b "TXFц;f}.6ZMQ9MxĆIiJ0jW~ 3{p)esg*n-kEw9d4Φ6[M`@"ck[U#$\LXki9:dN h1E8LS-1%3ad ƃe큈TjC5w12 `hË%V@* t|ũDb6RgYJ; *Vեiiv @.]јc5XP)昌ļM +Xԩ`Q-J¸#H\ęŒ ֢E'((Тa(%7Ҏ@PZЗ%>yd{fs_ɖ5 O,u앎 VKvNZ܂ #VD_ky` g~Sr2;h(TUUSL Yvн:\,̶ِ͛POLIBwT!@W"6a͚_{zJKYHC 2I?, f[[fUͶ:_WbS4$BU8,ҹXT &;Y.=/Bnme,&eZUiUI"m $ &f]63IDb}E $JS')?3a Ĵ$yMsy3 (G%6m*vQ؟!~8>h|L` q,)yk'$Qȏo&JlUi.g)9%'C 0fO6KIK1:Yek7Q+)AfYek7Q+)AfYek7Q+)AfYeMAAS.Iy%4^cIy%4^cIy%4^cIy%4^cIy%4^cIy%4^cIyhN*qJɰ%6l/'5kן?#^~Fy5kן?#^~Fy5kן?#^~FyS,ڷњl yO!Z(+VΖ%bB,;fK˲a(w(@ T:y,PV(g;lv$LRf-s6Zv Dk>S:;]h%TMN_{HRs.ƥ;2^]`m9>;@i`|w24lv&vĔSOYΕ/ZH>k%ڌ hL~0NšŤs<`W 1'oA&vbou$`dBٺx6dA yq @dCa_ܻAO5y3Mh4>!8Γ{fsb@e LVbr&1%n|׃fK˱mFGV<3;I]96%b2;O{ &vchU>~$.}>{s2^]rIӖ#~J$N11j{J}i;OQ(^{olnmUQ=Ȩ )@Ez[+=^ /.@TU ]H:Qwܛw^J LY6٭P 2[^( *cD骰٭I"v4kg<.=J^i&CJ%NdyrNUe;gGi=V{NQV{NQV{NQV{NQV{NQV{NQV{NQŰZR\=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g=g$e6svQ1F;(ecvQ1F;(ecvQ1F;(ecvPXĢQ(J%DQ(J%DQ(J%D- !1AQ Pa0@qp`?K+~߿~߿~߿~߿~߿~߿~h-QTzoO~߿~߿~߿~߿~߿~߿~kŨ=7߿~߿~߿~߿~߿~߿~߿A$0U >߿~߿~߿~߿~߿~߿~ 4vIUn͋,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,XbŋMH9沓]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]g]gG(@ hZʅo0jΡInJt>,<@&$*xGT 1υ: Qq¼$B˱]۠Ę2HP$!UI o0tlwowcTQApF²1ۄ_`X @4gI2Ш(]q x"@ uM^ģ~S4=SWξhO-V?)N+uCD% _BAQRW`n{v4y-GCB\IA&FxX̶%`OC1]Z}[jY 8|hg% Vق Kyk:^9މQtr8;(6tk>˻`H-&3aq*9luwx#{ra]g 1MJ ;cVd%i@uM^3U /i1H.S 0X`ؙ- -\ꚼfqcLZDJy% Ϟ벅U y2Lj J Fm%=1Y8>3Ud,uJDIJOha `&³Ue^g0%&"-"貅ߐ @0N^Lv`K`^3N]>3UQºX(.nܪ"0Yt|1.ʾj6K jWG,>.-N$<]@@PI(+sX$粆F}aVs;a\uW_.EB]>3U ],kiсڲ,~qEۆ!e ~g= Fr2l#Pv1ĦƳD#k(o .#0X2 ?HAk< * @ [X`LO5DNfp#34.嗡V6bl3tz7S fhcrjLf@5x k10iHoK(Xr_j_VVPp`_̊9p0 eU/ꚼfsTM@qGzYBÒ>3U .@)de7[,b\!p_bꬼ& k& T$'ꚼ&uã\0kǻR\Lx!q6Po [BɊaeĽj9 Khw}3jReꚿC;,PK=8>3jReꚿC;,PK=8>3jReꚿC;,PK=8>3jReꚿC;# ְp,ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|^))VV#tGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtGtG'>VD+0]yŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,XbŋrEhduK߿~߿~߿~߿~߿~߿~:#̂}b tT7Ç8pÇ8pÇ8pÇ8pÇ8pÇ8pȀQ G@E jիVZjիVZjիVZjիVZjիVZjիVZj騏Rա` @ @ @ @ @ @-͉c[ty 0` 0` 0` 0` 0` 0`Xr0KZ p @KZ p @KZ p @KZ p @KZ p @KZ p @KZ p @KZ  p7ϮUx;]u]u@!A- jHi\E^M@f1#jtӛ*x9I pl[nݻv۷nݳʥbM$q88rBdǾea1ON%{3Zj g9[J\"|p8Fݽ-KXV$8, :g) UWwZ[h#[xi. Pmc 08"K(2]*.uqM>[A@B)s`"2;<rw(KgiYZ{k=8W43l_@*q8FtF/r\g^@a7r3^(M2qLfc e# fƢAC}D;|d(GX+)Au1~1j̱o3Fdd14E6bn1CbO}0Ì'E$/ RhѣF0+>r.T!c Fa@ `oDZ #` FEXe x8;g8qGgŚ_ylbJ!-2`3P͵E^3AԎ)*(J D٠+(<¦ l_]`f.SP8\B?#Зcb]"e!- r(`h=S jiq ac5$hZLWk] PͿ !c%4V2B&9* &p4ĐvCA;Ab~pCS?.uU$OIB"ųu'AH˖6)z8 u[; Y׵o1nC)f`#Pȱ6jˬ)Tr02w ձVr( ĒŠ+k(_C]((.qe@Zq+RAm$EL 46% mH+gB#,-89PVWnS.BHLp%q .8TR "qM( FnKlX((oo`b=Xwbd nw;9;/O$(Y,18FN,h 'N+RLǔ2sET ' 0\ X͔#2IHLl8Ph|j,n4\6w LpQV.TV[\nKo\x6N!{ ŊSq|ꬫˈ5,x h`]&u^qV2~!P,GqDpi4Z@8.usf.YƢ3ҷmg!tI[mLtud\ 2^@8ӵiGEМ ` 6Da*AYRkPrQqK Y@cIRZ;#nE|7F,c g@0֟B؛55mb/ {kО@B0ǯAv >Dȗ  k2R?cp r$TH$ LGeГ, іR&%uX4a*% `ᅰ_JAEC5!qJ6~eOM AT%tbOILpX ,.T9PUUreNH\ n@Q"J_oIP  bQ6X $"H* d 4u7|XR{xC4`(yCz CrBl,dlAs7n`88H @4}*0%l34BP>da ,B ¥ch p,zǏ61VHY)7N ,80p$0o=b!`էH{ IB}:HS}6@ eD|qj:h͟]JZj&kdX-ͺB-,O.#߾ Lb 1HwS@Q!X br  %W(0I9`D#M $ޛՁ]/#%u[P KA5 GtznwBҤHD=^YnY!,($, @ @ @ @ @ @$ A)(VVۜPxJ1wD$H"D$H"D$H"D$H"D$H"D$H"DyW ҁ**o߿~߿~߿~߿~߿~߿~ߌL lWN<SK͛6lٳf͛6lٳf͛6lٳf͛6lٳf͛6lٳf͛6lٳf͛>̍BY߉xw3"D$H"D$H"D$H"D$H"D$H"D$H % RhocyիVZjիVZjիVZjիVZjիVZjիVZjJhcydɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dBX(/X-.^zׯ^zׯ^zׯ^zׯ^zׯ^zׯ^& ]ʪb.6c>USQqs沪5TŔ\m},oUSQqs沪5TŔ\m},o.7P8G!'X{4B[΃93SދhG #H G>!ṙfCI !AfCɄ׌rPUxF="qUQoĦ]\*ku\V"U)+u{%%U-J[p&d ;1 /&E s$ǚ!A.K50Hp d7ջXag2VJ'(T84W?6.7V&YKq-IQu\WdfWN(`F ]!Hc a +;X}Bj^XXhw漥FLdB:svJ&2pBtɘ;lnY )p) f).m>ֱ-;n=..#q0 Aķ'1d*+\#H7(x5R%&@\H-A!uM Ҝ*f6;BVFLLeNc@G miPEX{$YgsĢdMHʸ$$e{.t27!UPL(kKp!)K(TsWyy3 aBKf"3~Jd8>1(-Ibėe㢱KP w @׿%WG8R OpI!%H@fܫ2xF_6K#s0kaX1rTF Ewh(̖idќbJ49ESpG6.#q5L`D&@EU+9-3Zn%@2ds46KD7w~LqDkI e$.P߹!b7&Lj- GxŬy}s!<dƢQUo6.7{DfBanfNX05 #ߍh̜z`/$Jȫ!wDeդbN>BCV1䌅&;Ty$HO'>$))S潶HpC' K1.UnZ(XD99lNr䃈 04L4 hQf9Qa`0} J"8^g\[y|+[尴|W>fȗ}6!Ak%U0ؐW*z"Y;/,&uȕ Ք7xG{`!b.veƈ1LNb&]clDrCvȖ|.Uj^y<"MlHg'/YV KP!Ȏ ~1`0]x7䀂1KQŢbF2"~^8=}u` UAr^K %:kؾ*69th^Wv >e3)V|2‘}v[D|d2t`KHk8PFɄ0\F;$ / s\(?[$+'_;F,<&y6SVrm0XB6W}Zldt`2c RkxPFȰRs@;&0/ 6.7{6|a%,o5Xi'2V;8Łťb=WXs( (w¿5j lG0ڎ( JPY/@:{ N<6D5Dqۺo'XG/E_e#/9OHm LW B ,D|lvvׄ0JsPwǷ2%>Aelo PSl`_G*a Pv8Ï?5dX"Ȉlȷ|UP0]J8{):2Hߦha'UNE8g/ Dn b=Ȱ* Ta|u1~t#s9~k )I .pRnT" vVp"x m,B6&,A6.6{u]1k(Y*Z *P~iTRр>Jy3/ RG\t[jSELTHϸN!F,C @8wh+uQ1{:REy~͊6D ҟ@Tw5ݾb\=EL FȉխQW4cV*}`HE8 @kl,X?06(pJM?w&bŋ,Xbŋ.OP5yߚʪb.6c>USQqs沪5TŔ\m},oUSQqs沪5TŔ\m},oU5I*d uE:QNTS)uE:QNTS)uE:QNTS)uE:QNTS)uE:QNTS)uE:QNTS)XRWm%|@ik\_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_j_jk:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U:|ϣF/U: VW[}W[}W[}RXVf8lp&hoZFS-|]Ow M8 gخBN`.ފ dHBݻvۉ8`@c7jb[dMh'+85_ Tb`j[Q (L/J؞DCčd *a8S)SU^<_#m4SM_NK:%HB& .dTR"BK'i:a! }]'8{Ge" X;NsZƊ]`BԼ;78Qy#>DJWb;,b&iqh1>]̇7d볉@h VXd,[ EY~cfm>L^w#!|(" ;@{(XM*b$dnPB"Y8>G-fq 4)+us|wY 60 lDq)bDeT$ef,J-лp-`U{: -sk]bPVlGc$./Cot–w'8{),g82咼# !Kv~   ɂ{^Bw ^JY\gf|GOZǥKiCPX]Ы1Er3J:'ʭ#IJ\|#Sɒb\Jeac6X%ۓpX-)jn?̋ܨD} Q ,÷ٖ#= Zfq\pQֆ ǘ.{Wh$g4 1Tܤ<'罧ο0v\j3LU >ɿ4 <(c.fɇO >Q%Jl]_^|Is[+bHX@«2;'8{ $K[j J`;Du?BBXј K$oigb^BW 3BqO4Lz>sϗ}+qq^ԋ.}mY̩48%tjd ` nyxɀ8lGܨ[%W~@$*5?M:&$Ȏ#Rv`02{{OFLSB> P"fƉ7-!l"sš\2 Q5( ;L'dYL1V }Z/$UW-1AIRu&AW;F/USʈ5"Aa/Ŵdͷ0%ل3{{OFLQG4AZHUh3"ȬIz̰\|-yp q"ԼEgPP`xck|\Jya,Y,P(r ,pkjݐ{h!+zLt9#oX/ b՗ Nqa,H(:!3an#IH:qH2 _:Chp 4ʼn63$¡㡒>E$2ldj'e(3bs!7o B F LH bEe^@JCT/X)xO z>sϗ)5!KOJcSrױǴ% 2M7@B ů0 :DIi~,^#S)Fy.i„ձwbu Ո+t[;!$KUEkA2Ml\gsדs_ѸS.xġ#B$p e"@JVJȜ`=2?D{؜(D'uVŠacBlŠL v=EꮱˎhѐƖ(,(N)!4ٷa!^<Psϖ(cI4v#S)E) ec O3:ęr4j m${||>[H/Hp;bR KӼjb[<'؆uNqbT[R:t=UXL, OzܭTqA, 6&ɦ>_Ev*ar $H"Ic2\8 w ,oi$0Ly>sϗ(XyX $Mb-Ri'R\=My4b % j s yKN2\r51zT++xJ8M>C_䞝@"0 ;lVJV0FX(,N\L႙+Jy,$y{OGL Yd>TVF21{؜hDxck|,RČoQQZҪ󑩋o{$0C*]*V!d8n)wx˝`9)tō@2҂Q X 3ɖW}| z>sϗ(M2Z`uX(BQq"D3x.Pd, V*[W<y/1q @bԸ3"[Kqܡ**{{|< 912VMCfgx0eT[^(^QBAZ }5gi (> ؑ)3&xx@9ŎZ7D/5''Mĕ#=vϚ  Y`>qطn؀Sg(\ErBl5jը4c-m5zbO[B-k{&UGXՄ1-^"]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{OF>]k|#S{SjR6VZjիVZjիVZjիVZjիVZjիVZjիV b 3_~ڿ?Wj_~ڿ?Wj_~ڿ?Wj_~ڿ?Wj_~ڿ?Wj_~ڿ?Wj_ J*Ǐ|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|帘"ELR\ DڵjիVZjիVZjիVZjիVZjիVZjիVZz<)OwaX` 0` 0` 0` 0` 0` 0` $\&?XpP8HV$+?u À:aBG#XpP8HV$+?u À:aBG>V !_aVVC,Hꭀ3htO{˶4h`% .%[yϟ>|+Aj x#IY88 Zڱ`eR!3V@q80WKkHib}?E⬫ DJL/,ibT<5J $Pz6_ݠqTUkףB"vo.U 1cO&FGph RP2iYd,R!V.ڭ\idRSYmTg`,lEUȶ þR-ZOpsQjqeqMw  6P-4)CIȐfA`hv1BȍѠ-/*JgƠ "K 6|@d8pclqI:zp MM7 ϋcݨJ2/79/,g0a*$|kHz`>.`._kd`tf G b`"8df٤FVCX0bc QCJ 8+B"A@Z(-I ̽f30&F {rJRy-2ǾVf($(4FZFYճ5w9|͂B׋Kz.iR Y2}!8Fq\^G oJ10H@vS,FߜgB_cZV=4(p/xwaOtY.fE.7/Gu,ZqIO1S) $7+[V{qڡd [[#tK" Ӷt81X`|o(>\8{%?A :@b ,Jba1\uܙFA@K͜\ًlu]3r?#ȍ <Ť':elkc)pF]B@ [{m1ja4-t98A =06/vT` bVʷUv,vaH@Epb}v.(2} ͆hG`&ad(٨IPad>Ӿ!"QLXW3ģ@ K%ss8mfX܁J|dEȒ l3)(e2+HǪbY~V}ȴ6K9@ɋ{0?x2"c ٓ Ssp >p/_p"bT詸 `=PzFBF3Pc-7 |gK*ItYXOCl;p1rX#{潒iS]w5JU ^x%G/8Tt }k8QuH,[X2ug  3&:܀1'Ħ$}d Fד Vp VtofI]A_}a20b3`@Xy\FT3dʸu~Ƈuw*m/p] C/x B5rub/h]&4sތHNO\ʋI1`-;˰H!5>C4:@(Rb(09LX,,2` {Vzv:ή/Fdr9*%WU`8E6s졖ed1|Q])Cy.౴, '8h,]BeG &`/$ )HrT.!!ąL Uv0(Khax $F`#;ǣ[UF]HHJE ^[b@.O8CӇdͲ?>7AѸ+t=;gWw| lecQ3伓#s졎z,t)3zKjݰe<_DB& &pqR8bf0PVgvHL)..Smf.UY |͛#g҆1`laƹe&I󘗱1)OY2/.nˆm([:& @qk|e=X'DT;D0]qR]89d0#ɛj]O`Ŷ"d/+j^/F6.+Iv_BG C|6$BY.]qJM3HdKdŒsV4Dxwk*R1]yņ` P򠪲v-j4&_fG m@/.[:W6)dn<89Jg łÊԂ3& ZRNYyv<73s=BC%-@cg텬jmVzv:ήOm/*ŐA"csvL#17]WAsvbCwB2kOIٱAn^s<NkqZ0U0nc*Wզ_ [#싙j Ȫ_nU$CŴADѣ!"&"v|zeO1ض[2@NY  U)z#%.:_+q{/~qBl8AرǚW@DiT`,Q{NR1x`CJ.a k72`6b$3t;+ 0I$WqJM٫H3Ooe Ƅ 76рX Ir<ģSR* jD)*,  ls Ĥ7 (PQ X@hzt]Kjۇ 3H25G^@:VZxA!P?1 X'pu]Ɖl'ejk'<=B88c7--/Ŏ8]BdPoUZLHeV #X ΏFCQ7,75:ʲ$^ː] PWr̤k!c~Vzv:έ5?Uc]^b`8HR`ps8Yeɹrfgg0P3D1LgQ)^1 bu lr [ rlVont}6œ&B@@BYk "I/pMWlPpR'l|3" ;&!1Y2"X9g ໔p [Lؤ 8.eR.{)aqh%%x>bG*g_P.y:Bpx)N#0Pbs6-ػ2i%J S /D3P~*]P#³kHUy0UǑCΧ2uv$ΏAJ*̔X<@`QEQEQEDw/`vm6<)%a4#YdQ"r3M.ztE qnAX" I[caBUŝUÀ:aBG#XpP8HV$+?u À:aBG#XpP8HV$fW\&+*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JRԫ*JȐMinaWB4~G4~G4~G4~G4~G4~G4~G4~G4~G4~G4~G4~G4J娐 4h·C~G~G~G~G~G~G~G~G~G~G~G~G,Єˁ҂Ib`P[ypappl-1.0.3/testsuite/portrait-color.png000066400000000000000000002544341403603036100203560ustar00rootroot00000000000000PNG  IHDR+\gAMA a cHRMz&u0`:pQ<bKGD pHYs.#.#x?vorNTϢwIDATxw+{o#!DRbV[[)--U5iըRRFR_Ğ!"{''j4!RR,(KK@YsP}:T@@UM6 Pݯoɴ!C{Q0:H<@uwd^γ:te]&5_K\׷+_s'rv'>A@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@iZ>ӥv]ELɓ?3f؀fMX,mǴ퐶ӶCڴOi6I4dL2yl˔q>YrE`tϊ+f <8 -6mxq2bDFȑ1"O?.PY-Ȁ,bX1 Os\,{~fʔں/y鑼4ggq(/20+~=+lOׅ*={?_?b$w^7o2eF!O8/];wg?5k(eo[*4Yrϲfe;׿+2c,44kտ^}vY:[KsŹu0͙=RT7rfΜЌtdak's.z>[!YxfK}9K}9;.}WdxGhN.w=+ B|m\,X8 ~wo٫W>8?1ޞh]6eˬ~VX?+΍&Ьzsusձ[v=>z&kl~j-p__zK@M7dzzC'gS_4`B<$Y:\_'K1C/ QQӧ^?fϞ- 7~}rylqurHϯJ|Ey _.jN5b5P/+@(k:{Jֺu~wJM|6z9PGާOOgmjy7[zdz҆9Pi *G=G!j>(}K瞜xbv]|\@Z~KϞ:kԈ̯nK%U6(=? ʡ p mј1cDiEr9yGYBZ0fُdҴ4 BN;zs:(mZ>%Sys1nqD{pC:H2g6m$og>[?Mbʁ捱bE[lO^%ؘ^O䭷2aB&Li.=zg,h\3{*<8O?m@-<6g~/?Wӏ<DzYgRbYbou@:>wjNC4Z7?~dG=9ti]4"wM)1W\k>sG䪫r&dr)i{q{o ɥV.V4 !3G +[͙]vɽV"W.^&!#36ﮋr*1Q.0?xm)/\{W_}oY@xleLY/llYgeesi(ʬiӲVg;ԪE jc7ֳWșfܛen7 zlO_C 4k깺v饗:_Pky$`>d^;q*oSN)|nһE5肟W>oog߶SsqvYfelIЬTCC!;N:U@m?s3lhQGeUr%l 'dҤg݊4}Rn3{VyL^>\׌6gN1]t*$;\=3fo?G?ZhTԱs?s^z)뮛_2fロ /,|=,MOܚSvɡ+#2kcZOUb W[maÆUr҃>N>}Jo/ CsGzkS8{j]g돞7;w`^ffIPs /׾+TrC93M6sf&?ɏO]vg7?ɜrtP' 'Icd /^oqV K #tnV#<̞=A_ロ-~3th.lرybpPffOyٜpm\N=WG 裏~l4bD̞$7ޘV7-9[ rL]9`.Ȟɜ&+fp_R8\ꪫ:uTN<#8te+Paò9lEƍkv7vlOb (pΞg幷flq%JsudW<3`Zju9?=s책ybWe)UwK>a9J2aP)zlvXSk+s=?q0?%ާNKSil~]Lt%S՝wlIA[oVOS="Ec|A^i7^9C%$e)zy˖_|2,#/Zk5׿uYdbǟ8Q@ :JE >GrmM6گ(Sգ=zܹs[ '8{]ϽL@)?w.ZO&`N>g4h[5{gQ: +wyM2T-~_^}]vOs);9>˭]ȓ ΨM3T69i8et)z~m6GqK.W]uo~-Z|g>͢E` ћd7咻l÷ʀde)zuQnw}s}rq+?szi׮yF@ C>[2fRvicqwݻw1Gqĵ^۽{YfI`آ[@+jk0 6h F+SsO=G_~?fN.w-[.9sn |wJ^_|$O*` sl.qR9d뭷D,{w1n_<)S TWwz5S|df,s.x9v6OhzW\9+;1PQʾg53w+d $9X6R<,-/袁o=nG{|g7^7,frVvy 29e43&MZͣ qx[ Dtر%\rqǵj 9~#?ѣsmb*Ƣuv=!n._͘W% ̙=ҔT=EEG <߿w߽;|=IZ嗿,v+HCȐM;͟8me $M6ڊK|' PI[mՑGd 6x衇 $:{v1.[\"f@:es39 ZF=s7dě9~i2 zZ7o/h(g|LN{}} ^7+n_६5qLN:3<HÜ~j5ݹnUO,c.Zl[6Ξ]vg7Tz qZ([&3% u}YڤmioBKw۹g90f $ynA٪e?|Ƨ9{PQ!Ξ_>}⬳2amz`v]o7 2j# $5;l| R/Ξxne뭋b|*պ9~{E}F[vv&,S8{ Q$KS Rccn893󇫛l-riv>Y=GeRZkbg:5?4nTN#[MO9M6AgY9*B=g ̛o >3gJ_gh.wXwX}[98aU䊣0Ip3M6Zҡ|ɜFJP@_={F?Bۛ#չҼ0Oq9e/يP@>:o_~)o9jl5yyi}zo\isPˑGVb(oٙ5#͘W̿kM ]JbluN=M=LCȁfMl_6;?W+lyC9r}?˕fM=X{\tQZ\9ЌsM!?Ïu2NB;);)r45פ}EVX4_:_:gWk2~`7hK,]J5h)hJK,nJgbd 9l$[')[M˶fE|㤜R<4mI/ kl5˿j74T|a3th;wRj_t# aA5倻V+D=_LV|nذ|{R $&56f32aj h\29Zgg*4Ogm2kcrX}XBPcθ%<ޤJQpٽR~lY&L:Po\Zecma@xzaSZr5RD\G+Hkn^: sdϿdʌ&OyORs}t>Bs 7̳J kn 'ݐ;|7ȕZ~yd4)l:`^~4YڦMm6?vӭOZifso2Tg^ϑ4 _+J= '?QGUhiӲy l ~-ӭwzK7W]>K߻}N;/[gFitZkN[T1, 'PfV[;qNƿo?3` kfКp9תu~rEZ6r Ӫ"B6-[nn:Pw~br3S x ?cZvZK||'{ST)sҎi]74-Ro~Z4ـ=:}JPmu.$+$ɓ馹6Ks+4ת[SC3wfSxY Onys&MʦԎNҫf= ewȡ9o F;G>x}ᆹo_y$묓k2Pn?7s>RH*$׿kP7wYԁ0Bt]XT)$믟+:Tb&dDu B=@RP]7^+1%d2mԁ2V=v.:UbO.d,us0o9׾nHΕc9:@34mm]$ @5RP͍7V;qȁծ"');Zԣ|%7ݔ.şgؘ .ru]Yw4H=@Y}|sv-|ٳ"2L*3;ݺ>ь~\s*t)'[nIO4eJ &r$h%f*C[ңG6} I/ŗD9zJYDo6㏋}l- ei6H=@Xazkz*|W_͆fH|h-*1% @j)jgذ,\]Gk>ua%&zaPsԴ˰aݻ{,묓W_9ǬMwD9zڵ첹SDݗocw.UhsT+5jРv[Y 7qD4YzZ؏*=\/=bPsԢemeE k2E@SmPp5V^~TNs[8T)5g~{/|.w3D46dU W/u*ۅKҺM{bX̶pyMZҹ[D "}ll}d8ןWm^h,6r3>v߾÷֍ǿtn`_Ҳ5UsԐsmׯ=6&orڴ{f=~Cf4_ܚUtGs|ul֍=sԌ%g% #r1ʱYa̜w˶lʎOׅ*:#2K꥞&nKO9t\Y͚~yPڶj[f-3!OߑλIl]M%LMVUM=@7ߞ\+I;ʾZ5P#Zz|̣7'+ ^Br/nˀ2gճ3sz^}מJì&Sͬqڕzۤpj9٢2p$>Զ}Y#ˬg+u/? og{DT_./ŗKZZ5WکZ,nˠA4mڽbG͞)2e\&~ڤnvҾsڿ-}MkOޞw;T;U曳b\ڤ[t]{qQ$5V,#]yv1Ps@il̥?A=T#jz*O'R P=&I;fN$9J̙?$@-QUʣĭbƨjpٹbf?q$=E4_'OVavN+w\( jzhfL͉f sny1IPs@s2!:>*g zQ3^IP?s@30\ytn9Is43#S'f]iӮw\wbuBOrC9hffCkұ{]%ˮ-jpO'ΰ?g;uK=zfw:d/Ȯ'7p)+{\ut::ˬeYi٪vչy1Gޣy$i%_j['ɀ՛-^|$3^מt sԈΝ; `L"'屛IҦ}YnWӾS7{f^|8#Ɉ{2ҾHsTW\!u$$5=ܑgUIVYh>gEπtR46f̫ysDQ#Pfpdjx(zjȜ~)_~]3:}tݬ=3fd̚3L̈́bF=Yezw2׷8m\ɏ E=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPF=Q@isPYӐӓu۴n#L&$ }-tK:JQ@%̙+}LS3cjfN{3f̏|Ynm?]:L;]N;dсYhh!i{+y(y8y6>n|2m_&_ݓ%UUe]HSյKˋ-5t䁊B?ݟWϘWi4f̏v֮S_.}gҹ#Ui;i&ϯCߴ<_o|Af%c1sNɗ?u4\];\|q}򷿝ӑ 1yl ?sgW?%/>vQAzpό^ _#ɹɵ+O4%+?vJMvOs! 24$$%>,9"69>suM=0ƍʍ-͔q(=]Ǻ@K_&%m.'$?MΫNN~(2w5gr!~$3'9+9"[LAcrB#'Are+>%Y]Y7PgKe3T>sK=0/]w_@nONMJ X_/u{.6K%V#3s1G;T++2˷ _x ۊsE=WڵKǎw;wv׼͈ |oӒ247O- %RG=W2~|Q>2_|fm? C*~ڪ"MC3z4n\ Ct&`dޜמ,v ާM;a|'8xU~B\Vd֩|hf=WOŎy#C5k[ȭ+ M=q5'-1̗NNcaelhm/cf= wRgv.%RGsH=԰늼jȦd 0_fKg8ҲWϵ^t(ld\=Q#Pƍ]>p|yZk+yiRۘ}C^c+ha#z-J=S3{f!#\d 0_S na1C+ɬBcR/깺Gŝ=Jؘ_;. 1zv{(ԂV >"lR6#zV&'c(M_s2ȇh/~$f~ |XEvFtDz瀚t ۪MwVZ 0޼6F8d bFwEznQ_ۨG946+ yg F_ɢ[jEq 1 UQq\>Jz+X̕d37Xܙw.peH obf^ȍo_i 3գ=ҮtrteIŤ FX୻2Rҩ/J-lɩ %Y/zTP=V,]92_j"f!mo6ddv1$y-B[Sw3ujO/ddPre2_H`%mڥputZ0gvS~;jQE>[='%',WNH/>/,:;9qdɕ?Ξ?LƼ9 x_zY~, T7̔ Ӫ NvN.,~G_&_#P䑊ǤPsu92ロ)S5uꇿ93]W;:,yvҢł}3g3iL34rqOd0߹sӲUY#C6ɐM8=.p~;}1CX:9,9R=|5+ujvr^JmLT+NAg͞M6#4ӧ7$1CfWTu@MUsKk!+sr99p`Ldj^;-ž%f?;&{ǞnpQՙ9s2n\ ׄ 9,d~ b̌~:u\̞\vr}Z)i,ޯ@}kRn9|/gΜݻdg=ц7iharF+CӖPi33 u,fLPZ'HQ;䆤#sƮlٳsq`tuEx.b_ǍχAj~<}TKgevGt3ԱvIM>ꤳċsez;̗ns̡n}>I9f9G1u|(\fQ2gf;ط)3ԽN I-=mrNr2G=kla>:<ԦΞo=0;'n4fI; ^$(pE7Oe $ݒے kb_$${:̅zU=dΜlqF6m;}UHc.I9 ~ïbAb>=!ٯbdS՗ln)gu֌+zTe,:uS&>YVJ |D/I*˓K^$󤞫/u{٫f3f%5e;'$c^Wk,sf2meŎs\TCcIJs|lufδ FLݵwLX~1ys{[8K,f`6MOVM\\,suT}߂;>VԈ [ [w=w2s*vV ۲ɽ/񅮽Ӓo9Z/\} .1XP &S~N~DV Д&ׯ(I p/$MN6 ?i8 EPWƎA<2W^)z.j9 5I9y4̶X&i,Ev鰨fD/%%]z8{.Iccv5=$ n=Wc?)i.vAXO%O5,.z3mZv!Qլj 'Q @x4L-p^_KU ̟W$w4 |) 3M=WGOϔ)brb*4jd jO)v ebrXlrq3F%'_Mt/:spQ1C P?#uI2W.̌"]Y|+1ddz5l}W}]gSՑqd13ݒ ֌2_OܚW5Ɉ?;2?LVmlCcR]Fۘi<鷎 ?9GѮNǎ3",;zdL|_o=K۴Nϲjk2ydꔥ+f`nLNFUJ YuMp@3cN8!'$oNˡJeK/6Je ӽϼ7xV+ޘ7浊v߳1麐|#/vmw1s399$kHCrtrSrQ2q\Zw:wί~^Myc3ɍ jg-J:}3? i\mH_qM-gX5y"'h?307/&D7_%_NTYA};s:(E.$y7:,DwO׮ΙI2yr&M /7]ロN/~aQ@)[z|WY훟˗r~u[W.<4FVhsA,';"IueR{fܓ8cK碋r9|g|rYj VʀYz,TY$-Z,'3kV9\tzOYk*o`~sږYiU[s0:z[;tWS :H*%V;xaFrU&QϑdȐtSn=?Y|cYoj +k/:WY{7.W]K/Fȫf%&=},^ ضC~zM}"/wdo[;yRX]O |&sj}7oKܐx+{_믟g9眼b^~9睗k7A7z{[9 Pr%UܠMͽM䊬u%va+; |ܯCꠛ{SɚpGss,T;NJqT _9t_[ň{,`pZb۶WUGvyTNoǾީhFڵgĈS6?(E%Ϟ[dyS:v-p] ߑnOevRKS,oZu4k˘e][Ҫ|; k9EӪ/cd3tY/OTXN1{F^|fָo&@OvH*|Gv?'oӒo&/XK=G3n袴K\ e M3ӻRr\Z1gvF)~'4$y9"ZIJK #%J/YI6MX uJ=G체m[j*+rվUN-DlRϪrS :H@dvSek&''U<-iD=RѬmUN9Bs}wf%T+1Ş'WzO|;~5s 1I{+5WdOɎ3*ޚܛnM#>d5+1ь5JP5*pff%+_nQa3g}[?RNvrTZ%>χtMNMIVl&'YuG=GsעEN;--+Tx գ]ڻ_Zgo81>Mѧuŷ3$9"XNN_/Jp]c90gqU`UWbw6Tv_w[][O0o[7*l-i y89?5&g%KZ'?KKU*XE=Gu8J9"E=K۵ss<[uH(S6PC]]qnr%RGsT}*9"Es˯W+PZ>My%^ZK=% $7&E?PW2,Y\tHDKf[(B=Gr§pT/nշ]k:s<<Jg9A $ grkr# K*/ 'Pzo>zHŎc2j渼xfS(]4N-r-ɐ_3+W|P'Y+B=G2$nŭPEz.^{M2Sw9`^<+';Š $I.L =W#-ܝO$Z.uA=G5YjbwT>E{ױ[f乓˲鳱$E]RGKnN<ɖK]PQU ~SAYzn46w=VvIYLg | KЫ..K޲hjzjһwnX |NCM*s }.m,@#N(v6=$9nݝ5VrESsTϞQP5:vM +@gM/Mr{nħb}Z{+&ٵ=5)Ig>դGJ+ Щ :F_-Zg@qܑIvꄤmaESsTS_=եύ{]:vv{_)[c?IIW ZN{2Ɂ rSsTw)v|TBϞ{2wM=T?>Š |)u)b?KƩ&9tkӊU=ƕNs*i6l]-]}o;i-J?w)mƾQY;u')HG.l䍚m[ܛaKƩ&Es.+c&K=|T95kF^{$N^:):,J &Y]qz֩S'b`1C5i"ˮ]U~Df8zx n ?-Hq ;i X:5N=Gո̜Y-'f2-p;.,g^z[W_ ybh!KOt\w \SQ5nubL{c%ԋEszM; +dFJ-s4q=7`A7uqkSQ5:ըbtp%T.U!#O,|Isstz`SsTg /;AU~ke*p;/c싏H%WjqM&?WnKfH3ŽqU׿.| TBo>)[[z5KȈ bAbs1NK&6{X:5N=G\vY᳸J;eJ{왹b2}"I Da#? mXҩq9!TbgA?$KXS弃*/^8~6.n*r׹Ң)5Z}3fdsil}w,d\bjޞŎϥ~c{1sz.9)X1Z/P&<7-v6IPh=W{ u7jTN?=[negj;{WbvoP%;ό_?]*N+~bzI}]j,i`6=M=6.n}:2rd?#3jTlCe-*'UYebx圼cf5kOc joZ,PצW.,vKE~hy'N+r|g:\;6}{ǚv>d6$+V +XP߳)WN߻̜3Yn^Wkϟ93bobPߛW~KVhg!+g|/5#W3wTh+mY#P^"S_)vÆOpgy)(yYZ&Y4i2lX}pZG.Yy嬲[.?>=OkҤuׇWoO>#={~=zmL?5n\z*>#2{vةKϞ-Ԏ}8򞌼'g0klwNcwY32=TtҙSsE$i2I^I2kF&Jd k8unnaIuGr|l,,yy'9?[2YLb SsڹԜ]9ǥm@㜌{3l.aVuk#y߅2 I b䄤3nR;9f7ԗuv:>Ⱦgb>ԵW/ov2 i\z$W$?9gdmh3u;.knC]kl e-yia_1RHH.HD=RQ;.#Sm;gfzu|7[C{L|Y$i$®W9jaCu]v][㻹FtGȈ ,d:V9Ox`q4îϲk.~2Z;P޽7YH@jlY?&WyV_srɒ>xY#nȗVsčiA2OOѮO(iNHDݒk/GMK-{ɞsMY.Xo䊴/ \^Y|?Eh)iUhrGzZo/KsEXҢeE;;-[9@Ы g߿uaygYhpVNLOjݵoFgo_C˷e.Çʎ39|׬Ul|c_N :H@ŵHvKF&%s$MH;̝z 3k娣"ս/;ͳȐ!-Ej7~.{wVYsۜTv;!9\'56d䉅i,-a%)y:9ۧ]t>}Yt,Yz+rYk>ˮezf䵧T˔:_~$i6m;mǴ},dZ" /Ȣs14m**{7$#'w d~39I2tUWws̯-ӻwzJ+ϚkOԢ6*Yzy HiJTi N֧5$vJ9sƵM8v2Jy6znh\rsn\vxj~}~^;׷2U/>UrXK@YsPF=waZovNii ,6v`J "*((&% "-H,!s<,53>=3p 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!O `|*O>>lm㠎PzҲN<*צcnj)c-Xt*us(اI] m49W;;U!XWVjˮۋ=SټO,t,ojGD@qZOT"fz_I.iZvf\آ[勪{ޣֽqhޡ׷kc!1K/oT#L=5ESb*q6;4c}q,Mc/t}p'_8 XrԯyJPd~Z̩w:Yi=}mw'mIŷ\9l=Q"ۑŭzn*?x<ˁcVlgt^b~-GU:qkjH뎻&,9&LmIkv*qFO~s;4]6Ro,d7[[j\#E<Φ4szWfmښyD["_^آ[V*x4GW/գk%r{y:PR*8'>ѳ3{ z{;^VJJ4_ֈJԓ$j+z'iDmm?% 5h^Q5aМ c(3Gg3tF4ҽo+O};W-.Sܣ?49zȩ#{56m v}\_a'v zӲ?}}P=pEds'PM}N7j׎؈"n=юlok&^τnDA<uurdz5_wQtF*(m5 ys+ȀYXkޟj5m\c޵9z*mCrs-zso a\]򜎜gy|37-@7݇7N\#GE{͝QpzcPJ[i{1 TV3hIVS`-uXm9@%m.}΁yUjW оcږ}GHև?N pE^-[E<-{pꝋOװ6~PWX-,(ԡ,㚺G%}YzeˡXU5fRg9zVgjz=m;W}/>oq[J%(2iV}nQ&^фEkJiGGU+4JQ W^ҒvL)ںBIgۤJ9LJx;g{m1_*VGBT!D-bt{uG{.n3_ߢ{j(:(==]Τ%>]ft4׈qճ}OP5SfR?МUъ#ֳR x7i+55p<[լ7S ?ت-Kcmցn1RbsݝuGor[/b+IDATB5Ѩ-znM׽&5PB)U[C%J6=\>Wmk덅z};_?9\ť覛4oȀIOѫ7i ^ľW>jqvR`H1bB%Q۵7{I?}od*\ip((ԛmzv-?_=Y_?wVH^+۔x@lܫCJ%@dD1fx񲹓o|ez.;^?F Lۨ-(2nԐdsrm> +zsy[ͮ(^6Ohďn_YQ.#;ÎjNj&4-_v|Pl3V0m;H%J :t,!Z0L|{j-vp7C[k6*AObANR3J-{X9`V^ڬTEU kն}eٱCĈ혥^~XB-Wo4ӭUx[+(АnҲ(FiM[b| 5K;ΣW9丂 !:Q xک7u_,N=+U J35j\*_gh$,ϕ"BŪb>>z'3!HhC%,+2fzf_mF5⎩[7C%(2iҎit7ԳTP%!2^d|뫇>R6vd*(J֦h+[n`')`G pnv`-JX_b?.;;[9gG?rhN-Zh*AO{oz$豹 +Q4a:!_?30Dguӎ_Tȥi%D<0>ŭUյ4nœJ`擥j3L;e-5>ųozbyL-i;EKF GDyvG.Ɠh|CzT5oBiIե/mnCf}wG0gq9KG)2`V=w".{F?7%-d<ږJ38~njӶTru NJMMY^o We-]r[u"[ܯj\keeQd W n zWmŧoEg腵,]O깒"hGEEW SNAovõJ/"I~!x jy~# ծvﶷ <,3"Sd?v:bw@$x3Uֿ?R tEֿH>S VϕG[eĸsW |9d@fwF6,Iy/i?}t]pS&[#a4v$)?"Sdk؝JR~ ԋ/Wkhr]xU=+[xJ֙<1ⲶnNdT18OGx4pҼ5v8}e늭fϋ|M|ńxf[<[H=_-."Sd5zG_^.p̐$5\cVjc{u̎QP2smg\ggwoM5$ .9&K#o9}G9aRX-nGˤ0e,x2^=qpIuVɺa2.c̎oYRhg}<mdo]Z<yŭGRus{׵jUx)-n.XբEt-."Sd#ة'N=.nu f֒"\{Msv5Rgks*׏_M}H[?rƱLfTxc^\?R3~zt&[Sz1"=yŕ ݺiJկ_E)2`pIѴFT"zs}W^+գ%?Y)2υ9ieLsGst<80TD V_<- a{X<'igllq 4yotŅitYC)2` d=\POWzyz.o&"4P 7ɯt?\\ɔIspEkEDB5m^xA~;XEE9 > t -ݩ9WSdpx5DKxsW^ct9W~G3jsmӚrDbآ@Ex瀢{z+{%~LnpKWb[xΣ5jzeWZm\с;gz\C+4(Bv2SQly17) I%?*1˦ H1ϙ8X{˼{\5@1 *s-_ߣ_iJ\]ٸz"نpjyӳ9vU:,ucBm)v5*,42,uIZ49&^ܚ@~Q|'񜧳'.ݺR%tza:`sa23;]A]70vb@}O>>Zjm=j~.bowz|ea_?jr^0*0?zy'S;&%ԡޖ#Wo[e/ZȦvoQuxjp]ٖ FFsL`k ;rMU3@mC<}i1n4<12x9V0t̔veGCO?Li2_QTNsWS[)?DkrK()|#n`:t(v)pMsEk"JmFyB_i2*֥#VVSnWY}KRNZ?~En P;zaxiH аU1Uwz[Bwa&8f.:/C1fԋ[˄soŋtMshՒ;x΋jj+6/^>orJ22K]9fLdNC<ׯÁ6UUwwы7[KTm8daH Fg8])-Ʊ˷m/@|&RugA_GS[)ЅaZtzۦOs 1ϥ;]if'^姏]RstNG<IR*BEkk;?n\>R9f玥w\ǒizV1Vk+ _:kygu,Zwe}{ʞ} sUCDZXSyy4>k7 ߎC\5F]~X۫Q/&:@ij+./+,T's1}Lτsb^ɮl.TPuAU0):в}L 3zN~㹽G͎_-x 4+XTݕ:~>*rRى'B.`xn!uomK4;>YvWn-?i׊Ӯ#1CtAJ ,q-j:$,klu_({_J7%U;ӭe970;>$W+'ӎ]a_͹  ˍأi5ۼz@|B9d􃞿J9vծV`Uwsp+/)Guءiu~@m˶58U9^\t;>=![tDBڱz-s9}7<MYiv}(_7[Ȱ=*ׇCf_Ӿm1ϱzx5rQ}|_S|]e &OW͎9)|tDu)9,+s!l6$'깐@IxnUy2SO?%QrE<71/Q_&g%l`+׷2؊[nrǻv.Qqi ]x#%wk;8~15xJ.op<-bV,Zgv9RڱFvUF4dZ\C7APU7wz;9R[ڵVuUzh"5L=\O&?l`Pep/߄}GSC~vӈ.J;f|rz{iM=\lݾZfу1tilpMn?mU.=l5>Q\=6x.,#OW.iƿ*Pfj'K͎?uY:ލYOTF:ܓUZarI3iUKǟrL fe>qwoFtVj񉪞g~T\Ja ujK48E8]PJpF:\ ^l8~fn `[-Ivk=QrC<ד[h'gGnkvstӖx.*1xMnhq\].=IGuD>z98jM:+hSM3RU-c(<k^_Aj'Ym5S z6>QQq , ۩YUhӘp!_WRV8 %l6쵣W^3[K;Wt]'P?47_+:9kqMޭvqzԣf0>si^ \\}zv1 %½.6GMy< 3>\-?k!&׍#u\IRWzU:7]hЦp.~=IS @<6bQ:?ZGBgiWג#W(-p=ksC-`\ҧ4}nUjW*dmMԶDmMԷ0j࢒L'F%`w+TmLԿR6HGfN_d}|e)#s+]23]ҕr[J5bpQw)SAҜUWCJ 9pE)L'%n0x:EIZQuSY} uum5ʹk%^<*.Ձ-FVn޼2pu/n{"A `P3*ܟb@US!ӡ`v,Osp[mbK G%xj)Uq !xn(6P6Ҏ:?b|5jR͍7f?DadsiHwVEx-{N]~Wo+4w0N7VX*jj][MRN{1..ڊXGx{iu>QK&ݯ4Bt{u=Z[5)Բ֎ gJNwWXޤnW@Q% -t_MR Oo?=x*(tV6R#]]m"p!ԳzVR(7V|M]'_L]P P,sp˨g%asެIiBqˊ9Do)1js J>QazJ=rho‚ԡ6V*1R㎽Pz&(񜷊 Rrx\!jV1.vӛ14Vƒ+I iZ-ߪ+>I5un΍Ժx ͠L<( xoiaIJѶ4оLQrs|e+3_9?>RBPJ!**,_8WxEww ~@[h~~@+-K'2,edP‚~-: ] 10x T,ڇB(P 5 Cqׇ<*82%S!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc|ӧOBH,*6pRn(ͥUN濾׾L3eh1[ jkee;hqZe4-Vm;8U<]6iqx]BE_8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8xp 91sc!C<8ƟJ(r TH5@! fXt,MJNWZ%?KRA sÈMGY^QU+2ńd<%Lv&םZSTr:8W!ZUU#*P?TB sLINRae%);IYM{B m19OP.JγrU۵'TGj\UUըVVXe+>JQԦnh뻒Nt ŕSvUڻU\s*/Eyt%[\];7 *8K۱U-Tl+ŴRTcUUH%|O "/]IkQn2{snum+ǼU}פvyunN ձ"mj8iKOڼ(~k3`iY}DiV-}̤QJMj[kLX^Y?dz]w^*T盵pŴTLKŴRL+\1͵)9J ] `7J>R:^[R 4Я;5u-c.h-n3|}5il@uk>E,مVUSmC=< k.TBMw[ne]u6a.ьJ`ghqz5upRjߞ2Z\|5-kROU쩘ãPwPF3unR{D*O,_ul 5=7S|n%hLnM%T}2+3UZvby gpUCPT#{u5G{otWEVDjͺ PVr\>]S GkdS bu۫tE{eVŵW -+CqP ozN oQSRVS4s~ܤG'kNpx}_/,~49m5 jܯ/+[_B#9U~5yY+Ȇ:oP'“pMS};z.7P_h:Mx kDK%״ uDuCDU-\ET^srfPZ޸z: ֋uY!/kq5sZ <-Rle ZB*LtTIygz;97pQ}}⣼nÃ4j Z!^>B_O5z 9%-"}㩄Kxi5Àu_G?S׷89H?S0oJbO5yI\- ow޿Oun}}5U PWxA{X/(??*ZcQ2-/ʗs!Sa|oiUJ$9nC5=x|}wl5B[f*B{ddR!-us o*tU 񜋪كԴm}QC@W51N$tWUgiS*BFV:@\U 괸 q"C4iA%jJ}nJֵl+KHMk.-!AFb T)jA+jv(STf+9e@DJ` =HuyF^1=4I|((PrpxPEb\ ݚ%[\]͞u{ [B] agh]HE*V]U)cw*}2v+3?MW~|(DD*0Fu^ku6Sd((cim}w%qVC):}h_MWUں)Hz XFS(2J;Pn(רJ{.+E";.J2:Di[S{UW}q"*"(jȇYx8_=}RMA\dP[M'S=GwSV_P]F7wЅFj VZ\5|8pժ饗(P >iŕ*iէY#O?ܱKtx_(-CzHW4$un%w$}퇬y=)zgY덻lfGuz\E룏tT2A% C-sp JcEDG@T{Tkg.uL_nA *i ծ@%,Ҽ!l\o,OUh}I^+Z\͞ڵ{O;R*5Р٪@[罤C(0P< v"o UgJxW{?Q ' 3WkM_T2NEl]<=;S[k#8tIN%C:32Eպ`ZḘK9HuK-&9yjm|Ju/Z,xz>GS U9lߓ 4K+O]-W#Gj`*6 D@HuYxppj4RuoD7Qm+3Qjd: !XL>KP #g讷i~VOOׁdg^Kt^XC-.FSK;0@mPcT5(!s@ƨTU;aZ_Ж1㾖xJh (G% ZYsWnj;EEp5٪QJ 5beέJc r8Y=8*V8͌ʽ,Y.ޞ̗Xw[-I6gSwە$ehT㳰zZlhdQ dsf>l),s9UN 7G7yOLJPz~w=P>D|Sj__qя:bAh4nMeg05kF4+###d*8Oǩ4ߌ)wb{"e^R\ Ӏl:e_^7i4ek+^kv@Jm-.N h{[:rڗh0MD-U/+՛ZBy!N.ym/+™1Z} 9\4cԹR|@o/%ڼE__͎8ZZ;G K'(H'˟(8jM5fQ78n</V9{ZE5R5Yp8{⹒>emt\ "I!׵xpq+lhq7kRUjowr^7X,#ԠZE-:ܬ疪-n8M I"{UY*86@opįNgB$IG<ةbwFy $xM *7*-NyޅfæO,syI&?=ŭ0⮰ O⸲BC5i|h6m@loqӆ铿Z\S#U~6߼ZkSy -tgCcJ=A5-+[KU5C=ʔ@z(7fzY3xDeߖPmmmG+[}TVSco̽ވxQ߉vL_'y|18= 3p[}miqvҩ-s%չxF˖)*Y|M]Nmq^⸸ZeK+ת۵5-B* :"++4f)sEM_'s}M5;)0̷3pV^4?-xD"#{f:9&VL-.7[/iqsV!Nktm1򳵬vyŭ32lAV%p{)R6M_55y\GʧeÞ7L_mU.3t-[Kdx˞`/ 5mkq'.ZuŭV!d04߲.:p-R/#o}I8~\ꆗ7[P-r?..6\n#]-@7ܠ?0ⶨU+-Yr qe=߰z@/ߠZ-J[Y=gCi J3Rhq+9Csl-ն9v\i%ڱJ1sWPm?{VϹ &o-;[\ViijJ۷űzbb;ZլGKOkei[=WC[Й[3ϥ%+/C8d-Vys߼d+[P\Feacx8yfeи)Ws.%bLm.u=e=.nmc֣?ގx2?WoKTaqq+uM >K Zp}U)Z6n}Aqqk+7ްlm|sׯhq4_^uqkx"0\o'֧yŭW9TA…"=2RBb?&b(=2 7EK1GiU-N6{tNpxj}g?g y;@*^]㍌fpmԭť4l X \*W, =iN<o^+rJ0{S '\CF,rKaT- 5:5G($9fԨ XQ)X=zխsp7HرCmhƌ8V*:ڲўzJ롡Te,.B;COъ8VYx(-mm}%o^hdXk9+(4WhU ˈ'?Uj߰79T޼?m.a iqOiSuvP{+9-srݺRF[ >}R՚ٙ[o 5KjR6<親pvpVVnIǯ3q㹬J'@e*")|ΰ'/]ه_2+;_Ǚ*y((ԍhhծp?;WC*\LE^-ЀWoʕ]t-n Q~~)ZΪzueo3/9xrFTӵc5u Jqkh()ucYj$+:f֮=;ehlw\ qy׽b G$l5UnPW b]E9YJ;}m^W)/S(7E˺ eh[٫"*(a.-wb~Xmc IPW:RVWJ^ŹD@ ٴON}y{3ڮykJua#*DPkk6st$U=)=õ[SZJfsnq;??M;Vro(gOi*W\s&0D ;a']M7)%RRl_Is^7ucz[I<ɭ&n<'opu^ܟBRu`7x=?yBMŀ׷8hB[6ZAn]YYEf9/beK]~UNv|qǶf㣟.\Px7pQqɺllUoR8L,O C YWi~H{VFco￧OHߢ99d&~!%j6QG)}K9}b( TeڥC gTpcL) P 8^Jx^PDwtҦ}Z[onRgsպ6+NlhN`$=֎pwb[}Jx0%_MI[5gETokb[VZpF8ܟoC'z XT's{B4Up믫|yF۳GQTVpe8|MuKw" (CNgWz.KKDUrV*p7ϛ==EUjPrs||2%ģ!7k3G|JxnW[9wq IU}?IWNI]^=cf|]fDAF7>to P\{U\/Wk٪ޤCsG%\M|ߪoP(xʕ5aN+$Ϩ_M}ry\p3.z.yv Zy|tlNRauia#}LIRU^`:6ԯ/a<{OQz &iC7AU>)89Jڥ#{Te8B"D($B*_S1]|p'sɿh3J=WI WXEԱ5WE)69uU(OOR{vxd %+7jX0FM.=7*ivu:]v(iRXʨrCUi* UjuF98u|m-cT7j;[/GVPh1WoPtK̡YSc-ԩ;pijejQ7>Z-Nû{EjoL-\tʨUXS\ñ GVeԊ>:egj5j*^cms8vxpW^<}&MRXe:4Zҥ^mNWN'{W^tfwշqx2-𤋮[SUa|H[Zcn x]xɊz^EO2SjNT:TYlx||qkr볹?iU_e7m9ps4kw@(8P&4Hધch[ z4r~f͢P yٚ3Z?NM/ Nݰ#܅O)/y>7|G27H>@<O3WGQJJ԰\`wia-Z~箿&OVpe>~+ızF]߷tqT;P~=e&KxB*~A*s.)JSA`Vm?jQSmum~1@uVVدPr?NQ^H~v}6jܕxp6MJJuu1%\p9HҲF_Pj>Xo, ^ITdnI=Μ>PZ>K+/Ǧοn#ߖT\_ڷQ{tkSĶ)9$~١UukqݳY lGrЦk' J ;X۵4)92s4G].}N7oF\O7SjzHQW̢wu-T܃ xEޮ/M=Oq;C<X`:5KjXnU+=~N`B *3ȉ/H~>@<X#9]7>o˩zSu=hdYLdwu\o޴cȲp/n=Q e8|dl\W^2apFGH]٣%s0zn硫m1\\qSZ} j趋O΍4c%\Ea;x6ʫW7;ʕž,+K͚qL0yuvQ ݆s0ޱMKݤL?0çVV' 5i||,^=O(N VffrL9= fsl VmLŭŭ4t]Ҙb.mHk[)wfvNn] `ڕd|q_baCjgZ3[w9T{ `DAΎ.=bX,iy4.Ҷ=npp=W,s)+]KX!sq+.Msђ[gt `ʾc~,e#4ŎK5CONnp\0gQSX j Te18Wғ͎O<s P'itA<9wjv>Bܴ(gr`TS*"JX)Y=bZ9isx0(,ȎYX@`-9/!b8iF/n%+9(;f cLsmBk `&ss`=\fxnj cjv)3rgwP{.;My Sfط2P28|\{g6+| Ob L)혈xBM.3;~q%̀;1ŶQlTx.;I|P-9+,9/1cnR֚}A{(f)9+1ˮ$* `T)~ td(p[7(GR_ASӋ{{ώL 9X#䚝Dn))s\HP$5){TH7ۄHM{Xve+p];$PGwNԛ{{+ \T-y⒩ >yfced}x|Rl|FOzhMu{5W7ON}3.#piwީѣŷjw18~۶JK+_)$Cu@-y/ioPþV-*{nخI W{Zu+m}]}GutGJoG6荅ҳ]֩EU9I\ZF֯wov1x@]|.U~֪ϵsWz0ޣ)K8_YI 66-MG/D*|p cCeh:^/Vh6?_Q(Kq?G84{J[Q:V}Uk28b=궷oB-Ɓo a (K5? ߲DGKD%jJQqB*ds%D<{r=+kR_Ն=='!$PGE+%Etm˴sv5Jf٦j]ߪ]1;mzumvY( c$ZRZurn~q{ketR 'zoZ;x5F]uyB;\}UW‘U5QjSQmcNVLgWbӳv?L7k7Cy'T*5xF5+KaA cDx$8P-keuOFukwqK+ޓ%|}T=^*nEիԼ\bcG`+\NmT!'+_}zrHD*4R! + TΓk|3Fiط>U}3ƵU\[5}MgkJݨ*<.|ZE/S*{ూN.<%+9]i:v'2诰 +,X VTjSP+nww0 q{o<{(Sgz(I:r r&[s\bRxG)>ʽٙI1!q3&jH5yɱ㫐$JA}%K dHu>e<72HRa=NJxp'=djÔLZs LUʯTw2z~D:2pMyBinMz3&۷(ѱ^]u)cBi!LP~pzfx\G't.9 ,@<SѸںJ^!7Ү)޵ɅZ}A<Ո=sVL Ys0Lg-C<1st*YǴxL-JI{xW[FϭD<B!S{3FsR{ghɕ:oZy̘fdߒ=*dKZR,%EI;_I(J YC5dw~}b>׹ѣes]{u(%Ug%T3,}C Ku=3S.F<D>^C%<@O $)5Nk[:/< CKr]xp/;QZJx35.M%WJ~h#޵TZYi\^#wW|bx-_:VMJ6v~JsIO1Sd;wRgh}N胡9p#fՙ*z 'Dr1",;_JbqMohh]YEީT;SjS/%2íp#Q_{E/WAz9ӽ}j裧܂+:@+fsY~hE6f$h-ڢz=gzދe.;;ӎTZ U -ӴaC۹2"\}TEu/YО1J㪃.k \yգzWB^rj?4:[3՜j_-WD>xL^թ}\X*_G*k>7;T[+qspkj]ڥu][QnS>n'?++1`MPzV5ڪ@)9T>զ&Q,҃ ~tI%s k~IʞUwS _QJ)8Э{}I 7kQ!EڹR;W'UjUͶ*[Wx)ڷAǹt=ekH'rt~|C)5kb![-Y~] uMR~U?F%&8&Ah]Bsy^-~U&*Y]%DUyTii: qZ%pK).MgVJm}\*t3IgV:XWQ{ H'SI5{f@*GT~̯RUJWl =]GNjAmM,*drL Bdwn5!NsN?Տsm*Q-S/.ݩ#;utnS% Y22Y'gUQM¦oR~K<ϟ o`+L ͪ"$cUd%(1Y)JLV֙:g冷$S{Lp\kyQ@9 QPs+0,Wߟ%$)9QWtE)JӺxRO)]:'u.W[sk+)g5WRPNF(0"IiIJMPJR+wXGXG+'([#;psgQS4EQqS_(%'JIKԥ_R*F<ϥxd@_H ?%l!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!ҥ~ ȦgrRlCvRlAo<N_k R08.V瓔?r/OMP1K%H qPx$%\``ѣG`LL UϞ=;U/Z(U YETR k}k q,%]k|VWN&(:׍<ͣ@v-s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5s5Y(>(I)Iq+YC! ͡S/ktj~e͚g??usQVLҥKΝ;444,,‚bcccbbc{رt?OB<:K[O:W/*<t S*YC%˫?SPDxϫyU0BY`e $):At9^u9^t>F;jA:Tj ܼJ*~+W\rŊx\x__w޽{n{UJ5jTJ2eʔ-[`h$..n[f͏?Bm78΅ R*)%p)3;]Z_]:ĕ**ĵڻV{{UO ܚ:e*˕mO~* UX[ͫ*[ʢ%U�4Gޥ4 O+RHv 9cw޽{wI{]dɬYuܹ>Zw:tС[Θ1cΜ9 Lq#smfҠd%S wTG)q[1HD:OvV,;?SRm[m9UܔGhz?n:Թ6TJP9H=D'/jzY)UTիd\Ɣ/_|O??;̟??11J]BGyw޹rrիϚ5kĉfz7:ĝxC੒4Hջ7q)4]ERkJU76[On_^T[-}WAAO/\)ڂhz?=҄J\[0=DP\֎`N i!tദ,;+ĚMfaaa=Oz)S-Cܹz}3&,,s#GW_}uϞ=ֳ??{5jT֬YyEؼA(o G)GZC=O꒛j zr')'~:e_6o2x^_&ӝ\+xo>8x#""e6jԨ?cW{lÆ vc 3f̮]>^y7fC5{r9Eӛ[keL+YgMQ֭[nvŋzqTQmkp1ʕ+?N<9<(# N VzP Me/ziP}bsdWjgbZق Md I=5WZIv-bŊSf˖ͻ4$$믿nԨyo~}jra[w)oޭQk 4syi#4BhO%*_^xj-gCҪU۷ObeՄI&֏dر#F{=z5e;TdMܦm7qm:D)n [u ^qemyEn0z7ڳk]]?@6}ƍsKeW_{ysjՊ:Sw3I9q[LcT|FjO{L3ϴw'J\s3/?v5'OX@+V9rwlzf@VzP{畅 ㏋/KyxwUaJLpK)Iҩֱz΄Ax =ϻkz^gڡ%-s϶m4hEȟ?g}h`0o-l\͛ s:PWuj.Cz~~tQ4!Z yC4gvG+Oĵ-LTGSYF/221ҰaѣG;Yʗ/݅]I !s ˴jJ55|PyQ~i=+J ਗ਼ߡ- \G 3a`sŇc^Yf1Fjܸcu>]t3xξrK7q_ly}Q:"O7<ψfrQG%"p@DDҥKJ) tٳO8wj[oڮ#ݴXvQJLpEGj܍=OSXrTzN+:ceOi|77Cmk+L ?*Qƍ5kF)tҏ>=PBSܹs7٧}uTgS 8ĝR^Aq]@ZF n0"cV]E)weF1J3"M/RQ*<^ݯr/?z7l0]L6͙Gh݊3?RY(ҕ>VcGk4U)z:m/FkدSe)_.ThB|QqG6je;T{te?쳾\1c|'IIIf.A<眽Aj/k 7wĽ:A)ncST*qs_WQE*`^'jmuPi{Y||!}9L SRm0.\`~y>^ |))))]tY~}D(W]:cƌ?0::S]}yw>n͙+VDrIkݻww/\дi_~VaZZڌ3RRR~mYv ^W_fRCk~!qs5\6>CaiPGϓeX^Vrl YepjaM êua<\F4]:ѾbƳ?l;#۪k0= #Gtl7hѢB ӦMd6WZuܹf͚jQF7}͛7@6wy穧r<#s4Ј.Xҕ>I̵ U(u!.+T#T ;\oKu0yInXzk짗bd}5e1;Z\~HRW;q=˟`cHzzϞ=7wN:թSgٲe=ݻ;\ 㹮]>ڸVZmٲ%F2}۷ 㥗 .B+zEP ,Ӳ_p|m_BJJk<\ٞ}F9ݗCw<nX+LKGhx{*qmsΨ\hZΥUMv?~<-9s\,--G; yZ\NpppL3ѣ/L"s[Wu$IyVkRd8;ĭ p\v:q*JdJ;S>\;L5tSh_K!,p֭[{yKܹ^z>ŋ;ӧOO6t/9rȗK8d /ct?_I7.P/ sT>gk gQWMg~1r߽)W?5 UH0ʆ?_SrxlY,p̗_~9bĈz˗:f̘ի[2iҤK[d~[@@@-֭[͛fGo0p nsuN皫kzNhHn|ύN q55}*{waPYWBne7wbt8"Ov-{^OuөKu. %v|]0ڂMZl< gM0gNWݵil|TU@Z%6yJh+j|NZC?P /gfMHҧk:Ac;˨|!^pRG}TjU+lŊF=M6}wWNSNye\}݇uN3Wg%.:s4\4 HA懸5w:54?eUcgXD<z׺*N'-IR6o>^Թu٩ed]п?7Ej ᤤN:߿/ߣG޽{GG۟ߧ}sO>\/_6k,Y4h-È2hR/3@Sbnr2'SeUOa~;=SRRSu/(ֱh/95>tF6n}"IYk6λ3ʽy!aϟoӦͥKԭ[O?u#p?l Xfs>6mk-È2h88W͓n+A dpסnWy3q[K =wIJqҕ~xuu{|~FYt |b?|Vw9z: cAzXc_;w|uKof͚;vp#2~,Z6N:ٲe3wxSV`ٲe.ܟx.32h[9&kH<>'u\l gB&k:i-b({otȁSث&9 N>/l^}R=Jw^n\aE8/22wÇoӦŋ&G2'[ͭ:k/_H"2x.n5ہRO=Ҕft߆jXEU3@G85I!`V.ŁSX%S3YJ)0|/GM':JMS7>r5 K¹ 67>Ϸ’>l„ Wy˘m?\ڵ^d{N._hLRJa]e<__x=}~կqu!.|=W 6%Bkxqo+ vίiWss*K:`>Xmq9o4 Z;0z?E6u]C<)4Zj|Ajչ3x.Lw*]{_fϹ:\B = UWhߣ|b\-c\4Q\ztu{\wn[Cm}'cΣ1CP"g 0~tttѩs:wՖ-[5^r@n }BT/eKkv?1{ Mds+8 qa_0euWsƗ~#窨7>{uP㱺2]j<.KS.ƩDź4YEwo'+gErJK}ۿ BLs5f:ۺucu6UT9\*\Ҕ^ԋ 0\72fϹ=ڳ\~ꗝ} am+^Tq>)K{2xR{izRM4>:hCGF5) G<>3\Tz^*?Fʵfm(K3܅ 2Y-֌ktRF+-'%񜻙)iJ3*V:#!C\zߺ&d)lw3f6V_omV;n_p5~AfIPI=Os*S#MnJ< k~?7ϝ?رcy۶mF'ieTf6}yխҩ۩m:@LlQF6h57#Um*n8W:^څ }4S{ZL|VwRuFx칣 6^/>Yfux. B -**R_x̙3گVsϹ&ZmҦ7snef"C5"=!&mU䦇8_=ECzkֺMN5Kg=*Q5;2Mҭ=BozփWn ^58?iРAppٓ[P ݾ}@J*׉PB-m"FjW*B7O}泫Uz\T^KC\B m"FWz.BjxR|l^Fl낹C V wRڿzD_tV/x.>]3x‹M6F_n]mōsaaay喾Us.VI>ԇU \Ӹ[];gr5ϫ:7~nC>Ps$UXDߺuk\\G*ܡC.O<sFZ mіu{gB_AsW`Sgw+UWe^]2:ĥT=+-\]W0-NkzM-z/g_d}=g ЈPgnq$~?/V;q?~2xPY?էeT;LÖjiNp̞s5\ : .sm"Rypk%ʉ 5 Jj蹍3?Hȭ&ƪ|-2[oh|_V/g* x2+-U,9߿wFXlYfs'OtA }ȂW(d漪W21s`V̵/~ B;)DAs4U7_='W{Ǻۡ#wi#z[=(=EZfv)h5r)^'*OT)zfn5fݪ6O@L~b c0xbŊ?sʕ+3ӂxٳ))N=qDzwzs@Ԡ?L+4A!.:UYǺKS{ZYZOr9K4+¼vvS&肖r9\b3K\fe_|hOMFnuVIIIIx.yIhTUU׼=g}c_k{1~^%5R-zJhx.>pes+J%5fVԓ}qCjK nM@VT~5z &nJEڽͶB<_׵kbΜ9e 5SThWҏ0w-j[WsݰUP !Z*Z\\{7 ϷyC}yͿi>g9{.lJvNͶ969rdݺui!gΜFŋVsrs, n/ʝ[$agw9+=lt̝t.M:\>We3xZ:Bnshs% ^pKҋ/>=K瀛QWi*owum*ZVj|=}bz4ꈮ~m{Uwwh)8ٲe[paBLwv{υ l=j:4]7C<ܔcY:*^c_P'}5g]|jqL<'O}\4I϶έ7g̞Ϙ={v͚5Mrʕz(==݅md$&&msxgopR@ QiLp Q(uܯi<R/:|N'.8WZ~MhMᲰ?';pѣGGEA{ӳRSS\*9MTU:IwvMt]˩P/ݛݣVz;WZ?N<"2ϛUѺ1n*gb< 0`ށ6okY⹌!UY(pctnfbRgdQ9m:TYOUQDHihpK;5OK]٭6=q˪51sv;v>}]ro߾&"'1s=wގ:5T:Iuvt7;$k+SL8~}ή>EMIMCguRg.lFJr)_ W\VBnx^^z~#Oqnf:KKKr k7>>3/ԁ:GsO"wAGNvoYK 7+>++Vo͙Ӊ ۿsVmܹsn p]iJ{Mb!5Uv"а+-SRu6ϟŊEq.]ř"9OFM=:Ε+["EZ(s*a=L7VYOG?zԙ( O9Rs25XlY2e+==_~;w4ڋN,czVs5Q[#A\SVz.y*mQ:e|•d\AnrLxХKqt /̝;t/IIIF7tݼpmkf֛kO~C4::]Kߑ|ݢ I_(݄ӗFpp… ֭Lwg7ne npm5h-ղRgQ^7,^I^/MO;bzN= -̙ӤIg|ǜtD#ִˊZ^zes럌s ~~~fСCvԩcܳnއx).LWRjJk&'ͬλ5x vi~?55+ 3i9xӧۙ?ުU˗{혎fje˖h̞Us_ɏ.k0u׏>N3Kh6,%Mݦ*&O!&Ag(-]r46{L_gΜiҤÇG1z=n7.գٔRW2PxwRKbu&n>}#ϏZ(/pw5wcZLL-viFG][gwdf_Es*q=Na=)rtC?~EGO~AhR[հr Jj w<أs~=O3ơw mڴٴi3plŋy9R^7Egu.”Ut^wv/&GOVTRAEW-rgRyfnS5k9=t߫JLs{skC=&MrhHJСÚ5kl獶oV5_5bRgW(8'/|j=soUōRNKOU߲i鏔uOe0^f#oڴi,[ra"""[E<DM4~թUSd+P#~&7W|=zq5Xޚ]x_T%<&ctaBFb׆y{rsTqwc[;GjR :ҵk?߉!11C-~֦91ʕˣ}ߙ:WTE;upH͉V=F4?Se~҄W-XY+`;Xn˭ڥj{ujo:g$<@_ ڵ[t;ls̞s7s$mo.Y h^]=gIsEϩ/T.?CVs)NS|*XxsΟ|3\|||6m;79w1p I5h! yDPgW 4]=jjOC)l^$OfS7u켵cx []wT.OaY]/ stqΜ9Y81 66e˖+WtONNt钹Mdד'O-W"tXj.zg.$}m.r+,R/T)W5щx1LQ* 5{шtϺwzMי}'[%>ŇGh߾ܹsbbbZhfw+¡C5^`A+'UP!s9s&99-!4MӒep zOFϥzpy} R/Rש/[! IzeJ#okFG\k_55{o7I(.;_۶m:˗6mn:75n4&s>|ϭb%,8S3vL* .̵MA5l:*V/5׸2$-颺0oV꽕 ԲZVsM:_(ET/^cz)o hv͵nzAANL?sLͷo0EDDd˖-!!3*\2x]] P એ&cSէJ= *Nck\8GEmZ*=Tޭջܧ*K-j ˡ||93T7| |ԾSJ{d RJ?u^Ȭ-[~dsGiڴiTN)5pB oN"OKTTM5EyoUdpU5q,*2=_K#N벹^j'=cWv9B+KT\R˖kXI ?5o|wf͚=IΞTpa㹢Emt镈h ~ɏRI8+<3sg5пbyZ?I (;_elVՌ4^!VӦM.\5kVںuk-Ξ=51ϕ*U 1ʔ)c}fe[Cw+}&"rVoJ UiK8y:uT]7~Ri; ZUw} I{jܸE֬YӰaCd~.XNQlYe|7fv=eljT}Ϩߧ_6)_Hs}+) 5lpٲes%Kh"::#*w)s훞wsnxk&m? 1$ 2Vj?DTH,<t~W!!N-3gNޫ4mfqg*U\N|2[.!yj6EGu,Uu.h9Z:J?uHLunի_:[oգGd*x\r΅3eʔ1?3e|ztC5:FcS皨JսQ[c{0ܥB\ɋZ+Յ[.]@_/hnNkPQn3.ǝwfϞtG0aFSVcRfM3{.cszhUPjvA& T@1~v"B9Gguh=Y$$U5t/C5RƛT3\9)CS!ʫW) UReŊr2ӧ4haW޴i۴ic)bz+WB9EZOvDM*ӺKf̫p2'gf5Mîʕ+X"wn9r~;vX\+VRڵ5wyi^kC<2QMw9&MPRnn)]2r~`\HL?o_̰RJy5QTTTzu˗m4~n݌Թ ^oeUZQjd(Ic(nfe׼T.^_BaQ "##g~GJnܸ1&Uzʚ;zUZh}iAW8zO~5j[d0OzZW7Ś-˗_reMwaÆ zɫV2~… d &-Njjm|.J_"B}ԇR\ "RT 5í ЛwLJÖeˮ\@;lҤŋs5#rp׭[oܸ˼2x>a Sp=Edj~xjj}ií)տ^W?QlXQt+W,XtG-jݺu\\/TuѢEϕ+׳>6Əo2KG<wB'>FPzRr*daקg#*tOyt *UjժU 6ѧ~ڹsD)l||_~iCvc>}*T`SSS͛.3fhF~hUR&@[nk rp mRڸOp^ɒ%WZUHozgϞ)))>UO>h!!!'OsͲE3f隬\һt\b[{='9}z^1q]-?)ͫ꺍˴zf1Wz3%J\hѢ;0a}.%и&kJոsdJUCNI%4$pZѢEW\YD ܹsڴiaa3^rrrf{7o<̕+W^x or믿f#7uhU=@܌?@c=YZrPgi0z.k;mdP"@=9ߍB.q"EZdɒUr嘘.׻裏f۷o;w6}FXѣwy3fL2e))Sx/&j.h@(5D;Vg^բtnVmx`Q\L U5AmyE;'kX;;㚲+'.\xʕJ0a3uqǎsνW۵k}s:ͭ_~ڵ9xZMیvUY?pT ?h%(9}:W&G'<[Gj337XD^VJ~=ka|X?K.h<$H%bo{AB]I)\y8`+Wt,U[llܸ}=]tYxm߿o߾_x`e˖-SLٲe5jTF 9x-uU׼K7݀>j0Qn5۔!5+cUZV* E#`*Ujχi] JIU +$H!:gJU|-JUnw/GpLV\ &8]߾}'+h"}RVmNߙeRj5xPgFihͫ5UJR*tQSGҁmסdj]ٸao7\Mw'kJ-lٲS+/_yZj:ٜI&q'6T*Bܒ2*PW:|RQ7j+K.PHBs($PrYKKwZ6X[x].]"X4dȐu918tG}=*sBuxs2Ys>Dԥ%vaʭ'd%R _>۷СC\[)2o)j6@4Wզ̽ 'IKCoiQ*Ç_toa9"cO ^>2jzˏ:\k9$=]?R 79s^SNLLM4EUUWRj.{|< <î5\KR ܩSxߩ^C</^{!۩{V.3lb c|Au׾ST8qaÆZvm۶mSRR&?^unU])5ˢz5Ρ47sI'5M#?SɊB1ؑ#G6lx85kִl266+k>B_+j0!PYf;75T*pxQ^^H%ܤC5lرc~"}V@<o0ESҔf  J j˫p>;j^\q|~[wχ)[rzm߾sOaɒ%Zj:xlӽ<+?`ZKUAjʪ*~_s+;[KO{*7DV*rȈCu]|yjj#ڶmut<[z+A {3":M~+wלTN;4*q;:\2%!!o߾OJJc>~xÆ _yt~:9xx ӽWjF8A/}+=*MȕR4TZE=;S^]v.YZj?Wasló:kO~toU,-;rGU }'j*;X]&k~6mTj_x=pݭ[nӦٳg^#KUdM6m>Gg^EC^P/QɏMEШ*:@OgsRRRMVlwy'--}ٳ RҥKLd\ pxsd WqZ>Қ/!FW8*w?5FŔ}rD_l(sC ֭[HHŃ9tл;}h.]s`5tٕ!=D ?5T冪;T 2WRs}Li>?tn;xPWXb I%Jh֬Y֭[`7} 6$''SpĻjDTW*IJUگ:SG+/)+l PhB۔JzC:xFJRP)"T$ R`+[)(U IOTB+:v^9>guHxC͜9s̙˕+W|˗+W.aaaٳg KOOgFEE۷O8AU=)@UR`*.i~m/9]b 7o޼yt JLLLOހx Q%-]iJar0v zV!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!rX~  qQk2[uƷk=C\E[tB+xx?((*VWR*vӋYm`VZj "kFʀDw@ [Ze{}X7@~!y8?N N,e*j>^O5RLS=^f|E僮ڂȀuM.U*ԣ+ *(rM€!y8?N ϩ}<;G9MpY,::.[$ g29*uOr%^02p:CjЀVZj "h..) [Ze{}X+$"| D/4q#2B^H ̞s,]%JyҒ`?P@6{0QUAd@ unDԦpP*mjQ{|m`k[(G$Ј%XPD/4q#2L E8Ҟktԇ'\`.h`[驲@VZj ">xc7/V|uM.U*ԣ+ Rk64BA`:q~(k&?ٸҘ+q{ fWs =*^{& sLH:d M~eBo(ϳ6\}>i`#TtUWE@9my$1{bD{MsE~@@L3fOkW< y<ʎ;x.-:C}7ؔ_gn6s"pz:9my$DIq'=&~(JݲbbF妎hWS+Ou؀^y(nڄbP.4_:&0=zR;ꉶךbKG\ޟ 5n=&Pp嵗f>eV*?]*$Sb'l@R~!,QIv;<h7ؔ]h_\vj? y xպ_t9my$jV=+[  AlŀFxaX<j## {]lUu2aXMƈ' k9?}M%!r'JkߙfX T@J)A{Mm|s3تez>p)бOLG8{+' ĠܐQ tTQɮx/-5Ė+nlL[1I yɼ|hT: b3)uUa6 r~PJ7yɼتez9?}M%rQI`O {]lUu2aXM&9my$s'o=*^{&Oj}@fX9?7[]LXy=b' Ġ[^j3y, OkW< ڄbP-5Ė{MWS+Ou؀Bo(ךbK=&Sb0$+t** BШt** BШt** BШt** BШt** BШt** BШt( @P* @P* @P* @P* @P* @P* kB 2+2̳̊",Ȭ2+2̳̊",Ȭ2+2̳̊",Ȭ2+2̳̊",Ȭ2+2̳̊",Ȭ2+2̳̊",Ȭ2*G/" @+ @+ @+ @+ @+ @+ #3+>~t Φ[3{urћ*Ě ?:f{gS-Z:h VebMy|3@罳C\f +E2&>ez΁sfo`֎Z3vBX@^a2@9le7PGW-JLI0^fhu2ٛ(u݀PhV$W~`̯@34{:l:FnRS+h+?0CW|-\Qm/9le7z1|>Gգ` am@Z:hwy__W| VebM='G|nx 0W~`̯@34Y6K UGK~Ʞ{gS-@?p罳G*@-EDit=:Fn춘m]uU.z@V)ZTPhV$vnkb7t_]H#AS`ndz!+>~t x}i@` X! #hh罳vR%* u+ua+Ņ;'Kitw8g#cg֎Z3vhr~qUfk=뭊XYZbf_dxJB +E2&9v?~@yy8IbӀWf+8aS_ޡQMKeր+?0CW|7}|hK|hJ44Z{gS- ]؂shh&v@si+Y{! *ӈurћG6MD&#],X]lUu`UG6Y"lJB +E2& PYi qSyd|sfo`!׊5CeB`֎Z3v$ՀY`R r#}XT)Z)4/k@K#k@ZPTUk@ZP W~`̯@34{:l:FnRS+h+?0CW|=Lf h7`T)Z)4!+>~t Φ[3{urћ*Ě ?:f{gS-Z:h VebMy|3@罳C\f +E2&>ez΁sfo`֎Z3vBX@^a2@9le7PGW-JLI0JQ!!!!!!!!!!!!!!!!!!!!!!!! o$'R lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lc\lb+xfdafFfdafFfdafFfdafFfdafFfdafFc 6Ȁ0tl;1 ݷRMFVULd@S:q6PVYn۩mJt+iͦ2 8(+,F7mԶkinQfTMbf#`4ҷ](ji*|N& 1C u-i[em5YD>qeeوm`4J2c" 8ӉPlvKm6FV[MVm1=7k5"4 8ӉPl=y? @0tl+v@X &^l|.Yf(vb6Doo On=h7zD9sn\m`!zH}F ^ ~ʵb$Q -zhZinQf@c2%Pe)> /_ǛJp n! 8Ӊ\sSb.Yf(vb6;j] .t"^%Qxt^.\̐An۩m~MRF4Rmbθ4nzL`4J2c"ǧbq+5RxZ@m"kVO`@Fj/L}-TX>.`I%\.Yf(vb6,mN v*z8@>m`-}Ϧ<K:GM9$sǡG>/4J2c"14IYj%JE*vFsS ɗ ISS d8( b@ 9: sA @ 1Cw\ On=g0{$A\_8%+bgc AnX _s4R:K>A͘Lk9uSo#M+uҌ6Ȁ2M,RxZ@6\bl5=FѨ>qepUu@Xtc%VY4U{prvӦlAnX _s4R=}(fuyK`jB|inQfxߪ4p^O"4 t'ϑ{tʊ&X 8Ӊej`.z ^(6 _LSg+eوL!yHDoSk㙣 ''lD~_^f\"FV[MVm1O`@Yf(vb6AnX#M+uҌ6Ȁ0tl;1 ݷRMFVULd@S:q6PVYn۩mJt+iͦ2 8(+,F7mԶkinQfTMbf#`4ҷ](ji*|N& 1C u-i[em5YD>qeeوm`4J2[3800Ì3 800Ì3 800Ì3 800Ì3 800Ì3 800Ì3 8C2l0c830c830c830c830c830c83mW"l|l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |l |xX_4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4ѹ4єņVHnv ?Ϟk/o >ݨ@=`<^Y"7H"}Qی#>zx 2Do$DvG|X{|@edH= Gn2s z@ڎd c#y '۵=3Nj+$FAOj;qMћcG| i X?5x6Dg"K+$FA>Ƹ5auw]0-jMjMjMjMjMPn*8Oj;q=G<Ͷ =9ɣ"A/P9x@{gX$5(4Ӓ[\~?`Sc1G&x 3EüaMpL N'8[CD>@~?ba>ҥVHL~gx<@ߗeKRt]/9Qڥ>ݨ@7=+P:mHX<=.uym:8E(0Ӣ=)u4mظoH?Hw&/8.×X{|@l)i9gGcTV~L󇦊 T1ԸҒƃŮӘ`  Hnc^Xo0rOj;qT[h_>~Nx2Q<ԛmR@VD/4jY x6S{5 9X#>z/Ŧy0 ]AZڥ|i*\PHn~WU5R7zY9Em6Pw#\`3VH:Q+l@tR@7y:]HC[^Gn2S#2U܀P:mH'Ŝsiy=`\+ĝ5_ ?zOMH8tS`Nj]Kp\ujæ@U;YHn^NutT4+$FA98Z4Ƒ+ڎd_kR6ڤ58G.e G|W߀ ]AX;v6qq#v퇟Ts#Nj&ͳebJ s]s/=.Y"7HDx Z* RQGn2/VԛmR@h:]>Lh2q77aӕ=3ҿR ~h0c8N&yjؾTu\?X5 S0'ϕ?Qۂt |iIptWR^m@edH= :C;7yiJ9ڎd_kR6ڤ-uQ.C(8̎rt=3ҿR ~h0~ODgpOiro79yNjM)yWvGg@I:s:NHY"7HDx՚KP5'+nv Zm@(I$[#rϩDo6j{`8_~)u4q &|9L4 9oyx@[O^);9zCh=ٹ]%y*LzI`a揄+$FA98a.R>ݨ@*ڀP:mH59Kғq/0`=3ҿR ~h0S9gk7Od`x <~&bc@y1rd ٖy:dhP§NfWGn2-+BWpO-Յ^ڿ GH z{gX ;nP:@ukGq8 :+Y1x {~ru G_~ru< vGhﶋ1:- >ݨ@=`<^Y"7H"}Qی#>zx 2Do$DvG|X{|@edH= Gn2s z@ڎd c#y '۵=3Nj+$FAOj;q{gX5W%DFFFFFFFFFFFFFFFFFFFFFFFFFG'g`}g`}g`}g`}g`}g`}}=bUV(eXbUV(eXbUV(eXbUV(eXbUV(eXbUV(eXb 4456P0@ 3%1#$2!"&ppU㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;f:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:-6j+{㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;㣾:;#fp{:60$A" `HD 0$A" `HD 0$A" `HD 0$A" `HD 0$A" `HD 0$A" `HD 0$@; %*{GE(F04Q `hE(F04Q `hE(F04Q `hE(F04Q `hE(F04Q `hE(F04Q `hE(uKS= `F*0Q 0Ta `F*0Q 0Ta `F*0Q 0Ta `F*0Q 0Ta `F*0Q 0Ta `F*0Q 0Ta ^,%f@kH2@ 0d$  `H2@ 0d$  `H2@ 0d$  `H2@ 0d$  `H2@ 0d$  `H2@ 0d$  `H2@RJ-xnټtv%y6%{=B9';wKgWkW-xnټtv%y6%{=B9';wKgWkW-xnټtv%y6%{=B9';wKgWkW-xnټtv%y6%{=B9';wKgWkW-xnټtv%y6%{=B9';wKgWkW-xnټtv%y6%{=B9';wKgWkW-xnټtv%y6%{=B9';wKgWkW-xnټtv%y6%{=B=YP X?<7]lTj\ײ)ldQ_Q*$/b$ą~tv%@x 4j eO/.e`&.$,^fͯ+D'1?f4ƐCs$8[E0BaQGKޞuJ8omXF 77"|ZwbLr'5'v*]{jϔ%Fڽ%DA`y6mYckOԽ\Ρhro{>w{xnپ֖swxЎzVuk$ԏN7mb]_Խ\Ρhޒ(v+:|nzz w7ҼdԓHB+M:3vѮ67n`Ia(jI5.Uv)uټeX3tB3yfk۩GNQ7z-|^Q& 'UMzz w72Xv|l]m,bzgJ&[;l tvy fVM#wKg$M};gXmQICcf""@!^fͯ^{=1ȭ*T2r]n{^u Deu<7]la}xq5}tO4mJ%;wKg$ؕueI"tV7mMI:iLg4i"4^=Rup{:zoE yԣ혶O]UMO䖲9zԣ/3yfΔZK5ʭosEs/wWZ'x+OYfjd<]Xsɔ^W,κ;R2[QXj[b۹a]{jN7mJ_]-ɮEi2e4^=Rup{:zoI uw =6D%d FO2ĕX#d>o;v&ɑΙѺ:'qt~]C-ղ<CVW+.w{xnپ4o7aެlRݻPF6s{Ҽu_ѽ]廝^gPO^vW[ޞu z=:u@C ;wKgO%ܫd7+wY[>u Deu<7]lHCnng#~PC;wKgKWHgI1/lRJT1)23yf]\Ӈzp=8~hZ=86bץ{=B={]nzz w7ҼgJב'n`I%%^S| Wư ޳QWTVVzwk7z-׼]w}}'^06w,?w>8Dd~o;hn{^u Deu<7]lI׺< ;wKgO[bpG./r m]Aq M0y6{/[ש{=B=uZX~kko! w7PvMi?M64`~lMi?M6HK.;wKggRjȶܟE<@u#Iۉ9'&gI.ARO$L97?mrOw%x=Hy'6eIɸk}y+ԁF9/lrNM\t]^>gR4I͸{crn~{J {:Nmp3$Wԍ'sn&^䜛'}HΤiE<@u#Iۉ9'&gI.ARO$L97?mrOw%x=Hy'6eIɸk}y+ԁF9/lrNM\t]^>gR4I͸{crn~{J {:Nmp3$Wԍ'8>m *Ы[Bm *Ы[Bm ʖzmDs$EyGb}G&gF8D*$Q TH"AED*$Q TH %zOwm}_?o  Y2$ϐbZ {:w3m wj¦-m!k~p3עnnOwOE nu[~%#1"I$̯R {:o .Wsn&^*Q< [m/w{w}}7ԩqEqxIȆ` zwԍ']~m鲋uO|$Ȟb)-gE:?GJ,97?mz*_FiFgT㓔 Jm+Y ۟0 C1AVF=;ԁF阙:5S*8("h(!) )Q͸{cӼ*K.2K-_}W&gFd'Kk!(k!(WlSzXX,}}'d:Q`YS`I]nm,wԍ'93Juy_ۉ=#fe*vrC I{NM^szOnC}}'sv(aEw~K@u#I?J=6eJ{D|br' 5(C&Jjҥ-E>u2TUfK*ROzA[976eH?O!|b?ozNM^_Zw=OOx'umg Q0<@+H-Y Q')zwԍ'ڼ!l]~q2Ǥ//6>m,>p3׬WF*\eϽw7ܸ:AS#HMd'Ks#ઝ,-J7ߦWԍ'ڽe`! jL^A.Պ {;aT' ^ |=wpE.BqY՝m+ԁF,^ '"[ٸem&WZm=76eJn7M"$䔄$JJvYY97?mzp#̙4ф2zwlIs#1!NB^ZVܛKzWԍ'~Am]01q.O" 9 Bh٣Ӻ0u->RRLO(̕ԙQ\1J[:Fѭgrn~o!_uG}}+OQ g'$mazMs&ƬeCgx=H}' QDgrG6eMمBl&MO ], (H٩Mta#{w%a<#)Ƽ0q3ۘ9_N {:W)JDTZaBsn&^,Ҏ T'ZʋЈKW`I^o^SD朖yV{qYR {:QC p[* ^"m˾wZ%]`+t۶nDzmOIR4ٮ0P.nZ`K\E[~i)W}}H8TYHg1`Zrl W@ܩF0Q"D[ԁF9/lrNM\t]^>gR4I͸{crn~{J {:Nmp3$Wԍ'sn&^䜛'}HΤiE<@u#Iۉ9'&gI.ARO$L97?mrOw%x=Hy'6eIɸk}y+ԁF9/lrNM\t]^>gR4I͸{crn~{J {:Nmp3$Wԍ'sn&^䜛'}HΤiE<@u#Iۉ9'&gI.ARO$L97?mrOw%x7mL3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3T!S8L3M2| %TR8H#TR8H#TR8H#TR8H#TR8H#TR8H#TR8H#TR8H#TR8H#TR8H#TR8H#TR8H#TR8ٳ'LZUB*P xT+¡^ WB*P xT+¡^ WB*P xT+¡^ WB*P xT+¡^ WB*P xT+¡^ WB*P xT+¡^ WB*`ɃsK }pc\>1 }pc\>1 }pc\>1 }pc\>1 }pc\>1 }pc\>1 }p:l@te a*YR@,)dK YR@,)dK YR@,)dK YR@,)dK YR@,)dK YR@,)dK YR^T ())!L eS(BBP2)!L eS(BBP2)!L eS(BBP2)!L eS(BBP2)!L eS(BBP2)!L eS(BBP2)!LYR)"%JQE:)QNuSE(QE:)QNuSE(QE:)QNuSE(QE:)QNuSE(QE:)QNuSE(QE:)QNuSE(QE:%Vn%O0q#`G80q#`G80q#`G80q#`G80q#`G80q#`G8䊒4gSIwZl\֮y'Vҹ'pΧiй%]NqrO=N%irKZwEZOJ9{:KgB䖵v:ƞмE WN0?+m^I')Z(Zۺ/Պh` QĆՆW:ƞ 4"!!O*LNd (N +:d̑xvQC06DFdgBBҼ_GqDjgmֻZۺ/Lfe>2fOEoJZOJH8/AiM:$HVWY$cgHm2K=gVҼM*&X/i8Dujي*Z&ӳz̗ f%Z^fuСh*ZQ2_=nLu΅s;3LM[Dua~Lm eX imK7بyvm7lΖd6]I[ۥlr%:gVҼmv܃C 0Cl _e4eWLa4ͧu^Ysu;O\1}YҬ+gVKzkgB19"&'7K[/VG(}Yek/ڬF%xֵvBX$ԹN'ϒZRjdC0åHZ^0gVҼh Sʛ.|<Ēgܿ&x9{:.H$.ouN'5iy#!YQ*@~溰?V >iVlCj](KiZhdf>Qw ERe)Snlծ4#MLSU&!apu~M/:~֮}?x9.lծ4A~H(SL/$:W9WpΧiI]B2g䄋=h RV MwZl^?Շ?aՉoXVjc_GZOJ_W_ˆadBȗrwlsئd͓s&ĒR;򉅖'J2?{]֛:a~?Ճ?ʟV,WkWn较'%:ƞ0ܿ$K'mCciEZюr?6t/Awh[Ed63_#Zۺ/>'%:ƞ0ܿK'pKFEmYc.TQ*5oQwZl^?ՇN[l6*@ޭa"["֮}?x9.lծ4A~(V Y4IeȖrLUxv\(h&> VUK2t4(н#|.-l'v~L9?7yv1u{g|έqz //IKq=K:>S[L$鼒}+Ou|$ҙNIOQGy% V,(NIo2J?YQgJe9%=EM3Z$9%ΔrJz>(gҴIgQGrKx=)}7Pϥi΢{:S))(o$J%E)-tSSQIC>K:>S[L$鼒}+Ou|$ҙNIOQGy% V,(NIo2J?YQgJe9%=EM3Z$9%ΔrJz>(gҴIgQGrKx=){s0;s(bߜ0VL#gJz*z>A/n`vl`E"tyT3Z Y_vGldvGldvGldvGldvGld*΢a/>;;SC4#6_pZu&\JG[LAyƧ7Z2S~mSW="Iy-H,1'J%*?*9^>fc^{kʡJ) KQ ՟YQR0 PtL#j.toeCg5"gJe&[ 'y*}+O"\2 #y_U<:>SʈiOoHަSmLq,ZA9 m"TL@0A, oȷҙO#ul 0YT ERc2l@Y6$ zY1ȧF2r"IY?+|,&s^U Vm4 AKNx R^U VeK*K`QC)nk+o;+8?^3pzk:>SgUˇQC).knݚ?xV/Mo26k#誰Iᵳ ;?NmˑsSQ=jA-D'9ٗkg=oQ30=jM=綼?qM@=&tEHt[n:cYQ6kt#!.oS)ga5kQG5Δy,FKU( k?u{k2皚ozN(8z>c "%Y`ɑvx͎]W6iޯ=PϥiF;A vVy_괆 &Ҭ2D(:>Su᳖SJ\l#2–Q,NHd]Yl ]r6l-tS*t"(:c"`]&҄ KJs"VʧK-gϜfoK[~Hs) QmCcvյ ?ί=PϥiSoe~7m&BI>O#(O)FM2~ GsaEYmf3ȜZwo2'o{k6΃U{&֔U)(owo[~6Ym,E! {myT3Zv9 ~TkR ",(OB]oS)RR,Do2'o{k6΃&B}vŦ=U=EM.Mxň[Vds^^U V]ge~,ZIt-+ %eM3ER<:>Sк[|.?T J(oI+Ht-JF [L^%M=; % /d P"zζdV;v&L)(owo[~i L,ؽj҇F綼?л[\(v OCPZ^M&X%*xu|uy]O1$CH#\lKoLgJe=NpGfb&(e\WX"Ÿ=QzPϥizvWXҏB/ru|uy]\_:rezrҮ^tSdm*drfIY)SQB4޷J<}+O.3VVxAYQަSv3w˷3ύo2'o{kH lr]^6./q}GVx3Zv9DM?ܾ5E)]^ke> :%JZeg~>5Δz UN@hdՇU|jz>]ƛ& blV-XblV-]҂jwSg /X`(gҴBs=o;+L+ kW"&ʛ&?M=pݖYe"!T9(u|uyCPD WH8]Ą޷v{:S)2vWbqN΃y`WDR'1ҊE 0W"I^E=EM.Mxuy*}+O.3 &zW|0'ݏb5A*ju"\rY5ȳ= uL? N(]J뢶8.zؐn1"oY -yN%gJe=N ]:t򜋑)(owo[|m{myT3Zv97uT|:>Sк[|dOZs]!ŸG<2y{:S)2vWbLj7Ia=&Z~U=EM.Mxuy*}+O.3e[geƖ-}y?ʳ= uL1LA۽9Jd~EΔz jrJ_a~F%9 &G*"O5v"/D BŴkDKȰe,|+dٓ)(otB)~DQtIdTbب* p=R<?g87~B tpI+͎ rpnM8PaM ʱIxGs",(OA"ܝA~@֞a"𖓡%y(,mͲ@p~ش[Z̷ҙNIOQGy% V,(NIo2J?YQgJe9%=EM3Z$9%ΔrJz>(gҴIgQGrKx=)}7Pϥi΢{:S))(o$J%E)-tSSQIC>K:>S[L$鼒}+Ou|$ҙNIOQGy% V,(NIo2J?YQgJe9%=EM3Z$9%ΔrJz>(gҴIgQGrKx=)}7Pϥi΢{:S))(o$J%E)-tSSQIC>K:>S[Ȭܷ9R«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<*«<'Έ˦ȩNNNNNNNNNNNNNNNNNNNNNNNNN6K-&6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*b6*`ɈN,8ľľľľľľľľľľľľľľľľľľľľľľľľl̃F7;e.aL# a0&0FaL# a0&0FaL# a0&0FaL# a0&0FaL# a0&0FaL# a0&0FaL"l2Q+&K|#a>0F|#a>0F|#a>0F|#a>0F|#a>0F|#a>0F|"8-dpvX;Ki`,vX;Ki`,vX;Ki`,vX;Ki`,vX;Ki`,vX;Ki`,v2>TvOSW$cOgd\\%?rF>TvOSW$cOgd\\%?rF>TvOSW$cOgd\\%?rF>TvOSW$cOgd\\%?rF>TvOSW$cOgd\\%?rF>TvOSW$cOgd\\%?rF>TvOSW$cOgd\\%?rF>TvOSW$cOgd\\\bÎ` 6RB#8hXq&!Im>@SFu^Xe%e2iJ[ 0FyKS3?WbF0t$^%(v0!! `CB0!! "H\λEك#^R>Rd15M5KlΑT-X\Z|W̻ko!# ^D)7nD)M t: "uܝ1J2v{~h rɇu fI&M+Yu;j 0H+n᪹cVZ쟫̜uTQI7W[{9\c'GuorySV{d&df<W#.졒,,|/re 6Β(YƜ۝1LglI[j%!n._ dsE<糲~+q0NoFy/^|fsrT4'E5#{!&lXɹ^ ?#%X8amA\Ж+4VA¿mFLRe|WׅyUspRKx .|==yL#+@B #u|$r~ۈ~ۈ~ۈ~ۈ~ۈ~ۈ~ۈY{Qix/te-YUlYI$J|wVCeDDIt>Qi.FSI␞l'>#a2m6%BLebLr%tAzqHa5k֛AA/ B|X:.[z= ̽y F:eM,("t,NMw:dz~F0bAI n"o#uy.]yk=7`_ʻB rɗ(AW | d&zْٓ:-Ed(ItȻ+ *[a%G'[|.zi(}H2m%}#P4scYm[v=oH%Jj un/-Qz[m__膮e T'tP%%H wvz(I*xrM[^F". 䳍y wr&4h)Gsuo!-&l&z$%YR/"YROj'>p1C"8-9eC{5* QLp.qF+e6Hp}Ys&JtE /βk3b S_ P7~S?W)М.QpggyEzpd)\87;I<]:fdregkzT<W잟ST"M<\z.KsqYuhm b)䷌ ZJ\8]y#?\7|~"zf>cY5Fݺ.q)\'+(,tV{ĉ)rnbM( r$|%$Kb?y,mV^V]3:˭&G CX[CR5u~b_\=oo+=zozF7R"\X%}VT#3g0W/]uobsWk98-m}^g:.Ku7_U&hȲmvʕ.Dox}ayҨM 7e:\}ߔ_|.J/oS?WvWn"zޚEu[ig7h>1C pȉ8g?z5|/S3FSds`jψ *ee>3oQj5:޳’zvor{S?WvWn Z|Ud+$Jg0W/]uo)5lB Uwg4X잮Fs1$G\s7t1C?'KowJJ<}"OxoM&L3GK[rP} ڽo#uxnoEgOEu[Bz&L&[LmW |Q䇫ճ&-G\sM :݁ʊ+KKv^R2:ѯs}tlM=od6$1<NoW_ɮ7?)쟫л}7Wy $JHMmCc^ſ9\tZG[վL ~eM"cR'u4R9tй[|-&t yAdxϰꢒBRWIM1SNe24H ǒ߆tg|nl{忽"7[3Ogd^^^(u%)3+.H{\0]ݽoWg?z5|/!bl"@EU[Co98K$D[üRq2/!1flΎU_ M1X}=zozF QG ,\tZG[վG6Qz%V0[ߝ}^g:.K SL+,{k91*c IuKUZju&MFzkv\J)LlϔvO]ywWRňhvmsriowV]*2z[$\ĖX$x("*l1 HU9tй[|PQLݛ!gcd&\bCket8K6Lpǐ Dڙ3HUDH~jQY?K5Gf,u t2ᰪWq5ΜsbnkU# =zozFpD)z&sriowV'0yXw9R`}3?z5|/$bNAJ}MO$e*GVAJr8U<'ygd^%Hoh-dLEWZOW%rBBNY+4^2RșioeBH'x3bB9`*`y@Ӊ*+ 䯪Wvv,%?d7zpTTȡ[),6jFQ$XyUmm|l5B(ο,AE?h X~b,AE?h v^=IKWRD4}2b٦ 'YRce>ūD+cڸ#9~J#}쟫3+>Hʞ#9~J#}쟫3+>Hʞ#9~J#}쟫3+>Hʞ#9~J#}쟫3+>Hʞ#9~J#}쟫3+>Hʞ#9~J#}쟫3+>Hʞ#9~J#}쟫3+>Hʞ#9~J#}*a1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1i1id˃1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 881q1;wcwq1;wcwq1;wcwq1;wcwq1;wcwq1;wcwq1\n,?V?VU  14Pqrs!0235@ABQa"#Rtb$CSc%TdDp?sU*i)hxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxYYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYwYw土ŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxŐxċyjCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCH"rEg \ Rhͤ8$0|&_DËHq|>I/"a$L8$0|&_DËHq|>I/"a$L8$0|&_DËHq|>I/"a$L8$0|&_DËHq|>I/"a$L8$0|&_DËHq|>I/"a$L8$0|&_DËHq|>I/"a$L8$04BSˋRU)b&a`C0!IX$,|f>I3 $L&a`C0!IX$,|f>I3 $L&a`C0!IX$,|f>I3 $L&a`C0!IX$,|f>I3 $L&a`C0!IX$,|f>I3 $L&a`C0!IX$,|f>I3 $L&a`C0BSνˋRU)bc$I3 f$,2IX d0&a`1Lc$I3 f$,2IX d0&a`1Lc$I3 f$,2IX d0&a`1Lc$I3 f$,2IX d0&a`1Lc$I3 f$,2IX d0&a`1Lc$I3 f$,2IX d0&a`1Lc$I3aKH"jD. #D6Ԃ3 4,2iX,d0Xɦa`Lc&M3 f 4,2iX,d0Xɦa`Lc&M3 f 4,2iX,d0Xɦa`Lc&M3 f 4,2iX,d0Xɦa`Lc&M3 f 4,2iX,d0Xɦa`Lc&M3 f 4,2iX,d0Xɦa`Lc&M3 f 4,2iA2F!$lDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvK|_N(Is$dPf>Ȓ6\{[f(^ yZ#2H4̿I BME֧$([eu=CВ2IX^풝?N߀*-?8r_ڞ1C/H) Ms=%u3؈+8։re']f,- zN_SB|{[f(^ 􏑜3RD\\£%E[hMs9 _,>>KE32􏑳vZY{˹3\+O7]eAuSIIQsHm#W)a_%L-5f#X"0(\*l~~&.F ~ڪ=c1Bg|=+qJsmj>lU2' 51M!c܇_M'RD*" ̋zx%P>F )[x#2#~e1ݚZE B_RqS9''NB|[R tT/y$uƿ׫;D_2m>αcNʦQoX,{Eg?.HC)bc)ݮUЬ!'=#nBE eҥRN*_ei4#~J}{wY qֿ_x/r+IHE:$_i\Ҩ2N{Tw$xzG?ܠ,lK]m\MRCww_%ȴDz=ȭPHl)*UU ^euҢ&SMI]>y+#5\ֳ)30~Dc-_Z,="h#EQ,'BY em쥦kZB"33RQۮLY(VOяk{L GheE3NI(H8zz6WQv R%));6w%_ڞ1C/H)%JEUPϞ"Z#&B{9$uƿ׫WJW̹.х5 HdQoX,{Eg?.FI- ) B ?F~a5}+$QFD88XʢaD=P+f_9v$UD\W30>IE32rߩf %JX:ż(WgG^Lss洏Yg9w]QF|{ C&הIIu[[gQNYg)QoX,{Eg?.H e˨˴ (3w/j\W96((VOяk{L Vi-~TsWSf(e)5(" nҎ*lLUhrx\]m5<]>`M*"JH$uƿ׫UJgXA Ϛ{=k#SSGνD/Z% #X"0(\H(VOяk{L [FG*XOo:$)J9LιиB \-)A!7 Qj{L =#,D D^Mw&*W'0-،"Y ħ|,PT&G^KW.dk=3^{WL%HB$UB'-TQoX,{Eg?.QEpeU+z'ǵbHKJ=^jv̒Q8󗴔!<*IFGjuf_ڞ1C/H4^*9Ŵ:A,DhpIxRc rI[gXr&>zrECk)Tn8f(뙞!I *f[oy]k :->Kb#;Z%( aD=P(rQ<-3.2Bg>sMQ}[Qj{L =#xg4C"V»hc Ϟ]-w?Kq|k}D#Ru./lYORg>}IÑQlGu[} FYvKQ\iE z1oix3>NL[gGs5]gۡ1eOgY0 *T5P>L'Qsߗ~%~%JI8ܾ$d ԲQI8O=DUL u >9$uƿ׫'n ˯eeߞte$"JH}QEG`qDaPgkDETP!'=#Q 4E)R&~j"F A7L mʕ)N'xzGA6<9$3]*Z$p%[\^V#>zr'cM3mwuTYfueP0*TZT|ĆĿ,HlKBĆĿeK,sD~ M,R^F]bĆĿ!TL;(myM|ϳ`>؈+8։r+-"XC>OF=3/zG]L4Rm&5+fE)4PB+)$!"R[DD\Sf(e+e{հ'SC.Gq|IfVGb#;Z%( aD=P$v>=iJU(;P,kWnjnH]L;UsܮIIHEh=b^#Ѷۭ 迵=b^:'-ا.C f!%vzXXe\%< &MkKjRE9eZR\.j?1(pc2럸Hm#W*(,="h(2*=c1Bg|[gм|Sf(e#xB-Αpw A4y6YM*KG\ki=zT}QEG`qDaPgkDETP!'=#t_=օt_ڞ1C/He8wL^KQms48Fr:C4UdWY*2sA@4Ld\ki=zT}QEG`qDaPgkDDE RAHDJF,*3bʌБKvI-H4֓nD'=#qAr%8J$9NO0Y1Lg}œo1dwY1Lg}œo1dwY1Lg}œo1dw1BMJjRk{䓑j{L =#QI)CZ/K E Ծ$C0wiPډ/I .{ֳP%DX됱&At3p"'>zrcə$bȊ "+,?@oq?KMLy,HE`qDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V FYvJяk{L :/Oiv:_HlqDaPgkDPxzGl5V 2U8KMQh!<8/,E8㈼q\"˜q^Ys8.qye8/,E8㈼q\"˜q^Ys8.qye8/,E8㈼q\"˜q^Ys8.qye8/,E8㈼q\"˜q^Ys8.qye8/,E8㈼q\"˜q^Ys8.qye8/,E8㈼q\"˜q^Ys8.qye8/,E8\DB\3RϜa(C-#z:I{o\˜qVYs7.qYe8+,e8xo\˜qVYs7.qYe8+,e8xo\˜qVYs7.qYe8+,e8xo\˜qVYs7.qYe8+,e8xo\˜qVYs7.qYe8+,e8xo\˜qVYs7.qYe8+,e8xo\˜-n8ᚔf| DbiԥQYe8+,Ee8hm\㍢˜qVYs6.qYe8+,Ee8hm\㍢˜qVYs6.qYe8+,Ee8hm\㍢˜qVYs6.qYe8+,Ee8hm\㍢˜qVYs6.qYe8+,Ee8hm\㍢˜qVYs6.qYe8+,Ee8hm\㍢˜qVYs6.qYe8+,¢c]St9;LyסۖZT,RDU8HiT"rqNUS4ʪqƑ9UN8'*DU8HiT"rqNUS4ʪqƑ9UN8'*DU8HiT"rqNUS4ʪqƑ9UN8'*DU8HiT"rqNUS4ʪqƑ9UN8'*DU8HiT"rqNUS4ʪqƑ9UN8'*DU8HiT"rqNUS4ʪqƑ9UN8'*DU8Hr%I)QBDaB3U̮/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88cTэS/F5N88M#m>R]DJqQPiqtʔϴXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjXƩŀjGC0FyĤ!"'lϴX(sq`,c9łg8Q 1F3X(sq`,c9łg8Q 1F3X(sq`,c9łg8Q 1F3X(sq`,c9łg8Q 1F3X(sq`,c9łg8Q 1F3X(sq`,c9łg8Q 1F3X(sq ߓi$RF? NRg8qN3XIs 8qa',$9ńg8qN3XIs 8qa',$9ńg8qN3XIs 8qa',$9ńg8qN3XIs 8qa',$9ńg8qN3XIs 8qa',$9ńg8qN3XIs 8qa',$9ńg8@4LMd]rDaA`[+gD#vΉ{CF o98lp_vFYtO]1xzGl藴;b󝳋ށVlDaA`[+gD#vΉ{CF o98lp_vFYtO]1xzGl藴;b󝳋ށVlDaA`[+gD#vΉ{CF o98lp_vFYtO]1xzGl藴;b󝳋ށVlDaA`[+gD#vΉ{CF o98lp_vFYtO]1xzGl藴;b󝳋ށVlDaA`[+gD#vΉ{CF o98lp_vFYtO]1xzGl藴;b󝳋ށVlDaA`[+gD#vΉ{CF o98lp_vFYtO]1xzGl藴;b󝳋ށVlDaA`[+gD#vΉ{CF o98lp_vFYtO]1xzGl藴;b󝳋ށVlDaA`[+gD#vΉ{CF o98lp_vFY .EJ1dxqLw1dxqLw1 UDA#}Hr/%~ >SlSl(ğP C긂C-Jj&G2JR(3Ih;b"7tIJf|8ŌGH2bQ#X$qD1c(,e8ŌGH2bQ#X$yhGR~Rqw!WbcJK)-"M/Q<͇|Cm)Cj(>ư%:NR/b# މrH-ʈ߷-t@^iOh)Ƽ\r6^p2r(l y>3M/hwH >EsDo\.z>jrScy<<}WyL򜀡J_Ii/q푕c䰸/b# މrJ$wv*#~cO]1xzGW&u KUU}6C"K1xyϑF`\=W#ށ\*?Q3ZUٙ$ۦjp~b qJR>䐸/b# މrJ GQ%~ >HQ!U" tih>:'ڜWRJS=&8 ƙĪ{MPĴ oe.KD#7fݘKvNUoGq5z.Iv6 [)Sµ#__|[d}f I)/PF *Ur:r[B툌", z%wQ=8Ӵ⎘JDF|Xs ~`Q,9G?Pq- Njd3O]1xzG@!Ίu EY%7L%^H<3iiI) £o/hwH >D)P˰Ņ _ֿ,/X_ZPa}kBA"I/(s)=^&B"qw!WR]*q)_a_oQB )"-Qf#o#T:3$}EgXK洞"eCd(b􏑻lw?ov YS]kR\PHmϴy]t@slfiHޖ*(ĝ*/9w!Wh?(:GTGy4jQ$}EgXK=XwtN%R(bF,lT&-e}!tK_Q/hwH >Tc#l~kj=`Y߼O BR6I%p$]|q0\ȮJQ ^cN`Q- )H˜a+HnMy=TEe6$}EgXK*NQEFlT:2$Ec̀t"ܥ=d$8Q:_ZԼdO]1xzG᠊;Rsl -(/e $'9N8uE8ZO¯=,3 J-f 4FlZ/CK+\5t@sMPf{rT=F]-wO|x,]|g9n/)顎giJU%å2 A +)JVٙ=ࡊUxP0ƊKg䐸/b# މr8Ͳq:!L/;X`h+]-*Qrj'.=#qJ"ـA.FAT*9>قuFlJ"2k)'!$do1'?g%^|O:tAyԒXReL]!֣b7CW$r-4͹!*fsE>PMӨi>!툌", z%?}gsG~gzrZ'.=#qx{?a2-)5gLmZ\lT8Kk̯$^|{Gc+|u?z("{?&ށ\Pm$=xVa*hkɜm׽ɗ0alI=I8RNR>7R^~ID#|uI&?9Gsǧg[X9+WS)sM< iG=J%<^I|S=z[YHޑwU.Mq5z"yD 3: AiIW:RS: {ޞ'K "0 -$6I- 2=0nCRۖ\RlO W%BK\,R Z9:y:*(b􏑝GM4:Fue}$-8DNKU14R!ilْ[޺Y\QK藴;b([mE4*YYJ^ˉ'ށ\ԣ,){_UGZ7WLS~)?d.K6X=A? rQF*T/QH\ݱVqoD\5i2:PI#+,ǫQ?iwH#y^5Me N*ĦNwSB I藴;b(Z[9YioAǸ^0 .z>jr42r9)WQ' E  m{ :?+Ї΋ ,n/rH\ݱVqoDY$K(bP'J/}Ks6?藴;b'z%'#T!z?zO9 eBEE7CW#yʖ -s=J95@W9)SLGApWzsl[FB3gw툌", z%(aAI¡胬FE?$~ >G-үyyO!} LtBTҕy =C vo/hwH >NP ξ*їK}۹>Qq5zy=e`)yv􊪇1yR+L. ؈+8\.b!4'$uy8e=1)V8)}Tt}DA#kJyUMuJrJ'.=#iAyJVPyD,i•*.rSm/F OH\{CF o9g]֣L}3%+0;RI~Qq5zrW{o^]lRRA:Er2`陆*D19@{o툌", z%h'bH.Wn&hҏKڮ(qP CJLRuO8}%P)Jʥ)1a$gO]1xzGip*.0v?YX.5Wm{h?tU֗$A~PՋ*&-uRSv1F?qgK1xyϓ5A?5=㭈9E/2M.Sq5z4I%>E_'fKVUE/KuORtP+ =TP +,IWEp_vFYtB4RR!D ד%/|z-t@^Jf1ϯ&48(*jW2SaDҶHS%t@sG?e&|q߳!! rJw/_nSq5z/mSoOQV088HAJgX~*(k|"}Y$. ؈+8\.8ŠE+yW|aNE7NLdiLo1Q{?&~ >KOFm/]~2m+=Y~Mޕγ ߬>KD#7+4zG.tK]CkYJ/Uޖ>Uq5z%+c8Urgy>b`Q{?%}EgXKT:`o,L/KD=#Y)n, 2U8dWA~69kJ qŗRxbk%%!'~ >Lp%QJ t9+ei?6 WDΆJ.F?lir {-rmRU5t@sK!sGqquRt IOEo@^JiYJG`Ԗ X~v-4%(Kkʼn/DmP%ʳ)Qjjqm- R&}EgXJ?iwH^v.z>j[8\ݱVqoDt@^:%j[8\ݱVqoDt@^:%j[8\ݱVqoDt@^:%j[8\ݱVqoDt@^:%j[8\ݱVqoDt@^:%j[8\ݱVqoDt@^:%j[8\ݱVqoDt@^:%j[8\ݰ򌤕j BRVcgTrqNUS3ʪqq9UN8'*U88gTrqNUS3ʪqq9UN8'*U88gTrqNUS3ʪqq9UN8'*U88gTrqNUS3ʪqq9UN8'*U88gTrqNUS3ʪqq9UN8'*U88gTrqNUS3ʪqq9UN8'*U88gTr)kqg*{ffa,CG"OS,T>"OS,T>"OS,T>"OS,T>"OS,T>"OS,T>"OS,T>"OS,T>"OS,T>"OS,T>"OS,T>"OSb4BHyXIk2X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|Ec'X2|CHoɒ)؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱyb#; 搄v7 ]4H|jl^k؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱyb#; 搄v7 ]4H|jl^k؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱyb#; 搄v7 ]4H|jl^k؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱyb#ܞH>QJ$8%G^4 lϪCbRg4%R8D"q1D;$HYUm)g7Rb~<7 _"fWCV!$hA]!MUt=(<Y-Lm&9!/u 5RaPt(QYNDNU\DBQ~O>٪K'E~#:sw/8SDʕHARl+r$ ubS/bQ׻|$>5rQREtwIV"لU]$tWWD%uz,+)h)ISKQJ  Tziv9MGٶ$YH}`VAƺe]G[ny-&GY"p:U |g[0T>^hջyT()Y*gUpUKjHG1dtseaQϋub^FY. TQpꮳpooqHIi>c)B(wˢ[LөZC#=RNC Y̤]Wg4%U #D5UpC>>5/AO4 b-F-u=kΆt:hjYSWcp!ou랊;HUZbmb/IMb*|#!u'P:ACB URhLj4ћdRޢu& v]qWVb`h귫-QmBpJCa]qn^k؈+8#7"v 4V"mZo|% °Tl*цKoij=U4=Ge=\; 搄{. FYGs,Z״DB*}E$DNUKG\YEwTI%") ݲ"a!-xMe- 7 ^Qj%G/XRRl+h.i:oX/ [U7D̞Bz%wO RjgTꨋ/̝7 _!MD-8&|#!ާ]D%O.I cRRlҡÑYQs潈"\3r6I9[Q9joZKǒGaϐD5T൝CcMLꨌ{ny-Cz[nqMU={GCW!NKn~:7]m!̦? 4uuuݘױVq FnFwLۅ!4,Q5d6Nۋ)+lقGRx^IsHB^>CQS z7QT')]kNm7 _!MD-8&|#!ާ]D%*Dy'~ VPC8vf潈"\3rECE muB+ie$S*h1IHMj>b+=N  ,f}f"S[-)R)uH3BY|&'o5Dc}JA㖥zg3u~H2[|*50!zxDƮCzu|~ki߯¦ :P2.~ ^k؈+8#7&q$1> iP,Č6rȤoh"㌫6^#iKgaz"pP~deQǮ7tK2J|%bZW^q {^[FUD>^hbS[km;N T'ǴiDC#5_9 "0!pHʅÜrQH)|IsHB^>CQSW/nGp5m⿊V:xo)[ny-Cz[nqMU={GCW!NKU'~dL,*iPf}gn8tNٙ:Xb|{Eg$f2\ U;Jo{D1JzՋ|; 搄|' Cյsd u+J;Wz!蒩q;{ny-Cz[nqMU={GCW!NKUyv[$aaS"SϬT$!ffTBOyb#ܑhEHԙRer qS0AB{V"^'OPM2r9{o/pzܖ; 搄|' (~G5G!!1h/-*礐@i2Ww\n۾b/J^q {^[FUD>^hbSU% XΣF"$YqbkXZ/ֹŋqbkXZ bRvu潈"\3rH3[!LD GJާ)4v! { >]DNUR)PHaP]Jd)oi5֮0R!ane Uj!oa5TC\.:!/RZI\N}ы{Eg$fxg4a9{H/x}sg4%0=u8MU~E|1b'Rz~J;EQR?ٞ\=7 _!MD-8&|#!ާ]D%]I;Ϻ1yb#ܒ!BQLhہ{f,Jm@2-)~e$bPP%8Ys[`IAHE\; 搄|I y_pwG y_:JZ%" >c9'WX덦LΘQ(Zj"2ؓ$=?42TBd3>  JH$d8'C8'ABifft\ W)*lWR>y:u?pOSu?pOSu?pOSu?pOSu?pOSu?pOSq+3#^k؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱyb#; 搄v7 ]4H|jl^k؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱyb#; 搄v7 ]4H|jl^k؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱyb#; 搄v7 ]4H|jl^k؈+8#59!/u/8l> "0!pHliKglp!ou8Dƫf潈"\3[83BY82[v#!ٱya:~!-7"PDE&{"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"adY?B&OЉ"ad.! ԣ0hw[EbIGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&ad} YBfGЙ&`Uf> aVXS0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3 #L0>,3DĪW:ՇrXpJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙa)pJf?ҙ\]9-,Eg yT:㧌t1:xOpmBӴduv+8b@MF+hLuM31_uܨk?ao+3F|iݢ0SFJtwW6I)NV-*g-3 0QԚy}]m$hfyq(h&H'hHڽꭜ~+sW;DU0ZJ[؈+8f9!7E+%Ns]Fj?('d["^jn m¥RNC.-'ዚt7~ hmACpval9F|iݢ0qH&6]r.w:϶y?̗sKusݘ|Gu6$D~2EgS~ŸyN 9؞z6\U!{Vpsn ].u^B BSRk\˜qp+nQ*A"Gpꛢ )|u+8ba;\1x$I(>ڨ\)$Ꞧ3NVpH-zNsoט?-9a\f߆Fo{ԗh*wտt1vU@_1UҮT³0Xdm6R%%S*B|[)Gʮ8LډE!{Vpsn 59L *Tb*oQ%^ ʿ IJk6r?X96|S8Q%?Cy}^σM;FY77RzV ˧Ϋa &OoטFVy]CHm^QNF|s 8(~PC 8(~Pr!{Tj9;wW=ه{D?+E]|L0p-M'/טM;0fv֯Yg\qI$|tU%:}vSjIǖTkQDa3xۢ#M1 )ReܞQQkmh?$4)Wv[6DD\\uM[Ҥ)uT8^^28Eg \׺5E/"JYT*"!dR0I$uҙ⨅k[,9ࡊF)?ÑOo[)j#> 4Eg Ǭd!4]e *ThMJ9v°ҕO몙%Q~U̘ST _$sݘ|G+'%%/^`HAR;DEXMk9[ffp2SImvm@_1U;F/2E, )t؈+8f9DЈz5pJ>/ޤʸ 4Eg ݉q.$$j9vf8"3@߯1U 4Eg 1u7ULPx~J*FuKlbU$8!JrJ[>CD/ /טhc37Q%)gD k΢y=+^ZK3DSCD/ /טh:3AlfU-5/6x5Cj"Jtnb#-_/=CͺE H!\F$JVp|+ BAQEg 1u7[D}eLFsf!xQ~R'YTPy,t4>Oj\FFY7Z^zoQuKh)T E+@H".k1X_ ޗ*+8f!Fs^bݠн;)xTDVh u{ Fs^bx_?P)W {[Gu,&:VK}8tnb#-_/=CSWSOLiVnYSGXjQH;h)bS[%m'ዚ VkyÑ 1fuS 4YT.iwU0K]LY)D*BEs,afuSnEg 1u7U0K:TPf_~]f4(RR^y4MWHH[D[&Nو8=va!uU$A?Dت4,J,+JOXCbQC|T=ͺ7{Vpsrf )'b_#כ[K Zuu|G0xzx*IRN IAHEUG"bpa~ڈ\ wh"nrg:*oKc0Pf\?ȭQ4~RѬyLb=Lb!D{[I؈+8f9 WP3Tj$!) dCk8Eg \bmD. ^FzσM;FY79 }3D M÷61tǰJꤹW=ه|^#9@_1U"!R8$˘dGQ=Zs.b}wEg '7!jyFjhT.}FI>dQ!(dG_i8Eg \bmD. ^FzσM;FY79 }3D L%)}3: KlbܕfӶc(6Usݜf!xQ~V8F]G |YWhzPU5m:2rs=ջ݈"o !T"lLu?p/Su?.6*JI/:vT(ТebnBR2F©:oy)Fk~Ye6ۋ%^bC2"K{'quSp[n-N.p1wSO -ȋttG%}wSO Gu??qY D6G̣ݢ0b\C'$^L]2Oup=~DQ3pmeJ3E^TQH'Z#G9<q[?QǷyS8ۢ0D:5*%+1 qvȂ~e3R}va!r5,JHֺd8?X"{cVuʔg=1RF^<NBT4%MkU.q/6U3>={*e@0J7gӮmvv"0NkgVpv#ᛖvanb#q8Eg \l0lf6v"0NkgVpv#ᛖvanb#q8Eg \l0lf6v"0NkgVpv#ᛖvanb#q8Eg \l0lf6v"0NkgVpv#ᛖvanb#q8Eg \l0lf6v"0NkgVpv#ᛖvanb#q8Eg \l0lf6v"0NkgVpv#ᛖvanb#q8Eg \l0lf6v"0NkgVpv#ᛖvanb#q8Eg \l0lf6v"0NkgVpv#ᛖvanacZM"JN~oxpm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm5c aA:pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g~3 ߌoq8pm86g yUfxYBdF33|g7o8'qOx>3|g7o8'qOx>3|g7o8'qOx>3|g7o8'qOx>3|g7o8'qOx>3|g7o8'qOx-"kka/+8%yJ8Oxp'< 8Oxp'< 8Oxp'< 8Oxp'< 8Oxp'< 8Oxp'< 8Ox#9.IFooooooooooooooooooooooooo“`-qiEgVka6FY.v-M;mVplݾ0flDa"[7o"ٮ3EgVka6FY.v-M;mVplݾ0flDa"[7o"ٮ3EgVka6FY.v-M;mVplݾ0flDa"[7o"ٮ3EgVka6FY.v-M;mVplݾ0flDa"[7o#׉5fe *aȒ+I?ΓN~Bfao^$՘J!sM( !pu!&mB_.D]jiDX86Լ͡/ʗ8K0 l#jN~GVp *e+h(!lbc&M3 f5[E#{I) KFt:_GUL8"CdQ0l&] DË(qt6E.ȢaL8"CdQ0l&%TM$v7Go#Qpe.A$M3 fie HI'V!n8JJS0:tαx(hԢTƭB%!#%$CKjA\keps(n$Ҥі컦QD(>zva!!z4f$(qCɩk$vS)[DE\bh㟂hv[n_B]7}"Jd Dœ* dߎcJJo\Npsݑ Ҟyͤ)K}ohǽ/Jj/]7!=D0$ʡnUVQ%$[ I(zX%JE[&`ԨRy-ڠeyZr"\-0P Q"8|L/^7Rsn?uD;faF?](:uM@A#hmgY=xލK7ͺ0mf=ۣ%nQP]fcЗYys.}TtmћH=UoQ&iMpA&u,aӷ)4ڭXU049y7i("hsaU_1>I>=o5HvVJԣ"4I*R.Kn凁Iz݈ MY9>$s A5a A|%&ˑjQ1pҳfhU{-P(U헫0e/R<ͺ~oS9~}WC0f*J,ϩ=#C %%EPU&RB`\4$B1ˈO:_x6iJU livX۩Ý+gUL:9*6DI$GI6˩9S& +/ к:abC}G.$:ˆPa/+f":I&Z+Vj9 vSTAh/!.'V8XKIH-'YT7FOvw+8EDInWCȯT.&%dMQ"1eK]DI#ơwT:R},{~֏M|uWo#tjQr)˭]-4BRltNٙ KȜTٵQ*-9(3$'ܥ џtcԪú'tA^J˵&9B &YlJSZC*aS:UI*RǷQC0f*TR+'%L2_h& GAɼʩbTX-,{&b6)WA Q|\m-.m}hy}%j* 'آb(|Fq(Sj;H*4*NCݢ0pD"x9zmFǺM4~1[_jY/2joGukzUA^ouv7D$ٝ?B(}EM4J+h*PY:vgf1E֦uPqrfof*k41A"TiQv6[ʒs7 I!7dS)G.껦~5_|IW8%I푖œqD$3="P,ӏ5'F+QC0f*(:qcRT Iݥjf1?&i7C݉ƔhQV2:uMLTF~-'#ءpJF PtUR-'"zTT*R2 IH/R.Vp%ΕI9Hʹ1'$t9yZǔ:wռhʘGc[ٰ]wFYJ^{jr?蛺-J%z5_q.RE%YL~J2UbSjWI{~IJe̮N? D6}\>q(k&7=&4p#U+PG*ϭ3a;?TQ{U8㨋(>~0pP=ڹ#%QC0f*"ru'Pf}&[<1$U 4TPȯUN#DzC䄔bHZ*{WQ "/Mo.: _"F"5jpu mD _a{?ջDa"n@n8n٨Zq^7~=w{=GZ9Y*O{go#uDL2ͷZ:d8$""َ?D:H'Lg}NΓ5Һ+)H&+%%!B] ]?5x<k"ƅn #:J^QH ]]s1zX֙(s>Wu?榊6ڻsPU+n5j(Gץr>r?U8M# ?=oJs<9wUQe)r0$,E'\sA4^Etv] 4NQC0f*ΕTKIlCQ8}%I|:mԒuS/bis hXW:P zϙO7C(j)sKGA0~suw7QT/1B+.Ot*(YQA}1zi[FY.!b*ܡsI~ AÕ+ldA)5jݾ0CJWOW~uBAM2HBaȝ3ҟ8}%`GRKQ"ʑpn:92-I2Wty o9P#m\'\v h9Hʮhk2O=EtTPb'T~R- T67_ty}$So;Σ/\ChjtMҺL􏷨T5g)홙Q4uĪb>I(l!z6S%?~ʝuD 3=" ze/)홏/C^|B(u~EP| Ci/12W *) E0CDu3b(Ԝ\1K{ =Zm5ԣ?.y~o}c }FKyj=$g.:GcZC PѺ!g>Hx3?'ht#+*)f뮝23:OKMEՈ4sTgT4jYTFY.!b*Z*gQ%%b3a2]>sɩ[DB&>_FgJkO0CJLMYKFMӐ$D3Ju%ThZRQm6Y \ʞ$R7D|[﨏t] 4NQC0f-R2N;J\:})DK5: i/ʄ'1 z!AuGQLOVuB4ToSCڡ=yB"Մ#wOPAN%o],ݘ"n8)J33#9 qh9㩍{~8^Bm؄\U1Y.it] 4NQC0f-ոXdyG]Q%).s1Cs"PѼ"RCһrPFuҬa~lAgLms:# k;VؔhJU8ʩK'OT!e"#xa_LAI9RɥdD~2Eg\4oGp$ R=餥pum?D<|1 pq3ΘnJjK+ˠZQ3/Eړ=Qt] 4NQC0f-7]pJRRtJQyM P*!#^ϺFI NG/JڞP㾗zBz?gvD+ȹ|ߛ^O^:Of&_$gLꋢWBZlRf"Dppͩ\IJxquG)Eg\4oGpk?#)UlUħ]0CJLU Y|3,W6t hcd8ϜXYRsl֣IJa*}[!\"&D&\uN4f|wE0CDu3bn-(uQZ_4y.֜!:D d $]kZ YA*G% K)0䕝w9֑ۛ믛"UN5(Ϝu=Ѩ:T:z<^ޯʞ>2^) l%rvLg}&3œGbɌ1dwc;Y1#3JvZIKE," e#=#XD{ӚT:j[+s=&%mxq79d/r%? 總Vg1sCt@ƑrxEg\4oGpe G]W7 *e1gNG!Q! r:‰E73DT:U|w*}" WHH"Rp+2lR\ݻO!w:s1UB^{B9oQvŽoiT|3i{+eU7Q-*.c fBtב9]pa)Gm%^ki*?nVpXsJjK+ޕφ{΢"?aE3(֣>Dr?U8uhE xYi[QD6UW pyyu] 4NQC0f*Ktp6_"E#[d3i{*`״GYiGPiˤߠUku,J("C9K2+FY.!j Ey'g\ş'?H!eIA2]Utq erm$$Ys?N~yVԪwNI/8#v9 SV'-i?);GI8N {ˡTtNܿ1PNQYI9 tb1)(|Πĥ/MÄ}N$FUÔzW #!jTҖ5H1 *o% Ov] 1Qw^J >j,~;I8Nek5yLzUP1%K9AavĞ朣9Ķ~e ^l?)(2ůѧYe'gh꫉ODF)L RJڈ)1{3ߠq^g@?E8/~{3ߠq^F ?>F':RL%BsʱЖ?p{F%>Չr($0gD'7%+[`AX\s~>w)E9RvM{|# ٻ|awL&ٝ+8E¶nE] gm0pltmh"\+f[50fv# ٻ|awL&ٝ+8E¶nE] gm0pltmh"\+f[50fv# ٻ|awL&ٝ+8E¶nE] gm0pltmh"\+f[50fv# ٻ|awL&ٝ+8E¶nE] gm0pltmh"M!m ,G($ސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސސ5uLbV!XbV!XbV!XbV!XbV!XbV!XbV!XbV!XbV!XbV!XbV!XbV!XbSlC"+,)Q!, !1Q Aa0@Pqp`?!'!! ??j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j35YUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYUYGIڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬڬ ?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j?j0\8D $$7ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|π3 DL$46L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&LPG J*TRJ*TRJ*TRJ*TRJ*TRJ*TRJeHXvuydɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dhr&!6Vg uB>ZF0b"B)a>H38ѩwzKz*ҩ#+"smf~#H0"IM2\-;Y¦KcQ&e Ssmf~1 sOAp.JRg81kI7L#rHD^! K5> 8e=k1(aBn@{8X2\̗N&^#0Q<ʼnR  ` 6zq<#rhw~o|>D^9 \*hԻ@$t?.Ssmf~s̛IK88!!NpIDI`&v2$D,M ,%H@4Y1Kg,B{Ǖv(T{XxSF=FxhmtSsmf~{@0 A )1ɰcy= /%F5@o n&dn&wy;0Z.aLQZR`2})4j]X:Nň!aEx;0` NͶU_hS$ g|3)pSyԤ\uvْYXBA g/Y0o܇ 8Tѩw G 5;.b w X=Xpm1C͵)3h^Gа# l~F8dѩws=O@f$w|.G EVpm:1"M!t">d{8:ե0vI .cq i!Āb ؟\dnhԻcq@nL<ܜ0[)¹ʳ?_Rg^PE=#P!(;;q̖v/7dNT Ax<"hԻKɘ  iL h5 ļG`lgbha1m:)MaK#;hkcnM˄%AYm8y5.p*?IЕ&L%0a}O8^N:((ðsY1B-Y`i5.p*I$d\!nwvE^KY8,,,O2X: (Opz7Be.[ 1`W\ bH4j]ADAnbeq7 tbjca*Icctg-\mVW %D ,GSFv *򿣩I{̉1ssp1&4j]kpm+:缜,B`DjdAGk4*`)AMyFv *O@]#cf͇`s8I8*V8 &X擽` 0`i/_!&K,c 8']B-B,HjF=T@@b"6#+O)j#5.⭾4h;[lHfʳ?KMs>m;&yFϛl3Idѩw3*㿒g-4j]ʳ?KMs>m;&yFϛl3Idѩw3*㿒g-4j]ʳ?KMs>m;&yFϛl3Idѩw3*㿒g-4j]ʳ?KMs>m;&yFϛl3Idѩw3&& 0 s<2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&Lf4P6[l\ g2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2a;tJ`v@ @ @ @ @ @ @fl. gdp1g'j@p1pY {7ϛ1`о?1GzTD@FO; $p\Dt -EXS}ѻãGpY]X`= 4y7qH=wLG <2L pZ8dANkUP<1ղ? 3&= 0I)BstcAb_J4(G3l"\< 8;t}iML> WyǰQd^] s٨` ӆv 3[+3/77loF7\V *T0aP ؓqʻ0,HEah`i2B=.Pa<aí[l:u, HwXF;LO<Ġ!.ũ<FM7#`@G"blI<{3[+3.5 !s Ҏj6y;uyѣXq{qipb v27^K3[+3.:Ǭ0`;`͉/&sqE.ޡ` MȽ70l'dؖ<)p /~^>XA#iR6Hy=Ęzu Hf:Vg&b1x4zML 1dS% ($hr`ϝ:{ S"dϱ D6n&s5`DT3@@YE+Sy,1)M0;ZjGyX0&NFHNf1]|/GHbI;\/0BьI<𙎭Is?A4kGTE)] _GMZy&6b~"Ȼ2S޺C(;$4 >0]JD $` m Q#ۆpi ` 8/n| f:Vg&|lW)5 GA I&ˌ<3 LC^Ì5r:F1Ά؍p0ApgqߘC"n3pC`ȓO^1ղ? 1 ,N 69Y‚-@I5}ÙK:͂;0oW&c1A QHIᯒ!ō )"Xbpxxpqjw%`=zr#9MA٣Ļ.1ղ? 0fՎ7\Q#0OW mpb !u\+# Ŧ 6g28z v XDaLSTȍ .okRd9ԋM@8; $(e'xhu :{-208:XnTs+.#ulLi9.Sv>2|v`ؠTν ܾ5`P,`rzYtÌحw[G-N"?F.h)YD qrj)A"cÓh!uulL(OS6K;v0Gl]jImB '&6v7ם @pc 05 &K9:GB{ $h)7Q1ղ?0Kx0ؘp\ل7=@{9Mɀ=ݞ/ bPdu6dcpvi Z.؀jS>1ղ? 27W_f&%Q F 0zQa=&0RDy<04QـyAq(ah l'&y(c@qsHýkxx?E(#Υm_O#ph(m =(`C4cOtL!Ťd<=D~! :VgF"ci$s3w|!6 e9ԍ85z7,DÔ}BאbOu h\WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWSGABد/////////////////////////////////////////////////!71E1!0 _K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K_K@X^Z/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫzkQV(ʠzh׃ (L. %`_5 h3w%Fܢ5R$,+T +N̔ ^8 > /}Bc̄o faUXPx"گLԐ@J`;ټ1{N-IDx<n-b^LCet6&ɉ̋s?0`יLVYV%4unNP"1xZ42s攂ڜཛtf!p~Gd:ϵoIS=);4I Dx!2 a=/X$VSDzvjTq`X04atu ^$Vx0ћ%A0;QZSF}ާg͊!%Ջ!Ų:Kuڅ &4Ixie 4:v.Ll.t٥ N: !Jl/VgYFwQ0#Hi]fq5DbLxFΨn`V6yJ-1.G,A g`%^Cz1HC)`v5Ѡk$fN/9.l[ ċe)p|2̷ 06@Zdl0,1ǻ,jzVgYIhQ6(Hh" F{ѢyJ { Yb!-aj}Xu.;[+3ļl"π@Pp!IL6 \p`(,ˁt.,L/åfuji6(63{UX; x ! ؞&; ݁Ų:Kn."'_K8"#D)hL.c%d_nS bjfYgڸl͊$ ^$V¯!dIebW $` ظл/z2 űYnp,l\=ѳ!QYS㱒Auy+-傋?[>flQ$7ulg$ vvy 3N j`3&BlVYV%|"b:aF =& DOKfz2m;4iedlc݆ b[u}آHn05IU`8*@W伝X;.&yX{2Le)`“ 7}آHn05IU`8*(@~, %8 iCפx [+3ļ[|@=%ep5>;.&k`bK%aD)q` ,%4syYgڸl͊$ ^$V¯!ll.Dp_ y;@dAJ pUVgUx0P1XH"7b]#xZ42j|v2]6H@T31m= \Bb3[(`艵 +c4/&oV3:ϵp4ٛI fI*]^BHI&cv52$;8*[+3ļ i,TGeaohI?Հ<X!H`Kawiedm{U`C>d`@I%8a`wѽγ\ 6fCwYyJgaW+6 eBz^MD I$I,Y@ VYV%)ni$K{2̸M|xi`_ ap7}آHn05IU`8*pqlΫɭZg=J < t,ˁt)p/Q@ Q>flQ$7ulg$ vvy8BոVgUxFLI{$#+@jaGL)g&Q& `842fǜa-hÃ9oAI6ȣ?wj4`#"mh8f(>hlo3W )(lH7aJDT-{~2IU`8yXDcÇ$@szÇ8pÇ' 69o+qlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$η/3L.gVgYgJqlΫ:42ufujy$ηЉUpU«W \*UpU«W \*UpU«W \*UpU«W \*UpU«W \*UpU«W \*Up`HX:iaR¥K ,*XTaR¥K ,*XTaR¥K ,*XTaR¥K ,*XTaR¥K ,*XTaR¥K ,*XTaR¤G #KX`KK ,X$I`%K ,X$I`%K ,X$I`%K ,X$I`%K ,X$I`%K ,X$I`%K '   -GH+ L%0J`)S L%0J`)S L%0J`)S L%0J`)S L%0J`)S L%0J`)S G:|LCdUʣT*Q?GUʣT*Q?GUʣT*Q?GUʣT*Q?GUʣT*Q?GUʣT*Q?GUo֋ D~UoʭU*Vߕ[~UoʭU*Vߕ[~UoʭU*Vߕ[~UoʭU*Vߕ[~UoʭU*Vߕ[~UoʭU*Vߕ[~Uoj GU U*W_«U U*W_«U U*W_«U U*W_«U U*W_«U U*W_«U pMyx~Y[~o­U V*߅[~o­U V*߅[~o­U V*߅[~o­U V*߅[~o­U V*߅[~o­U V*߅[~MefuVYgUfgU9F;+3Ο2us:0<1YUtkfYtgNgQ'3̳]\Ϋ25<+:s:vVgUi9>euYay]5Yәc:I,W3̳ O3ΜΣZNgOfVejyWMVtuΪs:|5,Sj3efuVYgUfgU9F;+3Ο2us:0<1YUtkfYtgNgQ'3̳]\Ϋ25<+:s:vVgUi9>euYay]5Yәc:I,W3̳ O3ΜΣZNgOfVejyWMVtuΪp1_n}j<weBJB T%* ^'(8k`ڲǁAwk,(J$0Aed*ҫ kh@ %Uޕ]Uޕ]Uޕ]Uޕ]Uޕ]HG{U8 R26pnM[ʍ ތ ! >FFN;MtF;+3i) s|{dˇ R#hAk. @,Xl ̳]\gs3^۠>׼ pпj0X_y^F^5L0ᆌ ]5YӀKv5 9vxr OS!r%waԁkl8!c:Io LDAN֬Dqpks /kP T9X7tG*X2uoϒN2#NVs{U0x1&_},S:bWcIt|0|gw@{U7 Vj&e0Mh<>&&ֳ{J01YVejwM !)ʝǶ4v85[&tgMsCc-JsuvR`b˨z:@ '$#bI{Y kC2oefuVx%G.ĐyNHhA` 8vP3K`1%e.?w0=ĤjC #rM'۽,V0Ht&;{O_},SoI<?w&dI3; ϩz@۷nXWMVt޶q Kֱ$I3;Ӆ@/#g C -nL؅4ߛ[Q:#LKefuVx/t2Lo&$waIuChdv2uo@}ܧ`kE f ˓X`}roW?ԸL^vX Ipj0 zE%@rHX$dO] Ua+:oÉytA| ) %I0dL@SŽˡKOQ&4{/ .Hv(( Li7^)unK c`əo2uo&FFD`o5yfe<}q4of']Xlj_},Sr@vrRGY}/O)w\w36@ PY7Ot"B5`:5GRmTƱkP;Q&ٮ`QH4bI,,So BM# 4?1=NyWù{Of{A .=i޴ 63vܰnAeɘ`K4?&_}̳ N@pB[ R>0p墐43 6?A?B+:o!v /;p tXS Q`Be3$C i2A c:IѧklB O+'&|5:,g sE3Z/Ya8mp 縏#*8{U8AK}3 c:IѧkmD b&pD[C1SY!c>j! qm`Py4@eګ25<^NՃv1C46 ˂c"BUv܍ j? 7]5YӊpF@p9L&IF /|=ZF6xcT̐XxAwp  ^7fZN>V#{G4E$@Hu SY},WJY!c>e阺1B`$б7^;pcn0lk_ zZ@7 lUfYdBB4!{M U8VCRsõrJa7Ϊp4ipO{6` 2up9 77Yay8CwOlbwWMVtj(/W6ȝCM"wջʌvVgUi84I /L6Ki,W豟k@}yUfG.pX`Y+:qA6C0k. /f|$=*]\f6(ټefuVO0Dbu-C=%St`Ol;&9̃yOfE]ńŀer3|}̳ O.My ѰGJ,SBmIx*/!aT1~w]5YӋpqk(v :瘼qB#GZ-)i'640z6N yQ'FM"ey;{OfE__},SdKvWnCլkDrj῏< ~n ]:ZN>Sj$Gt؀:de1M3SY!c>j0Y8y%&7,n"f쮚9w=1(G#@&چXKi$F;+3 ' , Fv_P2H:g%0ɤ1)4XXw@ٟB e01+}K;|5SӦT hx}@:tݾDYaZ LE"=Lëff!B_r!ptBӁ( %(a%c;U4;&@%gYL0!Bd k/B|qEV KYw%2P F;+3Ο2us:0<1YUtkfYtgNgQ'3̳]\Ϋ25<+:s:vVgUi9>euYay]5Yәc:I,W3̳ O3ΜΣZNgOfVejyWMVtuΪs:|5,Sj3efuVYgUfgU9F;+3Ο2us:0<1YUtkfYtgNgQ'3̳]\Ϋ25<+:s:vVgUi9>euYay]5Yәc:I,W3̳ O3ΜΣZNgOfVejyWMVtuΪs:|5,Sj3c O A!& M>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>B>BXAAWGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGC6D/Okxv Lrc&91ɎLrc&91ɎLrc&91ɎLrc&91ɎLrc&91ɎLrc&91ɎLr0r֍tFbxX c<,g3xX c<,g3xX c<,g3xX c<,g3xX c<,g3xX c<,g3xX z3xX c<,g3xX c<,g3xX c<,g3xX c<,g3xX c<,g3xX c<,g3xLڳ:Kf:k%sj.gY{t鬗3ͫ3Tfk)Ӧ\O6ΩRugNs=>ڳ:Kf:k%sj.gY{t鬗3ͫ3Tfk)Ӧ\O6ΩRugNs=>ڳ:Kf:k%sj.gY{t鬗3ͫ3Tfk)Ӧ\O6ΩRugNs=>ڳ:Kf:k%sj.gY{t鬗3ͫ3Tfk)Ӧ\O6ΩRu=鏦"dcHkM C iY.< R0P ĒG!DAtp?TR46X>"aDFk ;J IdbpsDgTp@Fh34hgpu6z @ @,o+3YO| WhS m^s Zy-N5H]Vv+<8N!{2"3v .Ba(ꘃhoY.  ( $KvT K !D d [0FN!g``C>{F̂S&yA! | >Fzl{ ^Bv83B|; ' ag0{+ 0AJ  7mܚ0\MrufQcX `6 \( 6F63#zLp5*[{8YIf&&5f˄1 2 t疂V8peḑrf $cdNJ8MdT)NM|9ݴ+`&P՘p,xEe {{w|r/!Bc>0nO, H7@a Y e׎fuS0Aq6фԊgTo]A݁6 Ui,$%HSyY{5h .v]"n uܛYvlG$܉@X0&l|5txx#DOf 깺!n# -VÛ>Ƌf%ΚCP 25_`Y zp`ls M1`fuJ0Fkfui,q1iaՇVXuaՇVA8%,%(iphn{NUӻ5"%-cQDy)F3h;AH /g`J( ֮OfF;] :k%uM݁} 7aH$0 i \ &;nH›Yئ wQTh?DgaxhwOņ"mfH. I"Ãq3D,3|, "h6"Is&NiN =mzBq7.tXa"DY߫3T݈Ό/ u2E0JZM>Kh.+t"31k3YO{ spa@m I+XM3$O`Q&$$ǼvɏAY-aO&g15cmMr$0e}xuo@ f'0`Eˬ~:ah: *>#Wv<: WVA\ &@"1`a6 P9T47Y,IF_0A> XI;@Gu2rFf fuJ xpnA\A3#T &B5LV*oqVtL-wok3YO{{,҇,Bˡ[ѐ2>c<74{46J^`m Q;H d6ɉ.tKx,\ O AI dxdǟ徬ΩRiXIs\g9,Bs%fŞ[!o8cA݁8`=2mq +,s:v+W 8Y.(j,F`E7[v|Z `QPZ#*|7>Q1y ᰁD% 1.V[cI$ē`Bzt]E?fuJLZM{o8 6 v;1p&gG՝6$ 1q' C$ƴobE 1%6 \S[9Dt}eV #yNqQ h7iWHaQ]*CbN:ތxȂZ4bK6 d>1.LY7ˌ /.bK^M16luVeaX^Ht3Td*nqCi4u$&gFnwCz' 9J/ QItŌErѹyNqQ"`8LQі6䮵K_9ó'vـ!+@'P Mb1&$Hs<g&.FBp ^I1&[ ٚMC=3Td*n^ y  f`!Qxp5&g@2^؞ d9M2_Nr!$-íW>p _B{O\! p~I6 fϟK !bMYРC@G`h"o0lΙL^ΩRiXI;$f/ĸ`p&s fk)ɬYK1AbĔpu%} Yyw'Do4鬗!MhRgg`J t\'n{XL̀%<3#^nh: HIfNewX)b Guͳ>@A&hΰ"aəwՙ*\ 2ki7 v 'bP]PDDi/$5&gF .kalzs i9 <5ȁ! 0nGd T[waHq"tKy|]$"7nRB-E2o"0yAg; X ފ"RaŴMz<SUÂ/7md`Y-<0 8ٳf͛1!HkByvZb9@L `4q+81 k!1$,6"$b7.5)qvVxVfŞY0,`8( 9T!-uq:yt:k%f>/M if8 `9bɕR`ɮBw*\ 2ki-P Nͱ IuX^ VfŞZL g|& GA"|p.{Nr1\='_ S`H2yp9)t$rFm 5(YRxa,K_}l 2它e=,[fУbYeh R z،D fq a>H`GC4,KԺ%@3\`d$#IS\ u2_o2BA}pC '>) K2e UPY2LR|HH%O 0q!D4W!@6 5DnF>Q9U`2,\kq6ΩRugNs=>ڳ:Kf:k%sj.gY{t鬗3ͫ3Tfk)Ӧ\O6ΩRugNs=>ڳ:Kf:k%sj.gY{t鬗3ͫ3Tfk)Ӧ\O6ΩRugNs=>ڳ:Kf:k%sj.gY{t鬗3ͫ3Tfk)Ӧ\O6ΩRugNs=>ڳ:Kf:k%sj.gY{t鬗3ͪ,@JU)TRJU)TRJU)TRJU)TRJU)TRJU)TRJU)TRJU)TRJU)TRJU)TRJU)TRJU)TRJU)D a'*PJ * * * * * * * * * * * * !w'f"""""""""""""""""""""""""1yZ@ɸi s<<<<<<<<<<<<<<<<<<<0 0 0 0 0 0 0<<<<@<Pp 0"<@  H(@0<P 0 0 <@@@P  1  0@<@0 0  <<<< 0 0 0 0 0 0 0 0 0 0 0 0 # 0 0 0 0 0 0((P<@< ((P<@< 0C  (0P@( @@PC 0P (<(@(<@  @ (@@(  FP4 (< @8<@ @L@@ (D2( 0@P <@< ((P<@< ((<<<<<< 0 0 0 0 0 0 <<<<<<<@<<P<@<<0 P0  < @@@< L  (B0P( 0 <@  R@ (@!0(@ @ P(8@(H L < P@ @< @ ( (P(@<( P @  F"( @P(P( 0< B@ @< PB @<P<@<<P<@<P 0 0 0 0 0 0<<<<<<߿~߿~߿~߿~߿~߿~ 4vIUn͋,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,XbŋMNyR@ybŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,XbHD*9]0gF4hѣF4hѣF4hѣF4hѣF4hѣF4hѣCj:ð`YҵR%yϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϐ>*% # Fݣ;43Gwigv0߁a>3H;|g~wh # Fݣ;43Gwigv0߁a>3H;V́ Og>nnj{/.NKKWJzD-]& E3Un|QT/n1ȱPՌ7?S$"EjKВ4={k-&=U *Ǧ0U]Uf͚фo|fB`ݗО%҅n<zK%xnr8C77&GS{!F#7qѾ LL.lh+"`HhO8`XM ĻRӘ>1e`PIG:CZZM@aWUp F#3w0!5ϿR >0jΡInיM1\,IXUCv{˰8b b+(1]eC 7v2L;e7la 0\gtL 9/pMykGwcTQApF²1ۄ_`X0~!(N,*SZOaX (o;Ze`X  d-e*;[@,.SffAbCva&B H3Q4"79dQ1b1bޝf( 品E9-N,3+ W -N@9.+h?&D0)%,V긽lJب/ RL)1 7L "P`p?1}HH phz;NJhO'y][2YF%̂b:S^V"BZ3eKxX #XR< R7~I ;;EwHVb7s?P)$EBjfw_{+d)[J1K`XfLKu]-Al&\S^XHtd/I2!- L"lK=כ)iV hnf_SVvOQb`> vCX- B"DJ*[ iFQ ܨޛXW^Ƭ`y31@?|ەXa + VŴLQUq{d.AopEQ'20IWWf? "U U?\H؅.quKK>02ח4O3@" YFP,JLkҴ|f$"p;W؎s< (q #~='X< 8ID@(<zFQe䘔^Fl${P%ǔܬ)mZCI֋Il(W[.FW!V_lAwLSwUlMKۚ Kp?jD<9n|>QAYP<}ޕ8P.QlMELO'3O-.(eopME@ǂ8M~\`eH[1Bŋp|SY-kWUIrTY>6Hm`Ї E]B۶f&W,͕,x1}{^&LD^i`kx.2{>3U'e זK7h/85fm+-Ux43pg\O9>'=4|f(0e ܛh$gc3O_5P8Xs]#qa ;'?ѹ>';4|f+@,]H2 ^ *./qe!`W_+EH$FI#UPݱQ XO;j<[$ X`Z=B~<[%ɌSkw 0hlc2Dq/A(P>3UlkjB6th^#卙*ν73iD؁>GBR k Ғ.!)؝M>boHfwE54|fgafؒ^m},Q**UfwE5|fYڃ:snV0=yhb REPC8bXK<7Q. k>3T҅q>|ۿ ![߀Sـ(~J*TRJLRj++۔i΅bRe;¬%fovjk{KSQQ>P 4m?xawK23 󾀀bpzđ8>tB߁a>3H;|g~wh # Fݣ;43Gwigv0߁a>3H;|g~wh # Fݣ;43GwigR†X8f|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|J+v[!PPJϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϗc)(VV37򠠕`1y*TRJ*TRJ*TRJ*TRJ*TRJ*TRJ!W3%iNv%c޽zׯ^zׯ^zׯ^zׯ^zׯ^zׯ^u FRV Oo-ZjիVZjիVZjիVZjիVZjիVZjիVZQ BI ׯ^zׯ^zׯ^zׯ^zׯ^zׯ^zb(/R/@03ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|f+tߣ@kK34hѣF4hѣF4hѣF4hѣF4hѣF4hѣF4i\Uy&`HU =ʪ? {KT4~:@*ht.UP\UCGirHU =ʪ? {KT4~:@*ht.UP\UCb$U.wYyyobH(1=X8`ZkP?y6Q iGʠOU#5CR{XJG(5Q2 }^zׯ^z:i&dUV2;4 |Q<eDg>+7s+%W.%Izr8.p hse0G:- jig7Y3UK * I}m !vh`&BW6%#[`JrB&H .,&p4p3)% Cr,?]K> ¡5D#ǐR?wO (/~B`d2{d.{V]Α#Hq\mI4=ɓXZ7R V|THc[MNQ'1.5r_C'݅,UT,bobԐpb,ba#`6vP&҄Yl4s Un=\)Cra, 3.?GuGiL%I+,MX؈IP=!LyEN003vR5x_u RQ+5"Y@18G| YyK(K2Jy$_ic9DP*{p 'rl` qJgF8c!;&$x֏* X VXu~{ Id;'(@`shGx'"  *BBꬫieL00H&,ZXt۝vi7 HnYU 6l_wC-UK5k<9č8*:A(tOΐ;Â5$ȼc鈾@%IZ,eǡsҮgM2x[Q."Hҝ B02PꦕY^Hr@4aHG 9Fv|t28@ԉMN\  U.Nm)#!<2<zb,hYOR%*,IU#&+% Un J. jՉ.#yF"?/vAHo%B!E1@a(b8j<=l-p CF09#$"jncy}N 8X;mIKgv D{vz!I唹]~b )%6+9Ť`/ `]tpwfDKjM=c(R"7ѽ3Cp/qR8t˲gdQ~h,mr8/z(Pt4v%r_b"sWt.;7zya ,cfr 8,PQkX9]T20X=>]@rK 4 ǺNŀ!`",$&P! ["Af%"T~:H}\.\TĻ=}P#Z798[|<,D3JJ\lb(' 1p! vb?yS&N+ Y; cBȉ4jX /NcG㦔.lCx.v·@  0t5hgMֆ+rS^.`5[yL!ϖUCA~$68p9;+o'@r?#R3֮\`L3Q irD 1zk%s98\BSV;2T3RTBoB %q{@EYlPf8*dH`0YfrYʅ:k"2BؖTB$*aȇv@Q4gEa8Y8 зc",wt`e>tm n样 2lN1 * XKܻLAi;_P ",&k=ưb;*0%lR6 ʔQ!SBIHDR´` pEAt`F+s~81thY)7Nڨl`--8 2(, %mǬ$>N E:ARȈ@6#- p=(BWbgA, hΏJ{]]VcQP-ʈ" fв):sz(^)1j(N.-"Xj\G0Vѩ&@A*AYaymT/m\DfͅIzs!wgpO]]VcPbIWUjXQ Ü `s<0` 0` 0` 0` 0` 0` T*UT V3J*TRJ*TRJ*TRJ*TRJ*TRJ*TRIVyB켟 J9zׯ^zׯ^zׯ^zׯ^zׯ^zׯ^zS5@[VgL,6,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,nBH2% V߉2!3` 0` 0` 0` 0` 0` 0`X V wFPL.fbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,Xbŋ,X`r,h eϟ>|ϟ>|ϟ>|ϟ>|ϟ>|ϟ>|Q%lIKtӛ|%ǏHWA$ $G̵t]QC$q|Տ$"(@FV4jf+R܇A\u' /(9!|p6&%RBtaC3<FӸB"`JǁBE1Z2(]U)+@?P]UMEvY6Q ϥ YYd^:YK@@JTtz !nD@ `& p Y`'H( [S_@ؙhY5 zXg?jXb]mli=u^mg h)1Mbv5H{|LdB1fJ&6naG$n6oD*T\ͰqYŘ=DwZ*̵3"\FR80}fCƿz8l ;L4%n+;I$8^"s h0\VC:FHKȚsb[@&L8g0< 0A0La(EI[#Ȧ?91%dY0T߾_bvFֲaiTQav @IHd#)m,iUw2RFN8q/c\.<5<4cbLUHӁ"`DCdA ot68#BM_I0jOIL .#q!l4dD&@BV@J|)jmW0 `4ll3&W99|fӁ6 w"46% x/BF-|[I"4lL]3[A0Fm@\V< 81~ .q`0w3;: .*|Yi.kl6EtF)c!8`҈>b zw'o ?*%e_51rm)v(, 1.Uoof!Nrr& J3a H ۀ]f~L~hpw0@98(eVhh^ y2-2nJmߴVL1AD۵T=h@$/L("8,v,VkY3ńwKy>(I XE7Z 51[40ed1Hq{əSSyAp(!hdt|*.FxY4LDKEA(^ }qzUYU^, 0G N80}Z? |οC>8?@|N3I!zpES ^g!9@^"b'eXųDT w OdAS˂.O.Ű #`偓/.{0=jI GsV8[uM[6a枻yDk@4eMdm)+0K* N/y/[^rMFHeJUj0͌)>?c) Pw'oCD?]+= fe Vu;p:oE"V E`IF0 f_S@;'?q96pexySϧ`J!22'a^v)g8" [i({ϓʼ!t#R Ddx A,bãaBV"FȘ ,8Ave?ùw' za]wۀ@2Dv/$"%pjT6 $bg*` qLGٝۃ!1tB~O3U"K^Ag7&ƘD]wRUlOVˉ%uUU^ 903ܰ'ǀoZ;5 qy -ՉabY Sf@fZC6ˊ'ᯙY- 82c3o+`x&{v߷ȇ3KOۖ_'Mw! l.ȡv[uUt`Jy5>p9qzb^8 ē4ӀaMi[Z)ۥnoہڛy03ܰwx1x?|9b/`I{/^b4u8Qt "d{x.Ɵl4x'_ЂnI Vk#[" .@Puq B}ߓ Y=@\M xz%'?}- 8:ocJt `< P*gId^'>O3M?4\rF;k4 5 cw/B]ra+>g`JgE. T~"d"=@mY_f|#N/͕b0jb(iu S@ؖ\U[bҷvSS|g 2@E8?H rhJϙX,׀+/g 2q_Jh#Q]VF_7%IW u}|cJjG,gP >VE.C$W&LƎN%g,F8H;]Bhu`á#;I eLeI0-y. 8.oc,Q<(ڛ *պ%" obb~tIS/ OS:#bZL6r,.e)LIP&efĬ+`JWq!ZIfi pH2=?˟f|#N/u;z01RP˜y NaRo8-Hm|gb*pK/+Ĭ73͆$rH4c9h=%i)x9ٯ_5=OGy~:).2 Hqj) Y=J DГq9 Y)l<z05Uys/`I{?|,%O0) zi3,Յ"4l%*)EBV/2 7$ !<fl!DI R F46 z-y*UѥURJ7P|HH,w77>B8htPZİv 6Tɐ% Cyx 8I*,l٦$ٙHpbf-޳f͛6lٳf͘R_-,֝V?'ĬN/33X<^g?'gy8~O?|%cqy~J9?8sq+瓋V?'ĬN/33X<^g?'gy8~O?|%cqy~J9?8s}` p;ޭi ZYs&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&E6OF*H߿~߿~߿~߿~߿~߿~8Id \)EDurdɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2dɓ&L2d0H@+xLYlv۷nݻv۷nݻv۷nݻv۷nݻv۷nݻv۷nݻvEw> AZk{ٮ]fw{5k{ٮ]fw{5k{ٮ]fw{5k{ٮ]fw{5k{ٮ]fw{5k{ٮ]fw{5k&ٕ͂yXEn{)SsئMb7=n{)SsئMb7=n{)SsئMb7=n{)SsئMb7=n{)SsئMb7=n{)SsئMb7=n{)3ȉs`Fҷo4i٦Mf7o4i٦Mf7o4i٦Mf7o4i٦Mf7o4i٦Mf7o4i٦Mf7o4rDfZkۦMn7otۦMn7otۦMn7otۦMn7otۦMn7otۦMn7otۦMs"?+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7+V\~s7' \'Ъ{XP4ßףG8F}z7JYt]e)K#P>."(uw]]QISPz1'uw]]TKF൧h 1͛6lٳf͏1koVYk?N1{Ƒ1I=FHHYk<| ϥk\M"LeQ<_VDlpǮ q\P ŒcHKcT(|O 99,ZzBXcjlBзT¦`4|}j g2`*LSk ߏKZYA`]1Qq04P6Q,rru$2O?U*+Snba,-C4@@w/bh ҠAl4rfă$7PH , ְbzBr 5n os~@12oVXu8 `2@85ߘN7(@pD_idWc|Pp_ٓ!y|ylǻ kc zr.` @!gh[ 5(-qߊ 0*{Bψ6 ڔpdRuYbً?UG~V/l\ rb˾i_@8VGv-1Yp [f`,H)#j$/%%IQV(ìs`˓ʎ:.[/0<#R*~m9 YB'Ļh~J@|& \~w@7"1xfˊ74Uꕖ5|T(\UeUW,u$QTJм RwԾG@U(d "Àr%SaPjwDYtUz 3~yQ.*QBfʘZ2q^g|7"U m+lVYe%@HtQ44b85_ ;Xb[PW8['āf *a,P B*۪ѠG @ Bb{?~dRlDxFΜ.%պ`- |9'dlFg;X6gqa:1F%D*~zl^G[ GeL\q7 <LF̮XE\J(h*! cǹ!҅.2DOrrKm> q'%ئevQ@Ӊ i JfOsH&qfݡl7h I[?+Z{Y8sc #̌ߗg).G{."\)hg@K̒H:@(!"71w|̀R7ŝ-lѥ%p(.)% o|LF-Q"3b =cq-[;--sK ׂ|[%(,[g#z;nslX={bel$ xW.^0. g35m,&.qE% )$JDAYA6LWn!S^x-p11.%|Y1,ZHݴV#KJbZd8i f4>hwoϥ&u0%|3zA8R>jG KKZ8 iCPX2/@})"To4;}P u Հ">j(nS¹Mm= "BU[ۿGpdR1c pl sHP%g &;^l˧u1[;{d1Z ĩ #lXбnشDŽs Hfŝ i)^Ln/ &ЈO.izb4 4ڲ7q^ٿ;;;"u:sǘ.c6;1` `N?UJ6{diZ*F_E_#5 T saYD觢 Fȕ8F8h6q~G>V)wj[di LD{$@e-r #@%V+@UEqm`0b"AY˺b4吏VO1H ? $!+l71 *|)&D>` f"A@n1X5V19G:^, C*#7PM% [-fH.CVD|ؘ8Gv Vtq͌X,vtm#@N<` V0,XDDځd^ƀ76ebf!|)?N Y ۅʑKx5~4\qB7ɦk)\||SLu&٨Ń k%"6D52P{A (dHfRh0!Ë*Un(@  L) Y9LFV{kXǚ,Eײ)PtI. <FM2ş ׽koI '3dձ`ؽYYk.8`D\ tApXsM~>~#bGxJP#Е$ j$<)FV < #yҦU)X 7K먆"1|b b X[]WHcFjL,چ@8zIMqi_pHDƌLJ6T̎CXt^d;ϑt=m11(5Y@\&3\zyjG0h U%}JW ?USm+5WvRSφʯ~jC~#bGzDj!p &MD}9jG0h @J6'z)q%Ίxs%ˀK3+ރ||S*nKd*trnSLHž`H&53+'VD}P,+"D$H1IDKL$XNO"Dy:h - 6m5pK CC Y>IiBk kyG@%5cqODZ[6`+n$Rc˴ dF`] vج \A.yo>G>V)pڂaFO:{Sn}=w! Nə|7(v3ɬ 1gy9^8ҮC }jGm#@N vBfi fm,ybj7$Ty} dJzW$A0ẹW$ @p^iupL?U[m(WVo$K`f ĥ(`IchC ]4h܅PY< Vd.dB }G:wdbXV[қ.Bth $2Iq~.ՀQ ޵귋m(jykLX({r.]F d6"~#bG`׌%/&J0ݽ1BwZ 3ȖW/Bh & ,uX $m} mAy"%1f[koIy-ny"29rt: @]Zci/i|T!2 Te[{? @hHo0`ğ$Hi-n1%P#P\0@bwh"ɥ(eHB4 £ n^H ~5C ?+ֹsd連2%Jo!@9H1SL>¹h]r(C ϟu4`Ba hX<D/f=xٷsba:kQ)mm F\iO_(DuY`+(T鄯֢}%`*wl(*?UK[C D/6lۼ!K*f={L"\G5ԖAvmGcdzRP|g7Ѵs>C9΍ungյW}Ϧtmϥks>k}6oh}+]|ۙmU_o3sG3Z>ϫjyM9J_!}[U|mFV3ګgl:6gҵ}V_5>g7Ѵs>C9΍ungյW}Ϧtmϥks>k}6oh}+]|ۙmU_o3sG3Z>ϫjyM9J_!Y"'z5MNy|V_5GBS tEUGLD|MҌP, /(K/`'N oDM{3 uB2L_QOНA,L寄ѴxӝāU` eXdjdň,ZCm# HU[׽ǏuStUJxd (BbbXկUʿW*5jY̕w#u;E@{U6>.}WRP+N}K&{G1gL`( ϴI4/%koVNJv3 a0Jv_50@Um6\_-%,ӇP"#%1Ҏd/1 * a>8LV!3{ޛg{+s`N #I ^9xBʺB p yI&ʃD "6Dq[X˿iyx8JA`Z."6d;]sU&lDdR(HD@DEV!`.9!>tm#b]Nd2uc_`l^0@Mό0l> +턗4+]|ۼ8h/!V-Ávh~Pl<C[=bzHATn^|*B|I+j{Hg8!5ηStTUU^.,d`e13u>ARvJ rJK&R!%jgwq`Y.g[cK)[B62$"B DdJh?(a1I$ ZBViWKfXVʕ8&O]ۛHxFؽC7"Q@p[t'z!;˜\VQ~NpJI+-%,J4U%?Ǚ=2lR[#)Ű2zVySj2 -iШ9 R2fZ{"OIEfsMǴρ #glmGBh"Y!N֞DWhVȒ@Py z6@v@.JD<e8J_0*/W 1ezY>x n4}ITb*4'_^2F,>?N7T!P%hp(Rry@ ٕ[!2a*n{ ϜKtm3@.5&N(lp3K79Q[+UFMcIvda9Y+]|۽Pqƭ;M,%$N P"AtL$(09Iu^0% sR \2XbBb - mݞ3V_5d9s)9WH˴>VEx:]>KYw!pG| Yǟyl xav8սU"T]O Ft/ 1}w΍Z "! N5dv/aDlMVHSN}ҵ}ײI6O' ʫ_t"i)U-!̉< HL 5OqD= #wr= >^j{@"\G-R BLX0tw>f]O'hfk<$(Ēr"C0a"eyOZY'IkEkqj$)Gh=d  `@&b 9SN\tuo3"hR*!a,BJ_44/ i A@Y,HYġBTU{kN]ςDd@P`Pd2(bU,P`k4LMA>xCfx5Ta%ԋP~[ 5%$ Ihy3p Y'gB;:;΍x0#]|¬tu3.kx)x.ܒR+a;@l+QdK#A$R@y[D@d}յW})6O/$4K#`e/Jc1jc̴TYUeERr.*{*Ib6orYR `(4 ;έknĬd3CbaECHDFGdv:v$wMyRS͸x,0@;]|nWUmSQe8-5FǼ@f  G@[cМ|J_!ZSsd1 ֎RGYSY 2(fРeU2TXPVFP\ȉC8YF8V_5@I?,BRW#23QaX@he-R&ôqlHxY}8w6i`-5 ZPmYe `6j$3BӇPh> 4|"|y=΍xMl%z#<=J` Za"tuo4sȼDT=ǵDh x d w[U|] \D͉ +J`t=%|r,0D,1,߅t-l"6(i6rAnk26kP5lX˽w bJEU`P-Xȅ%ETXra4XFB *!Ck5oo.%mÐ$@#/ L-};A!P?1 X'|Eč1jD,OA *umU_oæ} c{ d2VhŸ3eH.-a6tZԇnh|3LG#s+3JtOJ7CFBu2 HgJ J\Wt;ɒ,((( ~C͏.n IXiEeM-Y+bT}DHDw1LA !*ힳz:F[.Fa2DŖ_:6gҵ}%V_5\Y6Du:6gҵ}V_5>g7Ѵs>C9΍ungյW}Ϧtmϥks>k}6oh}+]|ۙmU_o3sG3Z>ϫjyM9J_!}[U|mFV3ګgl:6gҵ}V_5>g7Ѵs>C9΍ungյW}Ϧtmϥks>k}6oh}+]|ۙmU_o3sG3Z>ϫjyM VH#Z꿧꿧꿧꿧꿧꿧꿧꿧꿧꿧꿧꿧꿧 ( 2rkW_]uUW_]uUW_]uUW_]uUW_]uUW_]uUW_,E M xf~kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkh4AiAI 0FT*upappl-1.0.3/testsuite/portrait-gray.png000066400000000000000000001353161403603036100201770ustar00rootroot00000000000000PNG  IHDR+YgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs.#.#x?vtIME /8vorNTϢwIDATxeUtwwH(-!"v>؁(X J(4( $Ι{|?ϋw=s䙀|gҺ,JݟS&:|rк7I`h$*pb EE(QD DՂ/tsaIM8%Q Kw~k@-,iˣ$jbIeQ +~7ZXR’#tD(EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD(E@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE!) ]05̲9z<=#3###[E( ()7mڴdΕRr)yl޲IW6/Tvq D/.8I.Xi>VBm׾}I*e| mZitOsN$D:u.F,hl(Zi5Y3Ζ:6ij@-ZrJUf QW*bޖ4hL_ܗw6̜Fu?j~{0fЪs{}~XwBLwЧqZe%_Q+Hnn/_t[@?O?6ܼwB*IaQt2pShʧԒ%#*ZŽqu_ѢF$l{g|{i[ʥz90;!6"Gny8]wf+(. oy߽{4 < jcwwg[;O!5kX_\A̶S} D6_4 &h˳7:d[ M*kӶE im>IGs[.Z1x;} {h>S胎z,VjסQPͬTwfQXӬ㸏(J!.2ہ#34\8zҡYdy{#"+rNm~Lz?(tϱoϤxg>h~|9MbZoݴjz>^5Eo_w/ɎxGN3~+Eb-3c9n}#k38s;^rɒ8IC<|'{^vv~OqǷvXg_ׯ﬷~f(l _L٥o'Scu_3Hw'ol/yUO|@jn]|Ky )Z_ ]?1%xnWlEg{?i"xݧMGOCyrts?[-[ZZ+]z/-^xWp̔^=.j~kgEmgVZGj`Y,PTf+Tw[:5[Cn֬A7mwځE.{㟖b(}4㲋=-kDqN޽l,x 79#%N\f&5Ãw9τ|YDQ|UMGٶeW\H{DѸr|D+ !rt/k)^"SLODàu"=ty-L'B߫W%V)LF韱& & nW^s&:.HtZXWULϡJ$LWvǴpӚ݇ڍ>7K";":Z`:_K}YM{sI啥B:Z -&EF'{8Wؔ(BD)?0OӞ<lr1+ u%DE, C&ja.lEU_~rSlI6s %2){X[R!9Ӕ{Ef0=WOE(uw"%=iJ"SÄw;%{ENg3S)?=4(O#>-oݷcx=D-Q\ jTH/sׂgHtD:Ӕ귝N,gt {EAGjd]܌(c랦~a%L7=F!"x+['F&y*Mt@{MCwY@o(x@S+% z1ڦ95ZŀTI'߭4ꈋdTV0"ۙ]Ҡ52C7mܤ˔I'4%Ћ;yN{/6q;"!iPwt钟eKuZ+FfztGtga<"q] qf.ݭaթNƬ4(lsC)R/y@vFFFFfFƩORA)UN|q;Pnx;,+K )Dt~ÿ\"A=&KGs^n `^&"i4"YQi$n>iJkG< U;gzY)9.%],U﷣iҐfEJeIvyR!(bfECTj=ܚ"h7'~E,hd-c]=1`nm|r"o7nLFA_oy|PaG!`e->a TAg'xU,qۨi&Os;e4ܙ.BYժ~Ji(a JSӚN|gƞxnD zygoPvkȥ 㤗c"@[778~8UWʧ aI/"m=:AަB&^N4MwtI "16P1~4'<&zgRFM܌NfB ?'"0 |0n=G-;+333#333x}Za 12FǂNA8t[x|'ʴiRs)QC|wȄLEB5wQJϪ"@KߩxWR(KjRu~JV:(6qu]ݴW≬!S( =}TgY#={U|NF%Oh&[=Nz&(44NiS(iV?mR>ZI{D6}<"&wPVɧAFoP(dnľREٌ DMkK,׋6'Q'㤧]v]"WA_'Qfʲn_7I$obŷ-{Ctn5k؟P( {c:Y?"*=u_fbpzskM,^H|j@ 'Qu).-kqhUvJ@fIT &R%E7gJԛ[BY^Yu D,$j13BY_("X_3m)BA YD,hM67NPD@A'KI1&BCIDgYTiGge 2S(h >$zad]ܓT D,ee6è} FIANpVYYoydȯJH `!kn8#uD"ɺdM4NrlQ6iY"?צN2O"<*-m!yQĠ `KoWC!&SdT D,bkwE甡Nv3S> QqY&3R'I}YަeQK혬IX?#6u4"XқONNSayTD,`Wò&7NFhp81"Gިw0% hbwC&w@LqzYӷKE kUcv<"hao&LMbpGFDf("rٮn[=(u/kTK))5*q+"@}&53+u\J ~yQGfsI?g2# %KץCu}_.Smo* &;}7Ek_Qnj!$>t=m@UA\G6f5֭ŵ yؾmm7=LEA\GvB+W(:+-5=5^m@dS[9y)mE \iKjFREL@sj=N @0Sς("& EQL G"f~?F[ ()"&#Ev8 k5fD qZTZT1EQ< J@PgyY9|D4UQ ;erz+<"Do4hԩ;Q7ezh|\5?4)+/[Ӊ:]:W Wۊ۷k-[ŋB6nҸ(ųc)|vJxf"{ݓ*i[rQ4kj"TB{MJKKKKKKOKK"4$,44pbb‰*S*EUpK33@0G`CE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((E"QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD(r&GDaaIsx#yO<P ȚNg~`Tj07SQL FQ 5Ee(S_GѺÒ>}b-jaEK’V_@ U%m?uy}XRc Šn:~?A-,is_AjaI,B0tD(EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD(E@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE!),!7Km夥gDFFE&N=vر̌!  /PXE"iCiiiXBIgڲQm9qE 7͛w={&_N /[3*8{׮]>|a+8!lEhabmټyYUM3mJUjʭ =!iua54 ! E{i{GkUp@Ͷ lfMT6"C5մ-FU,7כΛWmx(vS@e%Wf&"xpr/)TPiW|{atuy9|Amj9TP }q "\<|Cig)Vނmvz 2Oq|ΖҿK~>ؑ("🅟p/4%cۂ"}$-+)_htEglgP4;d(yMLհc8 xaMFQD6Q NԸc"f=|"^8I].OE6-n&v~("\췷)t4+(roSED֒W(tZi~ȴIlEilFDw6 mRDlDTGz&tD=d=3\):jv U)zp mME6jJb=ҫZE.G y՟DQ> ~J(vvALOJe*ڴR (]__.a"hhW7Wt>V%{=wpWN عYsSvV!"w}Iׯp;bE!Dn~X!mhDdoVC`k"-%)N VC]vTHMSsFI.F)٘ZA/u(y.[!{Elen:DR,cM?wT#Zdz=P>VWE EDZjY:TvVNk)?ADyzwO'BmPJY(ï6 #?''NG%,8#v(Q| -hɱ A!9!+oOid;(Fl.%NQqˎ4Tn%r=csmdEM4ke Il+Ht}0*tɕjQ"HZjH;"DQͦ$~]Ȧ,f_@~1A=ءv9@ǩ"I"ۚ< `|kJ(ũ*H(Tw>N7]CQ渶r[`Z89_qr]Q~$uHJkyX*QDA!~R|{(.ⶭLɝm Q#](-GA~2Qd(*N}'gt.Z[٠=EAm/}ʮ1{͖(yBb m \9V]sx4mC9-D2O0*Rߥ&YM/V3|~DE"AqЮ={)^h bMGN(=uǦ' T*{;F*'yϷ  '7bsEoT_.4jT8҉6kOަM^a+5gv Xt}EӎrH)HcIDʕ"ε=`CF'n-ZV}Oh6[U&)LKwynd:*Z;ԵK.ZӧwM !N~S|+x|;֪1oi\*O/v 5Z MwQMTyeEw Jk_%s" k]_ g)9_0I(߼v{R(Oi/ͮi8fOqGѰLN2Bq޵>{쾵uT,3",]mwo8IN3&/AQN_GM&Kr}OH.v;@'?ė^JW^׽" _Υƚ!Yl*t%Z9\|H!-}H Ʈmcmkmq]|HY>TG6L5*S#-^$Gb(mÃ-nGсeȵWvLcK|fvȶu=&_0)@HifHoK[Mv(D%}5/i+bT젾-栅lk;{<ɍ Aݩ^v|n7fCo%jn;fMߥțeskJn1Im+@ըsEG>Rܿjt_cCN"6k9_ݧnU26Vdvyoֻ-ψ"][$nQG][%Pj=1r'y #XFMmrQ|!A(꤮ ɍC䝢ĖH'?{nB+Tj &5p~}MN2E+쨚dz2׮% "JEB ~9J1E1"H=%SK]Ot::x{UŮB[ (*&cH(m;jc-1Ikd7E?{Jg0&(6EEvTYy:犋dzS}:J6+mPWDQ[H7oRJ4(CqFr.ӫ lF$|&)mLꞢ+2\5\ުE+JXRbNQ$dCyE>{f\l/ݐ"Dr| OJZ!AK)G=qNO OIHqH9fTGG%7-OQ$<%gNT$"Z'_vTMʓz,xI`ZED_A(FI ?#0Z}׳W8hkunWEIe=])HCt4U3t<j/C\ "l@6~Q;u GM%̲EE(N+Jm?ŕY}8Qd"ݢhR"Q̺vꎺh"EDHW3ˡ%UjP$RZ_}G7C!]wU${Eyj;ʋz![FG~:^t! $^5gs<]XaQJ3$ ZQ$(J:DF,*-z)i-"FjXon,Vg"VnzʓSٍl|H'JvH&E{Sh4QduEvF -:Y ;t."WUWD-L/ ʒ fFr SE:ɝ.kE EQW]hQ$ 8UWHrϼt5lۣ<òr,gix;vC5 "9B]Fը>"].WHRZ(2zٵOcO^@vl4(٘"E›G)#L{E~,1cy%΃XF.ǩU^{Ez/'4wE~"rB#ZzػB #E23!ub#}d/Itf?dٿ'[~J8k}H/oAt,TwVyh8H^ ^!yqI}Iy(?fт*]`/! jwr%{E8uJT':M5*-ɠ?"EL8$͸(ھPe?$E!_5SPqa>;E~k&2>K Yjr pjDv6|rSWnSU4#Idꗞnx_[Rc=̓Opk)*zI~M(Zfs/"ݼFMYK2CHy6ziNe&ߠN]vO~ÿ'kXիU-_t kJ w-k*|XzX|\ 5E]Iݯ< \>nIu,_[>$sJ!"V^|?sǶ Rv-+n#Uxnm/=8eׇUtƹ.ctn.՞*kg];ϛ ݜdUXikZpLB1bbCRSSSwnۛM7C 2h 86n"؊k-܃5B8b`1 XrKmOGL(Igg.hϪX9oH٧SݏSԖ4UhkEڵgCq(q,6nDm'Q#ʫe-8.jiu|+U,yBvVy pyiŚSpe THF(QsM;ёAimVEa"h%t|mcZ"ؤmNR3n*;0$fޤV67HƨW[cC\Eoc5НUu8 !et[=*dd"WGQ(YRs:mқaNA7\[={`]Eoe%T52UER% q6_ŽQ2iMzlAE_ɐ8G4uqX[jd-* ЫvykA zZvu %Ūjnw`CTQ4tY ?P$c mCwuM\E㻯4?gcv,KER5G򫃄x0nI⩒);|Mz*>>2$2@CXH?d-,QC^b$Ǡo-lWTjWm7D#M??k ѝsɘ)kQU2o+'jwfvMotZoaZ5q_6Mt@CN4[`M9[+}S&ηknnf|[+M;]֢5TTuZ蔏AVCC{^`w7l>v@Æ ۔N{nLgZtAEf+1Wdxvl;~m4IGw:\kwq5 Rwj@NqScD\GPUrx+Vr* NZe$ z&}3Q`Æ\X[&P-.k1*YBX˖Ws WxJǵaŸض۶,芕*U7XY[m߱K^zu#~5$D={|V@J* E"/䉓'NBÒNP%|b%%d~`Tj07SQL F'[&GQj07TWbXҺs_OlijaIrtIjaIR.JwQKz/wΜ7Q Kz/كZX/ۄ 0Q DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ D%E@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((.i(;/44'Nfeeef億-R  YYYiD>u3YYYYBGFDDDF*[2oqZHъYK/uժvGEZڳ{K~_Aʖ)[+=$8lV3IھmG~aUUK`Y(I:n]}g4GXR%{8~Ym۬\EWo<9w>!"61F"gݲ]&6R-qO;M> !w tl &*w]5^scI̪({B_.=Xaw|}÷qBP.i[{w{g-42v[<ǫgm೤o5Ï y?{7rlzup )zTќqE˾ڎcq)`<^e^@EhS.rF!Wq{""ӎ}'~v?-5hӪnjMq$$?4SעGFp~{Syǔn]@h6\>76֫Bh[_ }T6"os@^&>BKh?DioԴ;nx}gRzZ?:U;;פ7rUfk?+q!W$汚He~7ۀRffO]*T|-n(JGgps9=.;0fk?w8SQt }NVe>8jlk?- ʝE]4[`q:;C7ӎ(‰?7V_e1m:ݷ K.\{-y99ʭ-g9ųo3#]483[ YBMߦ0v{ /GFQ3y(k13:rĨ3OVu?8|O]oe(R  "/[U^YЙU`~4hNjUc9窹(&~tAa.2(fTD?u89#=+8"2A}J;jXlE)M~}  oyz5Uۆ1 mQ:Zח;[9{|C]'/vm4[ Θ N~{7<{G.X+BCh4("5bΝfϒKۛjosѷ_:ʇ$Q֧fk?u)d޸'Vh0HE K ^k t(Ċs#ߜ0n+m1 *{/O!E ת['.EŒ7lLGE/eQz-kEWZ;1˸3V#qgI=](+E<6+޹/*i-C(mQO;6s[vC e)0rve B)mH^!әg;rlm'KcgCBfWzY !V_ͬB !UݦXF{{ '-wUTl`'~J>j ?Qx=_&(:odz!ۻ%>Ru A۞BÃx5d=%٩vْߴEäO4oݖ SD'wU& jgcSrv+O Aު~]sͪ 5WKƦ !N~D:''+d<_@U]񹽒*J:(R7?4oݤO UfѭPǿ?Vʓ{4UAH QsOBhy|NvHzFxcј.B?&~Qs\l Ǚ_{"ٵ&f(ʓUMrvY&7Bӽ*eY~.?/Bnfk?G& 5/] Ȯ#9IDT=tjW$9=\~욥=Y@ILZ#3inPߞRkLsd"v!(8R%!T=$W-9|imzdIk~QX/d剢s$Uޒʷ,)aI;@'{$m:h MU4;1`gvWzױXPvgţHrN-AǬ<}7A$Tt惉^Q$:8I/gyuB1[IWta'-Y(Y6&ţHJʓ !Jxv@+*^Љ=m/$.2[9Bu5 %tވ(\uG"5!^(EQd>dquQ hs%Μ%W~%e"ɝ{' {#diWOP^b@cӋ$2:={EB,(^5'X+s>+ַ!V7"n{W8XFۧŪO(\:6G3!=CNOdr;($z7936ü7h( _aCv^z{E*!|KyOmEHQȗ(s[y/mܼ?nR+LjpHvG#i宕-2i\ K'kb]ũ^?UJLr5} {`wKh, gFѾ)[Uj^-ۢ Q_SGKMy$ k2k >(o"UCφkl-(0U\?+t4/ oFgr\Z ="uɚ;z]vP\8=F)3Y6(O!6;wlT=_|4+nf(rޝ蔏b(:Z`2J+urW0_dk(SYŸk,y;ŌpCm{w"tNKW[ :ߘaEw=Mt7DƉ"kuչ &,xkqA(*@Y--YVB _[4={WzV+*r Qd];-wzTz+^#2iS Wda%$,+hp6UF1ȺC ,,5JVJ F~j07(s 5EQ`sG7G$R +:t~S K:wb%ͼ<]KM,'V4=7{ƹE-,iq!LG"QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@"QD D((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ "ņԴԴL5;$";(2OR E]5!75-}pŊ/Z42,<4<,,D}6I%΋qҞw_rR%lL-E #4?R":f`܊w(vAI^[;ޏGY潴FJ52kGje!/yAxBer~b+rz1uL~خZ@4'O{vjvyQT|DqCcu遝8n(*p) ;J6)Q2Ѿ=zѴ̌̌ȨȨز9gZǎ߫0{Φ_BKW$m"5aHk pk6lE>hGwcRfؼ-m^۶o?uΜ("5S7nLG}X$O ͌_>Cn3vϪ޶?f#'--u QqG6zN+BLMRX@T٧J9;(;~D6\`Ս3pcgaef-'̝yd`CpK7v|4 m0|/5Q~E3>z5,k޼T5'h4yeM{da]Z[o4x1D7g-ϳaN~9e3<"p%}܃3*_ҊpHQճ;VKD?YE^3M&dXnFGQPϩz͔N_td/ͪSI>x^Vr/J <<"H༊Jc4>tT*&} TA2l;$ R~*B-S-:@j:í;+ݽN ("5:=jL\#)Z-gXQUY?@<=5;5QtkU~l\K7fƋ@$U{5+OOKtnl!tTrkHݠs6rzNãMr[˧ \=gjL4[X,k=5q? a=4%JkjИ:, >^+#ۤ~EoM/,EʓO8hM?ZF6tպÄ#@$B(ߐ/7h|j+i]gaHd^"E+a>'Je~Γ<2EuY+ReIUV>Qt#OF,;jUVqr򫫌]@0cuZ[yIj}lcR<>3>35#z- c)9dZQd"cHU3>P5QyҊ9F/-dEctak59pM*҅4|79{E^´(J"~.[>\- rEzY\q2C1)s*sv C΍ڽ.]R ;\Z>W]0+T!N3|\v3N蟯929Qi-\~vx (aMˣH{x輲˖Gc\>Vt) [QWϾ?U WԸ w+_G_asS\EbKKj@K/]8t_z@J(4dUYj-2adFto[$R_ǮJS_{G&|g:x|Bܞ žxqo꿀8W䥯4]7!5+RS; qR|r.ƦIJJJ:z;(kOc(?D.ʴ&1߄L9/7w)y_Ӿ Rw(dLxxXXxxxpYn]>/~?~ 읕W!f/?Q\!ĉ+V3N̤D"UyKW IS^!biuVDr^H}H!D8YKW{V-[tqWOJ޷avƐ̧B7x08غomW'v钻erSG23wwy{EWTnGJ?{8@w)ߙ=^$\W$ G_mx.JB n 2߰mqQ3;^~;"X\Zmm사mzK@;H8WU{o ŔTb ѹ7-, tի'2,hxVSNiux|h^wEezZL+*TZP);(Y@#<{ͼv3h񻇰zn1+M- X>e_j;l|&=Ԥ(_hҗ'pUhxC_5;`K.ݧ"a (FN8k-%i!WtqmP/VNgFCQvI񠐐АШ… RwWPTFOSF2yӎt4\>Quzs'xHԡ?W'Pܩvi[(˅v~C>KY:Q$?!VE(=_%6;fB"(rVZOe4t&i<2Ys2/bx}31Q.v80)NibE֐d)`<=1S>zp)qH"߸1"%!m[>pJm=NW$yӯzFj(2egvYF1tqTz]ƣxDj0wgNyrb1@^=7k(*lQU 9u99h1Ǒ;EBll57wXsJJJ+z$&),ȧj$ϙ.yz4L|(QQtjN_:\SA:}q(jTKFNm'/7u燢5m-lc͚ѕiⅡ=EU/S 44̗3)`4cS"#"""JzDk@mw<2^OYXG~CqmP8δ 9rʕ+W7)m"(+wD,YF㒈" ]G'Eo8i:vPgH!8~WKݴItt J+WӤgk%}}Dս}4E_H=-屔vb݆f^E*;m;whs=O~6Eְ4.=NfuMzOEG*YF""c>h0[৒7yl%屯) UӰ5O2xf{JbȹEц 1 &>>>>D)oFO~Myl;>b/.jd^'K5YrЪH:!gB+TT;VϝtY㤯u~TE5 |bLUuU| W Iچ|j&<6U-[Z qVϯՈ"d/{j~L{R+-qҷt! I(vTU?KO)O/dȧ!<4jVMKR)_U㫖Pg\XWP>Or)phQd_wQ߽(CEEhdz(l$y7Q*)Jn;+IZH:E 3k(R}&"_ <_BͲ\EEvCypЙ.gIMEKHր(Ǔ]'r(RE:}B 2%v~9@+ɏYCIFf|-maW$?^{J""-Q"%E:9<ًj)DIOP$3@''Dr!E~nE?kP&%Q~$&7SHB:]45<=(A((7+0#|&n!QuGQDIFdkEzEQ EEߦcP"B(+jt6 R)KvhEDE oE(v)?a2~B$ ED`oa{Q#S\Ge7$77E~nqPP7"ݬU|n*O', 8Qdz)jWժ*ʓOgY"._V>U|w;$T׍@!"')IiCT"?W~m wE %v*,k@Cr.NdEk(i(<֚(**;EGކ(<J[EعKyz}>ZJEuWMmp໛g;F1[2CSY!$ vEiC6YT ]K/Be9í% 588E_bHy>ўvQj׃}{iZ"?WeşU//dQ׎VgEP166⋟zy;?`cו}ADV-Ԑepf#Q-.\8qENPl#+/;[-{tJJԱ˽\dȓHEqlO!jtkz.E'RH[ժ سz!u!:VU VGfnٹsCCu?)[i=!zD88ZIDAT_^NhVA#M |{8sȺkzuVJXzBٹs6BU"Jm_ux亙 /;|#?y3.[l!*}pgS8Xx强WVPd͚BdKJINMNIII .[WϔJӓoSѪvDBmڲG"=|y!MMKKMK (uG[w"B+Q o(YxN"Y͖ .^A~09@ iS401ѫWkا`8T?J -hc^GG!"?tj(KjwuzDN MaLnͬKc D϶͠fAK_r`QΎɧo2 JӾӍDGٯrɏ~G3P wWSm͛q$l`4Ʒ1DOD࣯zj͜`'|uE|4GOjE+5Ez>E|w@OӪ"w?]NWeA,^0E:8co~>Jo3rR= _։"fE|sr qmΦ*^ה7Ny!yBv:!*n߈zI88Ve4a~79 4oPP#d>6ifmGQڵњtvтdp5kl?y즚FY>.g8N9]w847w]G©fs -1M|ώ?Wtr֬򍫔ck J4V%|}y?fPEeOyc/^2󧣦c7\gTj9N[_lZucK7mZ-Wl]W4Cwhn/j￙`(ځMDp*U*yݵsYF, metہbWX1V+v[+} Z>_&ZFvV:sxr@B"E)" ,#Gs&?"L+Q+~Dcw|ބ*V(&?Jݷw]ָEj Rj=s߲y/xܮ{E~m'd@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((E\.s ~R +8|Y>]@C%e !߹rөePk/Lj`e$(O FQ25E1`ns(LubhaII[ /KJ"k"{EQtEaIxIjaErh#y@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@"J DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ DD((EE@@""QQ0]0%MDDf&''gP k L21111111)))))GO0* GEGEG$$r (Xvؘ NNINN>qî|cUJ.UXdDDxK>{ٳߚCiZuj׮rOoݶmMzͶVUϩ[lkU(I .ܰzԍ?zV3]vbW%$Gڦ䑧oqCZmoРAKXem]a:H!Du .[ެze:^۱x5sZF&GE+-o8jzxao1z.6UI+-z8̢fԴK[Ӽl*mx~~Yi1Bvm!i4%fo)3 yCPU-YxWKV^Qv:pX}[y7= {id۶gfN[Ϧ 6u4nL_Qm\Ɍk9XQñotQT{ٛ+ y&]lӷWne紟d.,W껡0pH jÆr|+CW%[hN~e$,x](pyM_cٜk%޼ˋnr]#%_~%_V/E7Bonn peoHu1F}* ; BM4ұE[eK7Ǭx83a RT/x>֗UrM9bk.4~B+N#EQsOake> ?o?T^@aJ {e|%__[on9JWEͿmMh7.*G]X_ʊW+NogTr{~kZ m%_ {qWN:EB!ů5VWޯE/=lv}{;4A%jQ5ͽ~ͨOe9#"!Ā,o cY\FE7/8{~>QT|l7~䏽~vV-Ro⽻/꺦Ez7DOj[7f|jM=j㽢 ׎vEA)_ڪniMf9Y$}ǯ=qGl ۴ϝy$44|bq-kh5*s8SDOwG꺀*y=]uMw'꺀nxJ޺|,jnFOj 8g(Vzb?־gg7z_ng}~q}:nfc53 (*͵VnRggX`qkwP-=zmBJ6WFF(j]%|8=[σusY KշK|mÁjfÆ;a/=ccy@Tʆ?s6:PJTPR'C龀J4L{r>~?qv~ 5Qb;G| gq\ԛF=Uwp/ɘ*yU_gGQ\DNWesu]W4zY.owƷUؐ]Mzeعs\a*6dZ;8*}UYEӧ‹?T ,~KuV(lkOR2%o(jb3w9㛆,2[b=Uʕ]ĈֹIt݇Fz#?p"["%jj귮:gi$u_ *9s&%,>D3ՏnXs牪ζOzuC޹u梄ǿsA 3wט1_j=Z;6}؈ī9+{.;}nRvY ;.5}7U[ TyVϣnU3ϭoh֬/ɟRZ4GQ˭)߹wbngМ`)B:¡b?2.[T)ʟʉrxP~.Hr~^~ž䶯=i? ,u}_+'Q/~aI"S&lOd*TpnDUii]}):/Q){;ȥlzPLҳׯ/௣fEi׭ʓNȇQͬq_(?]^8O_g=aJQo-8Ŗ*\yv#aH:,SjU={yiQ;}+@uB_7rnʓSdqK6/j*b{t%8jѤƬvͫV)YO*Ž^y՞_V+V"wI#;]d[[=? 3r,oJUt]ubC'ߥuYO{M[qr0٦Zg=![8]UGA{u?%ō+QЫ6_rq5ZGa%QB*!-g?5rn8AtD T鲋s_jXN(x'H6pֿJgOS~q~ \Jq!2߫R׺j(~j}{JY*ˑ)I&fpNV u?/4kUkU1.U[:j-ʓ,QItMl1GnWmL/p]h&mI[w6;f}XfQ$9cWB`4;QtKǡQ$>+hP}CVݔuș 㞛鷀K.b*]+\P~٨ UbHEEA{T>}LyzgFQA.,򣠖8@O{;+DA.,@(ʝ<=UO[e܃<ɉ Kwۮ`-(k'Yj [IovUsV]gFHys,_=|6^DS_\yfɺ. H~o(b/ɉ W.vS$tI(!F@t=!5"Ό"N%S!^ sW#/[ iwxulp_/`i7I\@|N Hiӳ-YfV "ǥ.z+ Eyɹ^'~҅꾀ޗއ{b+DK^tS.ٗ^Ld(BD QS}(u{n"\vkY`FN^+>҅E}鿀voѪ]]]{0k^Owz]趀$|HFv|]J^ڪgwtnS5kEyֈ<g'[2۹ *(/EKJ ,i0ׇ ?j޲rnsf4R5$e9/{H9VӅUi<߭_5}&b}4EMa_F5M;Xe HEv${;B}NQ9wI]ћ<_n^@ .X7>\ sj;@]3^rz{< Șt&^;E HK^A )F"W%? ]r/m^̟O8EQ}TC)TE|H6[.fGQpރ]_u(2%OcdQ_YGQ_ףU%5L>W[yzn߆e>Dܙ҂'XGUKFKF_5+^Qo]|i[!{OVkFU2J(A"EQdQN (Mn?ٶqciw+Ÿ}Exڹv\*O_t$~qb4 =Q-yn.ra+ҞJv{E$>&N;*ʨղuQ{Nqn.d-9cc -*+OOr_|ٟkN=HENfY7$4dM^8)G$+3f`&rPg5nou.Ԕti:jf(*5]<sZ3Dej7V~z65_6j*iOċopY<)REQ.o#Ȗ*4`HWiT^cv}/N]0Ѧ*~9@HHܹhuTT;o;>S|UxgT$T{E(t'4"VTj&^?/Iy0?QW={m]WMW(P"+~fQ#t%Ug{sI}:E|yӝԍ0&H" 'iS&EMԵ˛5]ZQ֥<ݟW#[$w^3=Q$gIH7uSu~5VGv_ef#?$wNїmһPٛcȘDBɞ:8EKѪV{>S]#dLOm}h__}hDBMVv'0I:YS(B's%~j~Uq¨fF#(?Wu(8,C6H/U a=ۢkflr!վQ$/7]}(SُQd% xzn +o׻]5RUuB}d_۹Q>;w<REjP'9Ȱ# ^0yUfX %={7@+^3";W6+-dI'Zd=Le~ ow{/*E;8^DiOYDWd7P3~FzyZ<}'lXFfXKtzڶSZweE}~ћˣ/IE-6$ů4y"M#Y =!(_U:/ǏnsAz(OyjGb\?| 2@-AE r4dԟKuc$0-E8f=&Hۜ~w1I+kLxKle{IB{P+loʬ~nҡ7/Za*6%TkXEqjz)%"K%iܺA&I|%y^:멳r.}DZ w`{/pt|L'ѾNm$T5Qd !7R~y"9>7k#Otqꬋcz:lBid'?iQ?\ߗeRES'GE$ xPn(zVEGTZGy'4qi vvmKYURu!Dk[%ʯ^$WZmk% iEMQaZ:IW{G[TVKU ZJN52tROq~c91&tݒ8*K)d(+2@ SgEDe WxܹW-4+}XVέ9|IN86=Ȫ9 ,/5"M 1@I#AEhjkbi2N%h!ZQkZAĄ.hyYpewGLLsf={~gE q W(?DdUswioMyǣ]Z.uPcS}*܁odgƸ&kr})P"U3G?,ҩ۬v_WHm_OjdY}ԯQhA#\UC *jLtWW_~ymoFQсKL?/]l$tumĜRBEpaץgIW|3EC]7(w \\pEzн7f*xb}k\`֠Emv΄Й-0}+'R˂cx11·3E_:{b8><kn`ɗZ4u?De}dGBEv JN׳~L~cu䯥c'{s'P#%^1^>mB!c6[r^Q}A۶ƓgɌ ŷs\ݳq™(:3=k{-,`TnK5W)%q'm\'wۢ v3O)֣Ld X`8(XbؒN;mE*meh`WD)&:`Z{}aS$oco&gE8 Gv(8q̉2.Jk綟/`Gd?1hЕ;v6uҥKϊ~8_%vcuv,m`/wK1y==S[4B}$8M^u2T)fvqGxʴ\v(&d/ T]ovl,,fO]F/b_(4-Tb4kOtM> iZmm`EBA4H'wmPD3WEUI |( mY;.)BMwղeBF(ƫwFS <(Xp@CŮQn]aSe74Bq+j^ȼy,]ӌyX{cfMGxHnvTX{\֡h\1%猬9[tϖ7'(:*8 mzg?4n ({^~GB9nͺz̥;OAW.RmN+mzC˛߰aˉd_vV~Xy^`mk֦l\]0#|QݺUp*DQWQ-\WS[SS[S[SwSC _(o߾{٥KYY\.rh۷w^!xr\AS޽k[mݶ?UocҬygx\$u}LQ+u>CQęKvԤԾ>DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDQQQQQQQQQQQQQQQQQQQQQQQQQQQQtiiTjgV5鎤:m=Qm:Ddh6 Yok2O%[;eXIfMM*V^(if,,+BM%tEXtdate:create2020-12-03T15:47:23+00:00o%tEXtdate:modify2020-12-03T15:47:23+00:002putEXtexif:ColorSpace1ItEXtexif:ExifOffset102sB) tEXtexif:PhotometricInterpretation2+tEXtexif:PixelXDimension1674?tEXtexif:PixelYDimension2091]IENDB`pappl-1.0.3/testsuite/pwg-driver.c000066400000000000000000000671531403603036100171220ustar00rootroot00000000000000// // PWG test driver for the Printer Application Framework // // Copyright © 2020-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #define PWG_DRIVER 1 #include "testpappl.h" #include // For strlcpy #include #include "label-png.h" // // Driver types... // typedef struct pwg_s { cups_raster_t *ras; // PWG raster file size_t colorants[4]; // Color usage } pwg_job_data_t; // // Local globals... // static const char * const pwg_2inch_media[] = { // Supported media sizes for 2" label printer "oe_address-label_1.25x3.5in", "oe_lg-address-label_1.4x3.5in", "oe_multipurpose-label_2x2.3125in", "custom_max_2x3600in", "custom_min_0.25x0.25in" "roll_max_2x3600in", "roll_min_0.25x0.25in" }; static const char * const pwg_4inch_media[] = { // Supported media sizes for 4" label printer "oe_address-label_1.25x3.5in", "oe_lg-address-label_1.4x3.5in", "oe_multipurpose-label_2x2.3125in", "na_index-3x5_3x5in", "na_index-4x6_4x6in", "custom_max_4x3600in", "custom_min_0.25x0.25in" "roll_max_4x3600in", "roll_min_0.25x0.25in" }; static const char * const pwg_common_media[] = { // Supported media sizes for common printer "na_index-3x5_3x5in", "na_index-4x6_4x6in", "na_number-10_4.125x9.5in", "na_5x7_5x7in", "na_letter_8.5x11in", "na_legal_8.5x14in", "iso_a6_105x148mm", "iso_dl_110x220mm", "iso_a5_148x210mm", "iso_a4_210x297mm", "custom_max_8.5x14in", "custom_min_3x5in" }; // // Local functions... // static void pwg_identify(pappl_printer_t *printer, pappl_identify_actions_t actions, const char *message); static bool pwg_print(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); static bool pwg_rendjob(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); static bool pwg_rendpage(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); static bool pwg_rstartjob(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device); static bool pwg_rstartpage(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned page); static bool pwg_rwriteline(pappl_job_t *job, pappl_pr_options_t *options, pappl_device_t *device, unsigned y, const unsigned char *line); static bool pwg_status(pappl_printer_t *printer); static const char *pwg_testpage(pappl_printer_t *printer, char *buffer, size_t bufsize); // // 'pwg_autoadd()' - Auto-add callback. // const char * // O - Driver name or `NULL` for none pwg_autoadd(const char *device_info, // I - Device information string (not used) const char *device_uri, // I - Device URI (not used) const char *device_id, // I - IEEE-1284 device ID void *data) // I - Callback data (not used) { int num_did; // Number of device ID pairs cups_option_t *did; // Device ID pairs const char *cmd, // Command set value *ret = NULL; // Return value (void)device_info; (void)device_uri; (void)data; num_did = papplDeviceParseID(device_id, &did); if ((cmd = cupsGetOption("COMMAND SET", num_did, did)) == NULL) cmd = cupsGetOption("CMD", num_did, did); if (cmd && strstr(cmd, "PWGRaster") != NULL) ret = "pwg_common-300dpi-srgb_8"; cupsFreeOptions(num_did, did); return (ret); } // // 'pwg_callback()' - Driver callback. // bool // O - `true` on success, `false` on failure pwg_callback( pappl_system_t *system, // I - System const char *driver_name,// I - Driver name const char *device_uri, // I - Device URI const char *device_id, // I - IEEE-1284 device ID string (not used) pappl_pr_driver_data_t *driver_data,// O - Driver data ipp_t **driver_attrs, // O - Driver attributes void *data) // I - Callback data { int i; // Looping var (void)device_id; if (!driver_name || !device_uri || !driver_data || !driver_attrs) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Driver callback called without required information."); return (false); } if (!data || (strcmp((const char *)data, "testpappl") && strcmp((const char *)data, "testmainloop"))) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Driver callback called with bad data pointer."); return (false); } if (!strncmp(driver_name, "pwg_fail", 8)) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Always-fails driver specified."); return (false); } if (strstr(driver_name, "-black_1")) { driver_data->raster_types = PAPPL_PWG_RASTER_TYPE_BLACK_1 | PAPPL_PWG_RASTER_TYPE_BLACK_8 | PAPPL_PWG_RASTER_TYPE_SGRAY_8; driver_data->force_raster_type = PAPPL_PWG_RASTER_TYPE_BLACK_1; } else if (strstr(driver_name, "-sgray_8")) { driver_data->raster_types = PAPPL_PWG_RASTER_TYPE_BLACK_1 | PAPPL_PWG_RASTER_TYPE_BLACK_8 | PAPPL_PWG_RASTER_TYPE_SGRAY_8; } else if (strstr(driver_name, "-srgb_8")) { driver_data->raster_types = PAPPL_PWG_RASTER_TYPE_BLACK_1 | PAPPL_PWG_RASTER_TYPE_BLACK_8 | PAPPL_PWG_RASTER_TYPE_SGRAY_8 | PAPPL_PWG_RASTER_TYPE_SRGB_8; } else { papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unsupported driver name '%s'.", driver_name); return (false); } driver_data->identify_cb = pwg_identify; driver_data->identify_default = PAPPL_IDENTIFY_ACTIONS_SOUND; driver_data->identify_supported = PAPPL_IDENTIFY_ACTIONS_DISPLAY | PAPPL_IDENTIFY_ACTIONS_SOUND; driver_data->printfile_cb = pwg_print; driver_data->rendjob_cb = pwg_rendjob; driver_data->rendpage_cb = pwg_rendpage; driver_data->rstartjob_cb = pwg_rstartjob; driver_data->rstartpage_cb = pwg_rstartpage; driver_data->rwriteline_cb = pwg_rwriteline; driver_data->status_cb = pwg_status; driver_data->testpage_cb = pwg_testpage; driver_data->format = "image/pwg-raster"; driver_data->orient_default = IPP_ORIENT_NONE; driver_data->quality_default = IPP_QUALITY_NORMAL; driver_data->num_resolution = 0; if (strstr(driver_name, "-203dpi")) { driver_data->x_resolution[driver_data->num_resolution ] = 203; driver_data->y_resolution[driver_data->num_resolution ++] = 203; driver_data->x_default = driver_data->y_default = 203; } if (strstr(driver_name, "-300dpi")) { driver_data->x_resolution[driver_data->num_resolution ] = 300; driver_data->y_resolution[driver_data->num_resolution ++] = 300; driver_data->x_default = driver_data->y_default = 300; } if (strstr(driver_name, "-600dpi")) { driver_data->x_resolution[driver_data->num_resolution ] = 600; driver_data->y_resolution[driver_data->num_resolution ++] = 600; driver_data->x_default = driver_data->y_default = 600; } if (driver_data->num_resolution == 0) { papplLog(system, PAPPL_LOGLEVEL_ERROR, "No resolution information in driver name '%s'.", driver_name); return (false); } if (!strncmp(driver_name, "pwg_2inch-", 10)) { strlcpy(driver_data->make_and_model, "PWG 2-inch Label Printer", sizeof(driver_data->make_and_model)); driver_data->kind = PAPPL_KIND_LABEL | PAPPL_KIND_ROLL; driver_data->ppm = 20; // 20 labels per minute driver_data->left_right = 312; // 1/16" left and right driver_data->bottom_top = 625; // 1/8" top and bottom driver_data->num_media = (int)(sizeof(pwg_2inch_media) / sizeof(pwg_2inch_media[0])); memcpy(driver_data->media, pwg_2inch_media, sizeof(pwg_2inch_media)); driver_data->num_source = 1; driver_data->source[0] = "main-roll"; strlcpy(driver_data->media_ready[0].size_name, "oe_address-label_1.25x3.5in", sizeof(driver_data->media_ready[0].size_name)); driver_data->darkness_configured = 53; driver_data->darkness_supported = 16; driver_data->speed_supported[1] = 8 * 2540; } else if (!strncmp(driver_name, "pwg_4inch-", 10)) { strlcpy(driver_data->make_and_model, "PWG 4-inch Label Printer", sizeof(driver_data->make_and_model)); driver_data->kind = PAPPL_KIND_LABEL | PAPPL_KIND_ROLL; driver_data->ppm = 20; // 20 labels per minute driver_data->left_right = 1; // Not quite borderless left and right driver_data->bottom_top = 1; // Not quite borderless top and bottom driver_data->num_media = (int)(sizeof(pwg_4inch_media) / sizeof(pwg_4inch_media[0])); memcpy(driver_data->media, pwg_4inch_media, sizeof(pwg_4inch_media)); driver_data->num_source = 1; driver_data->source[0] = "main-roll"; strlcpy(driver_data->media_ready[0].size_name, "na_index-4x6_4x6in", sizeof(driver_data->media_ready[0].size_name)); strlcpy(driver_data->media_ready[1].size_name, "oe_address-label_1.25x3.5in", sizeof(driver_data->media_ready[1].size_name)); driver_data->darkness_configured = 53; driver_data->darkness_supported = 16; driver_data->speed_supported[1] = 8 * 2540; } else if (!strncmp(driver_name, "pwg_common-", 11)) { strlcpy(driver_data->make_and_model, "PWG Office Printer", sizeof(driver_data->make_and_model)); driver_data->has_supplies = true; driver_data->kind = PAPPL_KIND_DOCUMENT | PAPPL_KIND_PHOTO | PAPPL_KIND_POSTCARD; driver_data->ppm = 5; // 5 mono pages per minute driver_data->ppm_color = 2; // 2 color pages per minute driver_data->left_right = 423; // 1/6" left and right driver_data->bottom_top = 423; // 1/6" top and bottom driver_data->borderless = true; // Also borderless sizes driver_data->finishings = PAPPL_FINISHINGS_PUNCH | PAPPL_FINISHINGS_STAPLE; driver_data->num_media = (int)(sizeof(pwg_common_media) / sizeof(pwg_common_media[0])); memcpy(driver_data->media, pwg_common_media, sizeof(pwg_common_media)); driver_data->num_source = 4; driver_data->source[0] = "main"; driver_data->source[1] = "alternate"; driver_data->source[2] = "manual"; driver_data->source[3] = "by-pass-tray"; if (driver_data->raster_types & PAPPL_PWG_RASTER_TYPE_SRGB_8) { // Color office printer gets two output bins... driver_data->num_bin = 2; driver_data->bin[0] = "center"; driver_data->bin[1] = "rear"; } else { // B&W office printer gets one output bin... driver_data->num_bin = 1; driver_data->bin[0] = "center"; } strlcpy(driver_data->media_ready[0].size_name, "na_letter_8.5x11in", sizeof(driver_data->media_ready[0].size_name)); strlcpy(driver_data->media_ready[1].size_name, "iso_a4_210x297mm", sizeof(driver_data->media_ready[1].size_name)); driver_data->sides_supported = PAPPL_SIDES_ONE_SIDED | PAPPL_SIDES_TWO_SIDED_LONG_EDGE | PAPPL_SIDES_TWO_SIDED_SHORT_EDGE; driver_data->sides_default = PAPPL_SIDES_TWO_SIDED_LONG_EDGE; } else { papplLog(system, PAPPL_LOGLEVEL_ERROR, "No dimension information in driver name '%s'.", driver_name); return (false); } if (!strncmp(driver_name, "pwg_common-", 11)) { driver_data->color_supported = PAPPL_COLOR_MODE_AUTO | PAPPL_COLOR_MODE_AUTO_MONOCHROME | PAPPL_COLOR_MODE_COLOR | PAPPL_COLOR_MODE_MONOCHROME; driver_data->color_default = PAPPL_COLOR_MODE_AUTO; driver_data->num_type = 8; driver_data->type[0] = "stationery"; driver_data->type[1] = "stationery-letterhead"; driver_data->type[2] = "labels"; driver_data->type[3] = "photographic"; driver_data->type[4] = "photographic-glossy"; driver_data->type[5] = "photographic-matte"; driver_data->type[6] = "transparency"; driver_data->type[7] = "envelope"; driver_data->media_default.bottom_margin = driver_data->bottom_top; driver_data->media_default.left_margin = driver_data->left_right; driver_data->media_default.right_margin = driver_data->left_right; driver_data->media_default.size_width = 21590; driver_data->media_default.size_length = 27940; driver_data->media_default.top_margin = driver_data->bottom_top; strlcpy(driver_data->media_default.size_name, "na_letter_8.5x11in", sizeof(driver_data->media_default.size_name)); strlcpy(driver_data->media_default.source, "main", sizeof(driver_data->media_default.source)); strlcpy(driver_data->media_default.type, "stationery", sizeof(driver_data->media_default.type)); } else { static const int integers[] = // List of integers { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 }; static const char * const keywords[] = { // List of keywords "one-fish", "two-fish", "red-fish", "blue-fish" }; driver_data->color_supported = PAPPL_COLOR_MODE_AUTO | PAPPL_COLOR_MODE_MONOCHROME; driver_data->color_default = PAPPL_COLOR_MODE_MONOCHROME; memset(driver_data->gdither, 127, sizeof(driver_data->gdither)); driver_data->icons[0].data = label_sm_png; driver_data->icons[0].datalen = sizeof(label_sm_png); driver_data->icons[1].data = label_md_png; driver_data->icons[1].datalen = sizeof(label_md_png); driver_data->icons[2].data = label_lg_png; driver_data->icons[2].datalen = sizeof(label_lg_png); driver_data->top_offset_supported[0] = -2000; driver_data->top_offset_supported[1] = 2000; driver_data->tracking_supported = PAPPL_MEDIA_TRACKING_MARK | PAPPL_MEDIA_TRACKING_CONTINUOUS; driver_data->num_type = 3; driver_data->type[0] = "labels"; driver_data->type[1] = "continuous"; driver_data->type[2] = "labels-continuous"; driver_data->sides_supported = PAPPL_SIDES_ONE_SIDED; driver_data->sides_default = PAPPL_SIDES_ONE_SIDED; driver_data->num_vendor = 5; driver_data->vendor[0] = "vendor-boolean"; driver_data->vendor[1] = "vendor-integer"; driver_data->vendor[2] = "vendor-keyword"; driver_data->vendor[3] = "vendor-range"; driver_data->vendor[4] = "vendor-text"; *driver_attrs = ippNew(); ippAddBoolean(*driver_attrs, IPP_TAG_PRINTER, "vendor-boolean-default", 1); ippAddBoolean(*driver_attrs, IPP_TAG_PRINTER, "vendor-boolean-supported", 1); ippAddInteger(*driver_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "vendor-integer-default", 7); ippAddIntegers(*driver_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "vendor-integer-supported", (int)(sizeof(integers) / sizeof(integers[0])), integers); ippAddString(*driver_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "vendor-keyword-default", NULL, "two-fish"); ippAddStrings(*driver_attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "vendor-keyword-supported", (int)(sizeof(keywords) / sizeof(keywords[0])), NULL, keywords); ippAddInteger(*driver_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "vendor-range-default", 42); ippAddRange(*driver_attrs, IPP_TAG_PRINTER, "vendor-range-supported", -100, 100); ippAddString(*driver_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "vendor-text-default", NULL, "Hello, World!"); } // Fill out ready and default media (default == ready media from the first source) for (i = 0; i < driver_data->num_source; i ++) { pwg_media_t *pwg = pwgMediaForPWG(driver_data->media_ready[i].size_name); if (pwg) { driver_data->media_ready[i].bottom_margin = driver_data->bottom_top; driver_data->media_ready[i].left_margin = driver_data->left_right; driver_data->media_ready[i].right_margin = driver_data->left_right; driver_data->media_ready[i].size_width = pwg->width; driver_data->media_ready[i].size_length = pwg->length; driver_data->media_ready[i].top_margin = driver_data->bottom_top; driver_data->media_ready[i].tracking = PAPPL_MEDIA_TRACKING_MARK; strlcpy(driver_data->media_ready[i].source, driver_data->source[i], sizeof(driver_data->media_ready[i].source)); strlcpy(driver_data->media_ready[i].type, driver_data->type[0], sizeof(driver_data->media_ready[i].type)); } } driver_data->media_default = driver_data->media_ready[0]; return (true); } // // 'pwg_identify()' - Identify the printer. // static void pwg_identify( pappl_printer_t *printer, // I - Printer pappl_identify_actions_t actions, // I - Actions to take const char *message) // I - Message, if any { (void)printer; (void)actions; // TODO: Open console and send BEL character and message to it instead... putchar(7); if (message) puts(message); fflush(stdout); } // // 'pwg_print()' - Print a file. // static bool // O - `true` on success, `false` on failure pwg_print( pappl_job_t *job, // I - Job pappl_pr_options_t *options, // I - Job options (unused) pappl_device_t *device) // I - Print device (unused) { int fd; // Input file ssize_t bytes; // Bytes read/written char buffer[65536]; // Read/write buffer (void)options; papplJobSetImpressions(job, 1); if ((fd = open(papplJobGetFilename(job), O_RDONLY)) < 0) { papplLogJob(job, PAPPL_LOGLEVEL_ERROR, "Unable to open print file '%s': %s", papplJobGetFilename(job), strerror(errno)); return (false); } while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) papplDeviceWrite(device, buffer, (size_t)bytes); close(fd); papplJobSetImpressionsCompleted(job, 1); return (true); } // // 'pwg_rendjob()' - End a job. // static bool // O - `true` on success, `false` on failure pwg_rendjob( pappl_job_t *job, // I - Job pappl_pr_options_t *options, // I - Job options (unused) pappl_device_t *device) // I - Print device (unused) { pwg_job_data_t *pwg = (pwg_job_data_t *)papplJobGetData(job); // Job data (void)options; (void)device; cupsRasterClose(pwg->ras); free(pwg); papplJobSetData(job, NULL); return (true); } // // 'pwg_rendpage()' - End a page. // static bool // O - `true` on success, `false` on failure pwg_rendpage( pappl_job_t *job, // I - Job pappl_pr_options_t *options, // I - Job options pappl_device_t *device, // I - Print device (unused) unsigned page) // I - Page number { pwg_job_data_t *pwg = (pwg_job_data_t *)papplJobGetData(job); // PWG driver data pappl_printer_t *printer = papplJobGetPrinter(job); // Printer pappl_supply_t supplies[5]; // Supply-level data (void)device; (void)page; if (papplPrinterGetSupplies(printer, 5, supplies) == 5) { // Calculate ink usage from coverage - figure 100 pages at 10% for black, // 50 pages at 10% for CMY, and 200 pages at 10% for the waste tank... int i; // Looping var int c, m, y, k, w; // Ink usage pappl_preason_t reasons = PAPPL_PREASON_NONE; // "printer-state-reasons" values papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Calculating ink usage (%u,%u,%u,%u)", (unsigned)pwg->colorants[0], (unsigned)pwg->colorants[1], (unsigned)pwg->colorants[2], (unsigned)pwg->colorants[3]); c = (int)(pwg->colorants[0] / options->header.cupsWidth / options->header.cupsHeight / 5); m = (int)(pwg->colorants[1] / options->header.cupsWidth / options->header.cupsHeight / 5); y = (int)(pwg->colorants[2] / options->header.cupsWidth / options->header.cupsHeight / 5); k = (int)(pwg->colorants[3] / options->header.cupsWidth / options->header.cupsHeight / 10); w = (int)((pwg->colorants[0] + pwg->colorants[1] + pwg->colorants[2] + pwg->colorants[3]) / options->header.cupsWidth / options->header.cupsHeight / 20); // Keep levels between 0 and 100... if ((supplies[0].level -= c) < 0) supplies[0].level = 100; // Auto-refill if ((supplies[1].level -= m) < 0) supplies[1].level = 100; // Auto-refill if ((supplies[2].level -= y) < 0) supplies[2].level = 100; // Auto-refill if ((supplies[3].level -= k) < 0) supplies[3].level = 100; // Auto-refill if ((supplies[4].level += w) > 100) supplies[4].level = 0; // Auto-replace // Update printer-state-reasons accordingly... for (i = 0; i < 4; i ++) { if (supplies[i].level == 0) reasons |= PAPPL_PREASON_MARKER_SUPPLY_EMPTY; else if (supplies[i].level < 10) reasons |= PAPPL_PREASON_MARKER_SUPPLY_LOW; } if (supplies[4].level == 100) reasons |= PAPPL_PREASON_MARKER_WASTE_FULL; else if (supplies[4].level >= 90) reasons |= PAPPL_PREASON_MARKER_WASTE_ALMOST_FULL; papplPrinterSetSupplies(printer, 5, supplies); papplPrinterSetReasons(printer, reasons, PAPPL_PREASON_DEVICE_STATUS); } return (true); } // // 'pwg_rstartjob()' - Start a job. // static bool // O - `true` on success, `false` on failure pwg_rstartjob( pappl_job_t *job, // I - Job pappl_pr_options_t *options, // I - Job options pappl_device_t *device) // I - Print device (unused) { pwg_job_data_t *pwg = (pwg_job_data_t *)calloc(1, sizeof(pwg_job_data_t)); // PWG driver data (void)options; papplJobSetData(job, pwg); pwg->ras = cupsRasterOpenIO((cups_raster_iocb_t)papplDeviceWrite, device, CUPS_RASTER_WRITE_PWG); return (1); } // // 'pwg_rstartpage()' - Start a page. // static bool // O - `true` on success, `false` on failure pwg_rstartpage( pappl_job_t *job, // I - Job pappl_pr_options_t *options, // I - Job options pappl_device_t *device, // I - Print device (unused) unsigned page) // I - Page number { pwg_job_data_t *pwg = (pwg_job_data_t *)papplJobGetData(job); // PWG driver data (void)device; (void)page; memset(pwg->colorants, 0, sizeof(pwg->colorants)); return (cupsRasterWriteHeader2(pwg->ras, &options->header) != 0); } // // 'pwg_rwriteline()' - Write a raster line. // static bool // O - `true` on success, `false` on failure pwg_rwriteline( pappl_job_t *job, // I - Job pappl_pr_options_t *options, // I - Job options pappl_device_t *device, // I - Print device (unused) unsigned y, // I - Line number const unsigned char *line) // I - Line { const unsigned char *lineptr, // Pointer into line *lineend; // End of line pwg_job_data_t *pwg = (pwg_job_data_t *)papplJobGetData(job); // PWG driver data (void)device; (void)y; // Add the colorant usage for this line (for simulation purposes - normally // this is tracked by the printer/ink cartridge...) lineend = line + options->header.cupsBytesPerLine; switch (options->header.cupsColorSpace) { case CUPS_CSPACE_K : if (options->header.cupsBitsPerPixel == 1) { // 1-bit K static unsigned short amounts[256] = { // Amount of "ink" used for 8 pixels 0, 255, 255, 510, 255, 510, 510, 765, 255, 510, 510, 765, 510, 765, 765, 1020, 255, 510, 510, 765, 510, 765, 765, 1020, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 255, 510, 510, 765, 510, 765, 765, 1020, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 255, 510, 510, 765, 510, 765, 765, 1020, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 1020, 1275, 1275, 1530, 1275, 1530, 1530, 1785, 255, 510, 510, 765, 510, 765, 765, 1020, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 1020, 1275, 1275, 1530, 1275, 1530, 1530, 1785, 510, 765, 765, 1020, 765, 1020, 1020, 1275, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 1020, 1275, 1275, 1530, 1275, 1530, 1530, 1785, 765, 1020, 1020, 1275, 1020, 1275, 1275, 1530, 1020, 1275, 1275, 1530, 1275, 1530, 1530, 1785, 1020, 1275, 1275, 1530, 1275, 1530, 1530, 1785, 1275, 1530, 1530, 1785, 1530, 1785, 1785, 2040 }; for (lineptr = line; lineptr < lineend; lineptr ++) pwg->colorants[3] += amounts[*lineptr]; } else { // 8-bit K for (lineptr = line; lineptr < lineend; lineptr ++) pwg->colorants[3] += *lineptr; } break; case CUPS_CSPACE_W : case CUPS_CSPACE_SW : // 8-bit W (luminance) for (lineptr = line; lineptr < lineend; lineptr ++) pwg->colorants[3] += 255 - *lineptr; break; case CUPS_CSPACE_RGB : case CUPS_CSPACE_SRGB : case CUPS_CSPACE_ADOBERGB : // 24-bit RGB for (lineptr = line; lineptr < lineend; lineptr += 3) { // Convert RGB to CMYK using simple transform... unsigned char cc = 255 - lineptr[0]; unsigned char cm = 255 - lineptr[1]; unsigned char cy = 255 - lineptr[2]; unsigned char ck = cc; if (ck > cm) ck = cm; if (ck > cy) ck = cy; cc -= ck; cm -= ck; cy -= ck; pwg->colorants[0] += cc; pwg->colorants[1] += cm; pwg->colorants[2] += cy; pwg->colorants[3] += ck; } break; case CUPS_CSPACE_CMYK : // 32-bit CMYK for (lineptr = line; lineptr < lineend; lineptr += 4) { pwg->colorants[0] += lineptr[0]; pwg->colorants[1] += lineptr[1]; pwg->colorants[2] += lineptr[2]; pwg->colorants[3] += lineptr[3]; } break; default : break; } return (cupsRasterWritePixels(pwg->ras, (unsigned char *)line, options->header.cupsBytesPerLine) != 0); } // // 'pwg_status()' - Get current printer status. // static bool // O - `true` on success, `false` on failure pwg_status( pappl_printer_t *printer) // I - Printer { if (!strncmp(papplPrinterGetDriverName(printer), "pwg_common-", 11)) { // Supply levels... static pappl_supply_t supply[5] = // Supply level data { { PAPPL_SUPPLY_COLOR_CYAN, "Cyan Ink", true, 100, PAPPL_SUPPLY_TYPE_INK }, { PAPPL_SUPPLY_COLOR_MAGENTA, "Magenta Ink", true, 100, PAPPL_SUPPLY_TYPE_INK }, { PAPPL_SUPPLY_COLOR_YELLOW, "Yellow Ink", true, 100, PAPPL_SUPPLY_TYPE_INK }, { PAPPL_SUPPLY_COLOR_BLACK, "Black Ink", true, 100, PAPPL_SUPPLY_TYPE_INK }, { PAPPL_SUPPLY_COLOR_NO_COLOR, "Waste Ink Tank", true, 0, PAPPL_SUPPLY_TYPE_WASTE_INK } }; if (papplPrinterGetSupplies(printer, 0, NULL) == 0) papplPrinterSetSupplies(printer, (int)(sizeof(supply) / sizeof(supply[0])), supply); } // Every 10 seconds, set the "media-empty" reason for one second... if ((time(NULL) % 10) == 0) papplPrinterSetReasons(printer, PAPPL_PREASON_MEDIA_EMPTY, PAPPL_PREASON_NONE); else papplPrinterSetReasons(printer, PAPPL_PREASON_NONE, PAPPL_PREASON_MEDIA_EMPTY); return (true); } // // 'pwg_testpage()' - Return a test page file to print // static const char * // O - Filename or `NULL` pwg_testpage( pappl_printer_t *printer, // I - Printer char *buffer, // I - File Buffer size_t bufsize) // I - Buffer Size { const char *testfile; // Test File pappl_pr_driver_data_t data; // Driver data // Get the printer capabilities... papplPrinterGetDriverData(printer, &data); // Find the right test file... if (data.color_supported & PAPPL_COLOR_MODE_COLOR) testfile = "portrait-color.png"; else testfile = "portrait-gray.png"; strlcpy(buffer, testfile, bufsize); if (access(buffer, R_OK)) snprintf(buffer, bufsize, "testsuite/%s", testfile); if (access(buffer, R_OK)) { *buffer = '\0'; return (NULL); } else { return (buffer); } } pappl-1.0.3/testsuite/template.c000066400000000000000000000004031403603036100166300ustar00rootroot00000000000000// // file for the Printer Application Framework // // Copyright © 2019-2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // // Include necessary headers... // #include pappl-1.0.3/testsuite/testmainloop.c000066400000000000000000000107131403603036100175400ustar00rootroot00000000000000// // papplMainloop unit test for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #include "testpappl.h" // // Local functions... // static pappl_system_t *system_cb(int num_options, cups_option_t *options, void *data); // // 'main()' - Main entry for unit test. // int // O - Exit status main(int argc, // I - Number of command line arguments char *argv[]) // I - Command line arguments { return (papplMainloop(argc, argv, "1.0 build 42", /* footer_html */NULL, (int)(sizeof(pwg_drivers) / sizeof(pwg_drivers[0])), pwg_drivers, /*autoadd_cb*/NULL, pwg_callback, /*subcmd_name*/NULL, /*subcmd_cb*/NULL, system_cb, /*usage_cb*/NULL, "testmainloop")); } // // 'system_cb()' - System callback. // pappl_system_t * // O - New system object system_cb(int num_options, // I - Number of options cups_option_t *options, // I - Options void *data) // I - Callback data { pappl_system_t *system; // System object const char *val, // Current option value *hostname, // Hostname, if any *logfile, // Log file, if any *system_name; // System name, if any pappl_loglevel_t loglevel; // Log level int port = 0; // Port number, if any pappl_soptions_t soptions = PAPPL_SOPTIONS_MULTI_QUEUE | PAPPL_SOPTIONS_WEB_INTERFACE | PAPPL_SOPTIONS_WEB_LOG | PAPPL_SOPTIONS_WEB_NETWORK | PAPPL_SOPTIONS_WEB_SECURITY | PAPPL_SOPTIONS_WEB_TLS; // System options static pappl_contact_t contact = // Contact information { "John Q Admin", "jqadmin@example.org", "+1-705-555-1212" }; static pappl_version_t versions[1] = // Software versions { { "Test Application", "", "1.0 build 42", { 1, 0, 0, 42 } } }; // Verify that the right callback data was sent to us... if (!data || strcmp((char *)data, "testmainloop")) { fprintf(stderr, "testmainloop: Bad callback data %p.\n", data); return (NULL); } // Parse options... if ((val = cupsGetOption("log-level", num_options, options)) != NULL) { if (!strcmp(val, "fatal")) loglevel = PAPPL_LOGLEVEL_FATAL; else if (!strcmp(val, "error")) loglevel = PAPPL_LOGLEVEL_ERROR; else if (!strcmp(val, "warn")) loglevel = PAPPL_LOGLEVEL_WARN; else if (!strcmp(val, "info")) loglevel = PAPPL_LOGLEVEL_INFO; else if (!strcmp(val, "debug")) loglevel = PAPPL_LOGLEVEL_DEBUG; else { fprintf(stderr, "testmainloop: Bad log-level value '%s'.\n", val); return (NULL); } } else loglevel = PAPPL_LOGLEVEL_UNSPEC; logfile = cupsGetOption("log-file", num_options, options); hostname = cupsGetOption("server-hostname", num_options, options); system_name = cupsGetOption("system-name", num_options, options); if ((val = cupsGetOption("server-port", num_options, options)) != NULL) { if (!isdigit(*val & 255)) { fprintf(stderr, "testmainloop: Bad server-port value '%s'.\n", val); return (NULL); } else port = atoi(val); } // Create the system object... if ((system = papplSystemCreate(soptions, system_name ? system_name : "testmainloop", port, "_print,_universal", cupsGetOption("spool-directory", num_options, options), logfile ? logfile : "-", loglevel, cupsGetOption("auth-service", num_options, options), /* tls_only */false)) == NULL) return (NULL); papplSystemAddListeners(system, NULL); papplSystemSetHostname(system, hostname); papplSystemSetPrinterDrivers(system, (int)(sizeof(pwg_drivers) / sizeof(pwg_drivers[0])), pwg_drivers, pwg_autoadd, /*create_cb*/NULL, pwg_callback, "testmainloop"); papplSystemSetFooterHTML(system, "Copyright © 2020 by Michael R Sweet. " "Provided under the terms of the Apache License 2.0."); papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState, (void *)"/tmp/testmainloop.state"); papplSystemSetVersions(system, (int)(sizeof(versions) / sizeof(versions[0])), versions); if (!papplSystemLoadState(system, "/tmp/testmainloop.state")) { papplSystemSetContact(system, &contact); papplSystemSetDNSSDName(system, system_name ? system_name : "Test Mainloop"); papplSystemSetGeoLocation(system, "geo:46.4707,-80.9961"); papplSystemSetLocation(system, "Test Lab 42"); papplSystemSetOrganization(system, "Example Company"); } return (system); } pappl-1.0.3/testsuite/testpappl.c000066400000000000000000002304521403603036100170420ustar00rootroot00000000000000// // Main test suite file for the Printer Application Framework // // Copyright © 2020-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // Usage: // // testpappl [OPTIONS] ["SERVER NAME"] // // Options: // // --help Show help // --list[-TYPE] List devices (dns-sd, local, network, usb) // --version Show version // -1 Single queue // -A PAM-SERVICE Enable authentication using PAM service // -c Do a clean run (no loading of state) // -d SPOOL-DIRECTORY Set the spool directory // -l LOG-FILE Set the log file // -L LOG-LEVEL Set the log level (fatal, error, warn, info, debug) // -m DRIVER-NAME Add a printer with the named driver // -p PORT Set the listen port (default auto) // -t TEST-NAME Run the named test (see below) // -T Enable TLS-only mode // -U Enable USB printer gadget // // Tests: // // all All of the following tests // api API tests // client Simulated client tests // jpeg JPEG image tests // png PNG image tests // pwg-raster PWG Raster tests // // // Include necessary headers... // #include #include #include "testpappl.h" #include #include #ifdef HAVE_ARC4RANDOM # define TESTRAND arc4random() #else # define TESTRAND random() #endif // HAVE_ARC4RANDOM // // Local types... // typedef struct _pappl_testdata_s // Test data { cups_array_t *names; // Tests to run pappl_system_t *system; // System const char *outdirname; // Output directory bool waitsystem; // Wait for system to start? } _pappl_testdata_t; typedef struct _pappl_testprinter_s // Printer test data { bool pass; // Pass/fail int count; // Number of printers } _pappl_testprinter_t; // // Local functions... // static http_t *connect_to_printer(pappl_system_t *system, char *uri, size_t urisize); static void device_error_cb(const char *message, void *err_data); static bool device_list_cb(const char *device_info, const char *device_uri, const char *device_id, void *data); static const char *make_raster_file(ipp_t *response, bool grayscale, char *tempname, size_t tempsize); static void *run_tests(_pappl_testdata_t *testdata); static bool test_api(pappl_system_t *system); static bool test_api_printer(pappl_printer_t *printer); static bool test_api_printer_cb(pappl_printer_t *printer, _pappl_testprinter_t *tp); static bool test_client(pappl_system_t *system); #if defined(HAVE_LIBJPEG) || defined(HAVE_LIBPNG) static bool test_image_files(pappl_system_t *system, const char *prompt, const char *format, int num_files, const char * const *files); #endif // HAVE_LIBJPEG || HAVE_LIBPNG static bool test_pwg_raster(pappl_system_t *system); static int usage(int status); // // 'main()' - Main entry for test suite. // int main(int argc, // I - Number of command-line arguments char *argv[]) // I - Command-line arguments { int i; // Looping var const char *opt, // Current option *name = NULL, // System name, if any *spool = NULL, // Spool directory, if any *outdir = ".", // Output directory *log = NULL, // Log file, if any *auth = NULL, // Auth service, if any *model; // Current printer model cups_array_t *models; // Printer models, if any int port = 0; // Port number, if any pappl_loglevel_t level = PAPPL_LOGLEVEL_DEBUG; // Log level bool clean = false, // Clean run? tls_only = false; // Restrict to TLS only? char outdirname[PATH_MAX], // Output directory name device_uri[1024]; // Device URI for printers pappl_soptions_t soptions = PAPPL_SOPTIONS_MULTI_QUEUE | PAPPL_SOPTIONS_WEB_INTERFACE | PAPPL_SOPTIONS_WEB_LOG | PAPPL_SOPTIONS_WEB_NETWORK | PAPPL_SOPTIONS_WEB_SECURITY | PAPPL_SOPTIONS_WEB_TLS | PAPPL_SOPTIONS_RAW_SOCKET; // System options pappl_system_t *system; // System pappl_printer_t *printer; // Printer _pappl_testdata_t testdata; // Test data pthread_t testid = 0; // Test thread ID static pappl_contact_t contact = // Contact information { "Michael R Sweet", "msweet@example.org", "+1-705-555-1212" }; static pappl_version_t versions[1] = // Software versions { { "Test System", "", "1.0 build 42", { 1, 0, 0, 42 } } }; // Parse command-line options... models = cupsArrayNew(NULL, NULL); testdata.names = cupsArrayNew(NULL, NULL); for (i = 1; i < argc; i ++) { if (!strcmp(argv[i], "--help")) { return (usage(0)); } else if (!strcmp(argv[i], "--list")) { papplDeviceList(PAPPL_DEVTYPE_ALL, device_list_cb, NULL, device_error_cb, NULL); return (0); } else if (!strcmp(argv[i], "--list-dns-sd")) { papplDeviceList(PAPPL_DEVTYPE_DNS_SD, device_list_cb, NULL, device_error_cb, NULL); return (0); } else if (!strcmp(argv[i], "--list-local")) { papplDeviceList(PAPPL_DEVTYPE_LOCAL, device_list_cb, NULL, device_error_cb, NULL); return (0); } else if (!strcmp(argv[i], "--list-network")) { papplDeviceList(PAPPL_DEVTYPE_NETWORK, device_list_cb, NULL, device_error_cb, NULL); return (0); } else if (!strcmp(argv[i], "--list-usb")) { papplDeviceList(PAPPL_DEVTYPE_USB, device_list_cb, NULL, device_error_cb, NULL); return (0); } else if (!strcmp(argv[i], "--version")) { puts(PAPPL_VERSION); return (0); } else if (!strncmp(argv[i], "--", 2)) { printf("testpappl: Unknown option '%s'.\n", argv[i]); return (usage(1)); } else if (argv[i][0] == '-') { for (opt = argv[i] + 1; *opt; opt ++) { switch (*opt) { case '1' : // -1 (single queue) soptions &= (pappl_soptions_t)~PAPPL_SOPTIONS_MULTI_QUEUE; break; case 'A' : // -A PAM-SERVICE i ++; if (i >= argc) { puts("testpappl: Expected PAM service name after '-A'."); return (usage(1)); } auth = argv[i]; break; case 'c' : // -c (clean run) clean = true; break; case 'd' : // -d SPOOL-DIRECTORY i ++; if (i >= argc) { puts("testpappl: Expected spool directory after '-d'."); return (usage(1)); } spool = argv[i]; break; case 'l' : // -l LOG-FILE i ++; if (i >= argc) { puts("testpappl: Expected log file after '-l'."); return (usage(1)); } log = argv[i]; break; case 'L' : // -L LOG-LEVEL i ++; if (i >= argc) { puts("testpappl: Expected log level after '-L'."); return (usage(1)); } if (!strcmp(argv[i], "fatal")) { level = PAPPL_LOGLEVEL_FATAL; } else if (!strcmp(argv[i], "error")) { level = PAPPL_LOGLEVEL_ERROR; } else if (!strcmp(argv[i], "warn")) { level = PAPPL_LOGLEVEL_WARN; } else if (!strcmp(argv[i], "info")) { level = PAPPL_LOGLEVEL_INFO; } else if (!strcmp(argv[i], "debug")) { level = PAPPL_LOGLEVEL_DEBUG; } else { printf("testpappl: Unknown log level '%s'.\n", argv[i]); return (usage(1)); } break; case 'm' : // -m DRIVER-NAME i ++; if (i >= argc) { puts("testpappl: Expected driver name after '-m'."); return (usage(1)); } cupsArrayAdd(models, argv[i]); break; case 'o' : // -o OUTPUT-DIRECTORY i ++; if (i >= argc) { puts("testpappl: Expected output directory after '-o'."); return (usage(1)); } outdir = argv[i]; break; case 'p' : // -p PORT-NUMBER i ++; if (i >= argc || atoi(argv[i]) <= 0 || atoi(argv[i]) > 32767) { puts("testpappl: Expected port number after '-p'."); return (usage(1)); } port = atoi(argv[i]); break; case 't' : // -t TEST i ++; if (i >= argc) { puts("testpappl: Expected test name after '-t'."); return (usage(1)); } if (!strcmp(argv[i], "all")) { cupsArrayAdd(testdata.names, "api"); cupsArrayAdd(testdata.names, "client"); cupsArrayAdd(testdata.names, "jpeg"); cupsArrayAdd(testdata.names, "png"); cupsArrayAdd(testdata.names, "pwg-raster"); } else { cupsArrayAdd(testdata.names, argv[i]); } break; case 'T' : // -T (TLS only) tls_only = true; break; case 'U' : // -U (USB printer gadget) soptions |= PAPPL_SOPTIONS_USB_PRINTER; break; default : printf("testpappl: Unknown option '-%c'.\n", *opt); return (usage(1)); } } } else if (name) { printf("testpappl: Unexpected argument '%s'.\n", argv[i]); return (usage(1)); } else { // "SERVER NAME" name = argv[i]; } } // Initialize the system and any printers... system = papplSystemCreate(soptions, name ? name : "Test System", port, "_print,_universal", spool, log, level, auth, tls_only); papplSystemAddListeners(system, NULL); papplSystemSetPrinterDrivers(system, (int)(sizeof(pwg_drivers) / sizeof(pwg_drivers[0])), pwg_drivers, pwg_autoadd, /* create_cb */NULL, pwg_callback, "testpappl"); papplSystemAddLink(system, "Configuration", "/config", true); papplSystemSetFooterHTML(system, "Copyright © 2020-2021 by Michael R Sweet. " "Provided under the terms of the Apache License 2.0."); papplSystemSetSaveCallback(system, (pappl_save_cb_t)papplSystemSaveState, (void *)"testpappl.state"); papplSystemSetVersions(system, (int)(sizeof(versions) / sizeof(versions[0])), versions); httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), "file", NULL, NULL, 0, "%s?ext=pwg", realpath(outdir, outdirname)); if (clean || !papplSystemLoadState(system, "testpappl.state")) { papplSystemSetContact(system, &contact); papplSystemSetDNSSDName(system, name ? name : "Test System"); papplSystemSetGeoLocation(system, "geo:46.4707,-80.9961"); papplSystemSetLocation(system, "Test Lab 42"); papplSystemSetOrganization(system, "Lakeside Robotics"); if (cupsArrayCount(models)) { for (model = (const char *)cupsArrayFirst(models), i = 1; model; model = (const char *)cupsArrayNext(models), i ++) { char pname[128]; // Printer name if (cupsArrayCount(models) == 1) snprintf(pname, sizeof(pname), "%s", name ? name : "Test Printer"); else snprintf(pname, sizeof(pname), "%s %d", name ? name : "Test Printer", i); printer = papplPrinterCreate(system, /* printer_id */0, pname, model, "MFG:PWG;MDL:Test Printer;", device_uri); papplPrinterSetContact(printer, &contact); papplPrinterSetDNSSDName(printer, pname); papplPrinterSetGeoLocation(printer, "geo:46.4707,-80.9961"); papplPrinterSetLocation(printer, "Test Lab 42"); papplPrinterSetOrganization(printer, "Lakeside Robotics"); } } else { printer = papplPrinterCreate(system, /* printer_id */0, "Office Printer", "pwg_common-300dpi-600dpi-srgb_8", "MFG:PWG;MDL:Office Printer;", device_uri); papplPrinterSetContact(printer, &contact); papplPrinterSetDNSSDName(printer, "Office Printer"); papplPrinterSetGeoLocation(printer, "geo:46.4707,-80.9961"); papplPrinterSetLocation(printer, "Test Lab 42"); papplPrinterSetOrganization(printer, "Lakeside Robotics"); if (soptions & PAPPL_SOPTIONS_MULTI_QUEUE) { printer = papplPrinterCreate(system, /* printer_id */0, "Label Printer", "pwg_4inch-203dpi-black_1", "MFG:PWG;MDL:Label Printer;", device_uri); papplPrinterSetContact(printer, &contact); papplPrinterSetDNSSDName(printer, "Label Printer"); papplPrinterSetGeoLocation(printer, "geo:46.4707,-80.9961"); papplPrinterSetLocation(printer, "Test Lab 42"); papplPrinterSetOrganization(printer, "Lakeside Robotics"); } } } cupsArrayDelete(models); // Run any test(s)... if (cupsArrayCount(testdata.names)) { testdata.outdirname = outdirname; testdata.system = system; if (cupsArrayCount(testdata.names) == 1 && !strcmp((char *)cupsArrayFirst(testdata.names), "api")) { // Running API test alone does not start system... testdata.waitsystem = false; return (run_tests(&testdata) != NULL); } testdata.waitsystem = true; if (pthread_create(&testid, NULL, (void *(*)(void *))run_tests, &testdata)) { perror("Unable to start testing thread"); return (1); } } // Run the system... papplSystemRun(system); if (testid) { void *ret; // Return value from testing thread if (pthread_join(testid, &ret)) { perror("Unable to get testing thread status"); return (1); } else return (ret != NULL); } return (0); } // // 'connect_to_printer()' - Connect to the system and return the printer URI. // static http_t * // O - HTTP connection connect_to_printer( pappl_system_t *system, // I - System char *uri, // I - URI buffer size_t urisize) // I - Size of URI buffer { httpAssembleURI(HTTP_URI_CODING_ALL, uri, (int)urisize, "ipp", NULL, "localhost", papplSystemGetPort(system), "/ipp/print"); return (httpConnect2("localhost", papplSystemGetPort(system), NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL)); } // // 'device_error_cb()' - Show a device error message. // static void device_error_cb(const char *message, // I - Error message void *err_data) // I - Callback data (unused) { (void)err_data; printf("testpappl: %s\n", message); } // // 'device_list_cb()' - List a device. // static bool // O - `true` to stop, `false` to continue device_list_cb(const char *device_info, // I - Device description const char *device_uri, // I - Device URI const char *device_id, // I - IEEE-1284 device ID void *data) // I - Callback data (unused) { (void)data; printf("%s\n %s\n %s\n", device_info, device_uri, device_id); return (false); } // // 'make_raster_file()' - Create a temporary PWG raster file. // // Note: Adapted from CUPS "testclient.c"... // static const char * // O - Print filename make_raster_file(ipp_t *response, // I - Printer attributes bool grayscale, // I - Force grayscale? char *tempname, // I - Temporary filename buffer size_t tempsize) // I - Size of temp file buffer { int i, // Looping var count; // Number of values ipp_attribute_t *attr; // Printer attribute const char *type = NULL; // Raster type (colorspace + bits) pwg_media_t *media = NULL; // Media size int xdpi = 0, // Horizontal resolution ydpi = 0; // Vertical resolution int fd; // Temporary file cups_raster_t *ras; // Raster stream cups_page_header2_t header; // Page header unsigned char *line, // Line of raster data *lineptr; // Pointer into line unsigned y, // Current position on page xcount, ycount, // Current count for X and Y xrep, yrep, // Repeat count for X and Y xoff, yoff, // Offsets for X and Y yend; // End Y value int temprow, // Row in template tempcolor; // Template color const char *template; // Pointer into template const unsigned char *color; // Current color static const unsigned char colors[][3] = { // Colors for test { 191, 191, 191 }, { 127, 127, 127 }, { 63, 63, 63 }, { 0, 0, 0 }, { 255, 0, 0 }, { 255, 127, 0 }, { 255, 255, 0 }, { 127, 255, 0 }, { 0, 255, 0 }, { 0, 255, 127 }, { 0, 255, 255 }, { 0, 127, 255 }, { 0, 0, 255 }, { 127, 0, 255 }, { 255, 0, 255 } }; static const char * const templates[] = { // Raster template "PPPP A PPPP PPPP L TTTTT EEEEE SSS TTTTT 000 1 222 333 4 55555 66 77777 888 999 ", "P P A A P P P P L T E S S T 0 0 11 2 2 3 3 4 4 5 6 7 8 8 9 9 ", "P P A A P P P P L T E S T 0 0 1 2 3 4 4 5 6 7 8 8 9 9 ", "PPPP AAAAA PPPP PPPP L T EEEE SSS T 0 0 0 1 22 333 44444 555 6666 7 888 9999 ", "P A A P P L T E S T 0 0 1 2 3 4 5 6 6 7 8 8 9 ", "P A A P P L T E S S T 0 0 1 2 3 3 4 5 5 6 6 7 8 8 9 ", "P A A P P LLLLL T EEEEE SSS T 000 111 22222 333 4 555 666 7 888 99 ", " " }; // Figure out the the media, resolution, and color mode... if ((attr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL) { // Use ready media... if (ippContainsString(attr, "na_letter_8.5x11in")) media = pwgMediaForPWG("na_letter_8.5x11in"); else if (ippContainsString(attr, "iso_a4_210x297mm")) media = pwgMediaForPWG("iso_a4_210x297mm"); else media = pwgMediaForPWG(ippGetString(attr, 0, NULL)); } else if ((attr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL) { // Use default media... media = pwgMediaForPWG(ippGetString(attr, 0, NULL)); } else { puts("FAIL (No default or ready media reported by printer)"); return (NULL); } if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL) { for (i = 0, count = ippGetCount(attr); i < count; i ++) { int tempxdpi, tempydpi; ipp_res_t tempunits; tempxdpi = ippGetResolution(attr, 0, &tempydpi, &tempunits); if (i == 0 || tempxdpi < xdpi || tempydpi < ydpi) { xdpi = tempxdpi; ydpi = tempydpi; } } if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) != NULL) { if (!grayscale && ippContainsString(attr, "srgb_8")) type = "srgb_8"; else if (ippContainsString(attr, "sgray_8")) type = "sgray_8"; } } if (xdpi < 72 || ydpi < 72) { puts("FAIL (No supported raster resolutions)"); return (NULL); } if (!type) { puts("FAIL (No supported color spaces or bit depths)"); return (NULL); } // Make the raster context and details... if (!cupsRasterInitPWGHeader(&header, media, type, xdpi, ydpi, "one-sided", NULL)) { printf("FAIL (Unable to initialize raster context: %s)\n", cupsRasterErrorString()); return (NULL); } header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = 1; if (header.cupsWidth > (2 * header.HWResolution[0])) { xoff = header.HWResolution[0] / 2; yoff = header.HWResolution[1] / 2; } else { xoff = header.HWResolution[0] / 4; yoff = header.HWResolution[1] / 4; } xrep = (header.cupsWidth - 2 * xoff) / 140; yrep = xrep * header.HWResolution[1] / header.HWResolution[0]; yend = header.cupsHeight - yoff; // Prepare the raster file... if ((line = malloc(header.cupsBytesPerLine)) == NULL) { printf("FAIL (Unable to allocate %u bytes for raster output: %s)\n", header.cupsBytesPerLine, strerror(errno)); return (NULL); } if ((fd = cupsTempFd(tempname, (int)tempsize)) < 0) { printf("FAIL (Unable to create temporary print file: %s)\n", strerror(errno)); free(line); return (NULL); } if ((ras = cupsRasterOpen(fd, CUPS_RASTER_WRITE_PWG)) == NULL) { printf("FAIL (Unable to open raster stream: %s)\n", cupsRasterErrorString()); close(fd); free(line); return (NULL); } // Write a single page consisting of the template dots repeated over the page. cupsRasterWriteHeader2(ras, &header); memset(line, 0xff, header.cupsBytesPerLine); for (y = 0; y < yoff; y ++) cupsRasterWritePixels(ras, line, header.cupsBytesPerLine); for (temprow = 0, tempcolor = 0; y < yend;) { template = templates[temprow]; color = colors[tempcolor]; temprow ++; if (temprow >= (int)(sizeof(templates) / sizeof(templates[0]))) { temprow = 0; tempcolor ++; if (tempcolor >= (int)(sizeof(colors) / sizeof(colors[0]))) tempcolor = 0; else if (tempcolor > 3 && header.cupsColorSpace == CUPS_CSPACE_SW) tempcolor = 0; } memset(line, 0xff, header.cupsBytesPerLine); if (header.cupsColorSpace == CUPS_CSPACE_SW) { // Do grayscale output... for (lineptr = line + xoff; *template; template ++) { if (*template != ' ') { for (xcount = xrep; xcount > 0; xcount --) *lineptr++ = *color; } else { lineptr += xrep; } } } else { // Do color output... for (lineptr = line + 3 * xoff; *template; template ++) { if (*template != ' ') { for (xcount = xrep; xcount > 0; xcount --, lineptr += 3) memcpy(lineptr, color, 3); } else { lineptr += 3 * xrep; } } } for (ycount = yrep; ycount > 0 && y < yend; ycount --, y ++) cupsRasterWritePixels(ras, line, header.cupsBytesPerLine); } memset(line, 0xff, header.cupsBytesPerLine); for (y = 0; y < header.cupsHeight; y ++) cupsRasterWritePixels(ras, line, header.cupsBytesPerLine); free(line); cupsRasterClose(ras); close(fd); return (tempname); } // // 'run_tests()' - Run named tests. // static void * // O - Thread status run_tests(_pappl_testdata_t *testdata) // I - Testing data { const char *name; // Test name void *ret = NULL; // Return thread status cups_dir_t *dir; // Output directory cups_dentry_t *dent; // Output file int files = 0; // Total file count off_t total = 0; // Total output size #ifdef HAVE_LIBJPEG static const char * const jpeg_files[] = { // List of JPEG files to print "portrait-gray.jpg", "portrait-color.jpg", "landscape-gray.jpg", "landscape-color.jpg" }; #endif // HAVE_LIBJPEG #ifdef HAVE_LIBPNG static const char * const png_files[] = { // List of PNG files to print "portrait-gray.png", "portrait-color.png", "landscape-gray.png", "landscape-color.png" }; #endif // HAVE_LIBPNG if (testdata->waitsystem) { // Wait for the system to start... while (!papplSystemIsRunning(testdata->system)) sleep(1); } // Run each test... for (name = (const char *)cupsArrayFirst(testdata->names); name && !ret && (!papplSystemIsShutdown(testdata->system) || !testdata->waitsystem); name = (const char *)cupsArrayNext(testdata->names)) { printf("%s: ", name); fflush(stdout); if (!strcmp(name, "api")) { if (!test_api(testdata->system)) ret = (void *)1; else puts("PASS"); } else if (!strcmp(name, "client")) { if (!test_client(testdata->system)) ret = (void *)1; else puts("PASS"); } else if (!strcmp(name, "jpeg")) { #ifdef HAVE_LIBJPEG if (!test_image_files(testdata->system, "jpeg", "image/jpeg", (int)(sizeof(jpeg_files) / sizeof(jpeg_files[0])), jpeg_files)) ret = (void *)1; else puts("PASS"); #else puts("SKIP"); #endif // HAVE_LIBJPEG } else if (!strcmp(name, "png")) { #ifdef HAVE_LIBPNG if (!test_image_files(testdata->system, "png", "image/png", (int)(sizeof(png_files) / sizeof(png_files[0])), png_files)) ret = (void *)1; else puts("PASS"); #else puts("SKIP"); #endif // HAVE_LIBPNG } else if (!strcmp(name, "pwg-raster")) { if (!test_pwg_raster(testdata->system)) ret = (void *)1; else puts("PASS"); } else { puts("UNKNOWN TEST"); ret = (void *)1; } } // Summarize results... if ((dir = cupsDirOpen(testdata->outdirname)) != NULL) { while ((dent = cupsDirRead(dir)) != NULL) { if (S_ISREG(dent->fileinfo.st_mode)) { files ++; total += dent->fileinfo.st_size; } } cupsDirClose(dir); } papplSystemShutdown(testdata->system); if (ret) printf("\nFAILED: %d output file(s), %.1fMB\n", files, total / 1048576.0); else printf("\nPASSED: %d output file(s), %.1fMB\n", files, total / 1048576.0); return (ret); } // // 'test_api()' - Run API unit tests. // static bool // O - `true` on success, `false` on failure test_api(pappl_system_t *system) // I - System { bool pass = true; // Pass/fail state int i, j; // Looping vars pappl_contact_t get_contact, // Contact for "get" call set_contact; // Contact for "set" call int get_int, // Integer for "get" call set_int; // Integer for "set" call char get_str[1024], // Temporary string for "get" call set_str[1024]; // Temporary string for "set" call int get_nvers; // Number of versions for "get" call pappl_version_t get_vers[10], // Versions for "get" call set_vers[10]; // Versions for "set" call const char *get_value; // Value for "get" call pappl_loglevel_t get_loglevel, // Log level for "get" call set_loglevel; // Log level for "set" call size_t get_size, // Size for "get" call set_size; // Size for "set" call pappl_printer_t *printer; // Current printer _pappl_testprinter_t pdata; // Printer test data static const char * const set_locations[10][2] = { // Some wonders of the ancient world (all north-eastern portion of globe...) { "Great Pyramid of Giza", "geo:29.979175,31.134358" }, { "Temple of Artemis at Ephesus", "geo:37.949722,27.363889" }, { "Statue of Zeus at Olympia", "geo:37.637861,21.63" }, { "Colossus of Rhodes", "geo:36.451111,28.227778" }, { "Lighthouse of Alexandria", "geo:31.213889,29.885556" }, // Other places { "Niagara Falls", "geo:43.0828201,-79.0763516" }, { "Grand Canyon", "geo:36.0545936,-112.2307085" }, { "Christ the Redeemer", "geo:-22.9691208,-43.2583044" }, { "Great Barrier Reef", "geo:-16.7546653,143.8322946" }, { "Science North", "geo:46.4707,-80.9961" } }; static const char * const set_loglevels[] = { // Log level constants "UNSPEC", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" }; // papplSystemGet/SetAdminGroup fputs("papplSystemGetAdminGroup: ", stdout); if (papplSystemGetAdminGroup(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "admin-%d", i); printf("api: papplSystemGet/SetAdminGroup('%s'): ", set_str); papplSystemSetAdminGroup(system, set_str); if (!papplSystemGetAdminGroup(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetAdminGroup(NULL): ", stdout); papplSystemSetAdminGroup(system, NULL); if (papplSystemGetAdminGroup(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetContact fputs("api: papplSystemGetContact: ", stdout); if (!papplSystemGetContact(system, &get_contact)) { puts("FAIL (got NULL, expected 'Michael R Sweet')"); pass = false; } else if (strcmp(get_contact.name, "Michael R Sweet")) { printf("FAIL (got '%s', expected 'Michael R Sweet')\n", get_contact.name); pass = false; } else if (strcmp(get_contact.email, "msweet@example.org")) { printf("FAIL (got '%s', expected 'msweet@example.org')\n", get_contact.email); pass = false; } else if (strcmp(get_contact.telephone, "+1-705-555-1212")) { printf("FAIL (got '%s', expected '+1-705-555-1212')\n", get_contact.telephone); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_contact.name, sizeof(set_contact.name), "Admin %d", i); snprintf(set_contact.email, sizeof(set_contact.email), "admin-%d@example.org", i); snprintf(set_contact.telephone, sizeof(set_contact.telephone), "+1-705-555-%04d", i * 1111); printf("api: papplSystemGet/SetContact('%s'): ", set_contact.name); papplSystemSetContact(system, &set_contact); if (!papplSystemGetContact(system, &get_contact)) { printf("FAIL (got NULL, expected '%s')\n", set_contact.name); pass = false; } else if (strcmp(get_contact.name, set_contact.name)) { printf("FAIL (got '%s', expected '%s')\n", get_contact.name, set_contact.name); pass = false; } else if (strcmp(get_contact.email, set_contact.email)) { printf("FAIL (got '%s', expected '%s')\n", get_contact.email, set_contact.email); pass = false; } else if (strcmp(get_contact.telephone, set_contact.telephone)) { printf("FAIL (got '%s', expected '%s')\n", get_contact.telephone, set_contact.telephone); pass = false; } else puts("PASS"); } // papplSystemGet/SetDefaultPrinterID fputs("api: papplSystemGetDefaultPrinterID: ", stdout); if ((get_int = papplSystemGetDefaultPrinterID(system)) == 0) { puts("FAIL (got 0, expected > 0)"); pass = false; } else printf("PASS (%d)\n", get_int); for (set_int = 2; set_int >= 1; set_int --) { printf("api: papplSystemSetDefaultPrinterID(%d): ", set_int); papplSystemSetDefaultPrinterID(system, set_int); if ((get_int = papplSystemGetDefaultPrinterID(system)) != set_int) { printf("FAIL (got %d, expected %d)\n", get_int, set_int); pass = false; } else puts("PASS"); } // papplSystemGet/SetDefaultPrintGroup fputs("api: papplSystemGetDefaultPrintGroup: ", stdout); if (papplSystemGetDefaultPrintGroup(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "users-%d", i); printf("api: papplSystemGet/SetDefaultPrintGroup('%s'): ", set_str); papplSystemSetDefaultPrintGroup(system, set_str); if (!papplSystemGetDefaultPrintGroup(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetDefaultPrintGroup(NULL): ", stdout); papplSystemSetDefaultPrintGroup(system, NULL); if (papplSystemGetDefaultPrintGroup(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetDNSSDName fputs("api: papplSystemGetDNSSDName: ", stdout); if (!papplSystemGetDNSSDName(system, get_str, sizeof(get_str))) { fputs("FAIL (got NULL, expected 'Test System')\n", stdout); pass = false; } else if (strcmp(get_str, "Test System")) { printf("FAIL (got '%s', expected 'Test System')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "System Test %c", i + 'A'); printf("api: papplSystemGet/SetDNSSDName('%s'): ", set_str); papplSystemSetDNSSDName(system, set_str); if (!papplSystemGetDNSSDName(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetDNSSDName(NULL): ", stdout); papplSystemSetDNSSDName(system, NULL); if (papplSystemGetDNSSDName(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetFooterHTML fputs("api: papplSystemGetFooterHTML: ", stdout); if ((get_value = papplSystemGetFooterHTML(system)) == NULL) { puts("FAIL (got NULL, expected 'Copyright ...')"); pass = false; } else if (strncmp(get_value, "Copyright © 2020", 21)) { printf("FAIL (got '%s', expected 'Copyright ...')\n", get_value); pass = false; } else puts("PASS"); fputs("api: papplSystemSetFooterHTML('Mike wuz here.'): ", stdout); papplSystemSetFooterHTML(system, "Mike wuz here."); if ((get_value = papplSystemGetFooterHTML(system)) == NULL) { puts("FAIL (got NULL, expected 'Mike wuz here.')"); pass = false; } else if (papplSystemIsRunning(system)) { // System is running so we can't change the footer text anymore... if (strncmp(get_value, "Copyright © 2020", 21)) { printf("FAIL (got '%s', expected 'Copyright ...')\n", get_value); pass = false; } else puts("PASS"); } else { // System is not running so we can change the footer text... if (strcmp(get_value, "Mike wuz here.")) { printf("FAIL (got '%s', expected 'Mike wuz here.')\n", get_value); pass = false; } else puts("PASS"); } // papplSystemGet/SetGeoLocation fputs("api: papplSystemGetGeoLocation: ", stdout); if (!papplSystemGetGeoLocation(system, get_str, sizeof(get_str))) { puts("FAIL (got NULL, expected 'geo:46.4707,-80.9961')"); pass = false; } else if (strcmp(get_str, "geo:46.4707,-80.9961")) { printf("FAIL (got '%s', expected 'geo:46.4707,-80.9961')\n", get_str); pass = false; } else puts("PASS"); fputs("api: papplSystemGet/SetGeoLocation('bad-value'): ", stdout); papplSystemSetGeoLocation(system, "bad-value"); if (!papplSystemGetGeoLocation(system, get_str, sizeof(get_str))) { puts("FAIL (got NULL, expected 'geo:46.4707,-80.9961')"); pass = false; } else if (strcmp(get_str, "geo:46.4707,-80.9961")) { printf("FAIL (got '%s', expected 'geo:46.4707,-80.9961')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < (int)(sizeof(set_locations) / sizeof(set_locations[0])); i ++) { printf("api: papplSystemGet/SetGeoLocation('%s'): ", set_locations[i][1]); papplSystemSetGeoLocation(system, set_locations[i][1]); if (!papplSystemGetGeoLocation(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_locations[i][1]); pass = false; } else if (strcmp(get_str, set_locations[i][1])) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_locations[i][1]); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetGeoLocation(NULL): ", stdout); papplSystemSetGeoLocation(system, NULL); if (papplSystemGetGeoLocation(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetHostname fputs("api: papplSystemGetHostname: ", stdout); if (!papplSystemGetHostname(system, get_str, sizeof(get_str))) { fputs("FAIL (got NULL, expected '*.domain')\n", stdout); pass = false; } else if (!strchr(get_str, '.')) { printf("FAIL (got '%s', expected '*.domain')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "example%d.org", i); printf("api: papplSystemGet/SetHostname('%s'): ", set_str); papplSystemSetHostname(system, set_str); if (!papplSystemGetHostname(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetHostname(NULL): ", stdout); papplSystemSetHostname(system, NULL); if (!papplSystemGetHostname(system, get_str, sizeof(get_str))) { puts("FAIL (got NULL, expected '*.domain')"); pass = false; } else if (!strchr(get_str, '.')) { printf("FAIL (got '%s', expected '*.domain')\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetLocation fputs("api: papplSystemGetLocation: ", stdout); if (!papplSystemGetLocation(system, get_str, sizeof(get_str))) { fputs("FAIL (got NULL, expected 'Test Lab 42')\n", stdout); pass = false; } else if (strcmp(get_str, "Test Lab 42")) { printf("FAIL (got '%s', expected 'Test Lab 42')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < (int)(sizeof(set_locations) / sizeof(set_locations[0])); i ++) { printf("api: papplSystemGet/SetLocation('%s'): ", set_locations[i][0]); papplSystemSetLocation(system, set_locations[i][0]); if (!papplSystemGetLocation(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_locations[i][0]); pass = false; } else if (strcmp(get_str, set_locations[i][0])) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_locations[i][0]); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetLocation(NULL): ", stdout); papplSystemSetLocation(system, NULL); if (papplSystemGetLocation(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetLogLevel fputs("api: papplSystemGetLogLevel: ", stdout); if (papplSystemGetLogLevel(system) == PAPPL_LOGLEVEL_UNSPEC) { puts("FAIL (got PAPPL_LOGLEVEL_UNSPEC, expected another PAPPL_LOGLEVEL_ value)"); pass = false; } else puts("PASS"); for (set_loglevel = PAPPL_LOGLEVEL_FATAL; set_loglevel >= PAPPL_LOGLEVEL_DEBUG; set_loglevel --) { printf("api: papplSystemSetLogLevel(PAPPL_LOGLEVEL_%s): ", set_loglevels[set_loglevel + 1]); papplSystemSetLogLevel(system, set_loglevel); if ((get_loglevel = papplSystemGetLogLevel(system)) != set_loglevel) { printf("FAIL (got PAPPL_LOGLEVEL_%s, expected PAPPL_LOGLEVEL_%s)\n", set_loglevels[get_loglevel + 1], set_loglevels[set_loglevel + 1]); pass = false; } else puts("PASS"); } // papplSystemGet/SetMaxLogSize fputs("api: papplSystemGetMaxLogSize: ", stdout); if ((get_size = papplSystemGetMaxLogSize(system)) != (size_t)(1024 * 1024)) { printf("FAIL (got %ld, expected %ld)\n", (long)get_size, (long)(1024 * 1024)); pass = false; } else puts("PASS"); for (set_size = 0; set_size <= (16 * 1024 * 1024); set_size += 1024 * 1024) { printf("api: papplSystemSetMaxLogSize(%ld): ", (long)set_size); papplSystemSetMaxLogSize(system, set_size); if ((get_size = papplSystemGetMaxLogSize(system)) != set_size) { printf("FAIL (got %ld, expected %ld)\n", (long)get_size, (long)set_size); pass = false; } else puts("PASS"); } // papplSystemGet/SetNextPrinterID fputs("api: papplSystemGetNextPrinterID: ", stdout); if ((get_int = papplSystemGetNextPrinterID(system)) != 3) { printf("FAIL (got %d, expected 3)\n", get_int); pass = false; } else puts("PASS"); set_int = (TESTRAND % 1000000) + 4; printf("api: papplSystemSetNextPrinterID(%d): ", set_int); papplSystemSetNextPrinterID(system, set_int); if ((get_int = papplSystemGetNextPrinterID(system)) != set_int) { if (papplSystemIsRunning(system)) puts("PASS"); else { printf("FAIL (got %d, expected %d)\n", get_int, set_int); pass = false; } } else puts("PASS"); // papplSystemGet/SetOrganization fputs("api: papplSystemGetOrganization: ", stdout); if (!papplSystemGetOrganization(system, get_str, sizeof(get_str))) { puts("FAIL (got NULL, expected 'Lakeside Robotics')"); pass = false; } else if (strcmp(get_str, "Lakeside Robotics")) { printf("FAIL (got '%s', expected 'Lakeside Robotics')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "Organization %c", i + 'A'); printf("api: papplSystemGet/SetOrganization('%s'): ", set_str); papplSystemSetOrganization(system, set_str); if (!papplSystemGetOrganization(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetOrganization(NULL): ", stdout); papplSystemSetOrganization(system, NULL); if (papplSystemGetOrganization(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetOrganizationalUnit fputs("api: papplSystemGetOrganizationalUnit: ", stdout); if (papplSystemGetOrganizationalUnit(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "%c Team", i + 'A'); printf("api: papplSystemGet/SetOrganizationalUnit('%s'): ", set_str); papplSystemSetOrganizationalUnit(system, set_str); if (!papplSystemGetOrganizationalUnit(system, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetOrganizationalUnit(NULL): ", stdout); papplSystemSetOrganizationalUnit(system, NULL); if (papplSystemGetOrganizationalUnit(system, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplSystemGet/SetUUID fputs("api: papplSystemGetUUID: ", stdout); if ((get_value = papplSystemGetUUID(system)) == NULL) { puts("FAIL (got NULL, expected 'urn:uuid:...')"); pass = false; } else if (strncmp(get_value, "urn:uuid:", 9)) { printf("FAIL (got '%s', expected 'urn:uuid:...')\n", get_value); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "urn:uuid:%04x%04x-%04x-%04x-%04x-%04x%04x%04x", (unsigned)(TESTRAND % 65536), (unsigned)(TESTRAND % 65536), (unsigned)(TESTRAND % 65536), (unsigned)(TESTRAND % 65536), (unsigned)(TESTRAND % 65536), (unsigned)(TESTRAND % 65536), (unsigned)(TESTRAND % 65536), (unsigned)(TESTRAND % 65536)); printf("api: papplSystemGet/SetUUID('%s'): ", set_str); papplSystemSetUUID(system, set_str); if ((get_value = papplSystemGetUUID(system)) == NULL) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (papplSystemIsRunning(system)) { if (!strcmp(get_value, set_str) || strncmp(get_value, "urn:uuid:", 9)) { printf("FAIL (got '%s', expected different 'urn:uuid:...')\n", get_value); pass = false; } else puts("PASS"); } else if (strcmp(get_value, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_value, set_str); pass = false; } else puts("PASS"); } fputs("api: papplSystemGet/SetUUID(NULL): ", stdout); if ((get_value = papplSystemGetUUID(system)) == NULL) { puts("FAIL (unable to get current UUID)"); pass = false; } else { strlcpy(get_str, get_value, sizeof(get_str)); papplSystemSetUUID(system, NULL); if ((get_value = papplSystemGetUUID(system)) == NULL) { puts("FAIL (got NULL, expected 'urn:uuid:...')"); pass = false; } else if (papplSystemIsRunning(system)) { if (!strcmp(get_value, set_str) || strncmp(get_value, "urn:uuid:", 9)) { printf("FAIL (got '%s', expected different 'urn:uuid:...')\n", get_value); pass = false; } else puts("PASS"); } else if (!strcmp(get_value, set_str)) { printf("FAIL (got '%s', expected different '%s')\n", get_value, set_str); pass = false; } else puts("PASS"); } // papplSystemGet/SetVersions fputs("api: papplSystemGetVersions: ", stdout); if ((get_nvers = papplSystemGetVersions(system, (int)(sizeof(get_vers) / sizeof(get_vers[0])), get_vers)) != 1) { printf("FAIL (got %d versions, expected 1)\n", get_nvers); pass = false; } else if (strcmp(get_vers[0].name, "Test System") || strcmp(get_vers[0].sversion, "1.0 build 42")) { printf("FAIL (got '%s v%s', expected 'Test System v1.0 build 42')\n", get_vers[0].name, get_vers[0].sversion); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { printf("api: papplSystemGet/SetVersions(%d): ", i + 1); memset(set_vers + i, 0, sizeof(pappl_version_t)); snprintf(set_vers[i].name, sizeof(set_vers[i].name), "Component %c", 'A' + i); set_vers[i].version[0] = (unsigned short)(i + 1); set_vers[i].version[1] = (unsigned short)(TESTRAND % 100); snprintf(set_vers[i].sversion, sizeof(set_vers[i].sversion), "%u.%02u", set_vers[i].version[0], set_vers[i].version[1]); papplSystemSetVersions(system, i + 1, set_vers); if ((get_nvers = papplSystemGetVersions(system, (int)(sizeof(get_vers) / sizeof(get_vers[0])), get_vers)) != (i + 1)) { printf("FAIL (got %d versions, expected %d)\n", get_nvers, i + 1); pass = false; } else { for (j = 0; j < get_nvers; j ++) { if (strcmp(get_vers[j].name, set_vers[j].name) || strcmp(get_vers[j].sversion, set_vers[j].sversion)) { printf("FAIL (got '%s v%s', expected '%s v%s')\n", get_vers[j].name, get_vers[j].sversion, set_vers[j].name, set_vers[j].sversion); pass = false; break; } } if (j >= get_nvers) puts("PASS"); } } // papplSystemFindPrinter fputs("api: papplSystemFindPrinter(default): ", stdout); if ((printer = papplSystemFindPrinter(system, "/ipp/print", 0, NULL)) == NULL) { puts("FAIL (got NULL)"); pass = false; } else if (papplPrinterGetID(printer) != papplSystemGetDefaultPrinterID(system)) { printf("FAIL (got printer #%d, expected #%d)\n", papplPrinterGetID(printer), papplSystemGetDefaultPrinterID(system)); pass = false; } else puts("PASS"); for (set_int = 1; set_int < 3; set_int ++) { printf("api: papplSystemFindPrinter(%d): ", set_int); if ((printer = papplSystemFindPrinter(system, NULL, set_int, NULL)) == NULL) { puts("FAIL (got NULL)"); pass = false; } else { puts("PASS"); if (!test_api_printer(printer)) pass = false; } } // papplPrinterCreate/Delete for (i = 0; i < 10; i ++) { char name[128]; // Printer name snprintf(name, sizeof(name), "test%d", i); printf("api: papplPrinterCreate(%s): ", name); if ((printer = papplPrinterCreate(system, 0, name, "pwg_common-300dpi-black_1-sgray_8", "MFG:PWG;MDL:Office Printer;CMD:PWGRaster;", "file:///dev/null")) == NULL) { puts("FAIL (got NULL)"); pass = false; } else { puts("PASS"); get_int = papplPrinterGetID(printer); printf("api: papplPrinterDelete(%s): ", name); papplPrinterDelete(printer); if (papplSystemFindPrinter(system, NULL, get_int, NULL) != NULL) { puts("FAIL (printer not deleted)"); pass = false; } else { puts("PASS"); printf("api: papplPrinterCreate(%s again): ", name); if ((printer = papplPrinterCreate(system, 0, name, "pwg_common-300dpi-black_1-sgray_8", "MFG:PWG;MDL:Office Printer;CMD:PWGRaster;", "file:///dev/null")) == NULL) { puts("FAIL (got NULL)"); pass = false; } else if (papplPrinterGetID(printer) == get_int) { puts("FAIL (got the same printer ID)"); pass = false; } else puts("PASS"); } } } // papplSystemIteratePrinters fputs("api: papplSystemIteratePrinters: ", stdout); pdata.pass = true; pdata.count = 0; papplSystemIteratePrinters(system, (pappl_printer_cb_t)test_api_printer_cb, &pdata); if (pdata.count != 12) { printf("FAIL (got %d printers, expected 12)\n", pdata.count); pass = false; } else if (!pdata.pass) { puts("FAIL (per-printer test failed)"); pass = false; } else puts("PASS"); if (pass) fputs("api: ", stdout); return (pass); } // // 'test_api_printer()' - Test papplPrinter APIs. // static bool // O - `true` on success, `false` on failure test_api_printer( pappl_printer_t *printer) // I - Printer { bool pass = true; // Pass/fail for tests int i; // Looping vars pappl_contact_t get_contact, // Contact for "get" call set_contact; // Contact for "set" call int get_int, // Integer for "get" call set_int; // Integer for "set" call char get_str[1024], // Temporary string for "get" call set_str[1024]; // Temporary string for "set" call static const char * const set_locations[10][2] = { // Some wonders of the ancient world (all north-eastern portion of globe...) { "Great Pyramid of Giza", "geo:29.979175,31.134358" }, { "Temple of Artemis at Ephesus", "geo:37.949722,27.363889" }, { "Statue of Zeus at Olympia", "geo:37.637861,21.63" }, { "Colossus of Rhodes", "geo:36.451111,28.227778" }, { "Lighthouse of Alexandria", "geo:31.213889,29.885556" }, // Other places { "Niagara Falls", "geo:43.0828201,-79.0763516" }, { "Grand Canyon", "geo:36.0545936,-112.2307085" }, { "Christ the Redeemer", "geo:-22.9691208,-43.2583044" }, { "Great Barrier Reef", "geo:-16.7546653,143.8322946" }, { "Science North", "geo:46.4707,-80.9961" } }; // papplPrinterGet/SetContact fputs("api: papplPrinterGetContact: ", stdout); if (!papplPrinterGetContact(printer, &get_contact)) { puts("FAIL (got NULL, expected 'Michael R Sweet')"); pass = false; } else if (strcmp(get_contact.name, "Michael R Sweet")) { printf("FAIL (got '%s', expected 'Michael R Sweet')\n", get_contact.name); pass = false; } else if (strcmp(get_contact.email, "msweet@example.org")) { printf("FAIL (got '%s', expected 'msweet@example.org')\n", get_contact.email); pass = false; } else if (strcmp(get_contact.telephone, "+1-705-555-1212")) { printf("FAIL (got '%s', expected '+1-705-555-1212')\n", get_contact.telephone); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_contact.name, sizeof(set_contact.name), "Admin %d", i); snprintf(set_contact.email, sizeof(set_contact.email), "admin-%d@example.org", i); snprintf(set_contact.telephone, sizeof(set_contact.telephone), "+1-705-555-%04d", i * 1111); printf("api: papplPrinterGet/SetContact('%s'): ", set_contact.name); papplPrinterSetContact(printer, &set_contact); if (!papplPrinterGetContact(printer, &get_contact)) { printf("FAIL (got NULL, expected '%s')\n", set_contact.name); pass = false; } else if (strcmp(get_contact.name, set_contact.name)) { printf("FAIL (got '%s', expected '%s')\n", get_contact.name, set_contact.name); pass = false; } else if (strcmp(get_contact.email, set_contact.email)) { printf("FAIL (got '%s', expected '%s')\n", get_contact.email, set_contact.email); pass = false; } else if (strcmp(get_contact.telephone, set_contact.telephone)) { printf("FAIL (got '%s', expected '%s')\n", get_contact.telephone, set_contact.telephone); pass = false; } else puts("PASS"); } // papplPrinterGet/SetPrintGroup fputs("api: papplPrinterGetPrintGroup: ", stdout); if (papplPrinterGetPrintGroup(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "users-%d", i); printf("api: papplPrinterGet/SetPrintGroup('%s'): ", set_str); papplPrinterSetPrintGroup(printer, set_str); if (!papplPrinterGetPrintGroup(printer, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplPrinterGet/SetPrintGroup(NULL): ", stdout); papplPrinterSetPrintGroup(printer, NULL); if (papplPrinterGetPrintGroup(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplPrinterGet/SetDNSSDName fputs("api: papplPrinterGetDNSSDName: ", stdout); if (!papplPrinterGetDNSSDName(printer, get_str, sizeof(get_str))) { fputs("FAIL (got NULL, expected string)\n", stdout); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "Printer Test %c", i + 'A'); printf("api: papplPrinterGet/SetDNSSDName('%s'): ", set_str); papplPrinterSetDNSSDName(printer, set_str); if (!papplPrinterGetDNSSDName(printer, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplPrinterGet/SetDNSSDName(NULL): ", stdout); papplPrinterSetDNSSDName(printer, NULL); if (papplPrinterGetDNSSDName(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplPrinterGet/SetGeoLocation fputs("api: papplPrinterGetGeoLocation: ", stdout); if (!papplPrinterGetGeoLocation(printer, get_str, sizeof(get_str))) { puts("FAIL (got NULL, expected 'geo:46.4707,-80.9961')"); pass = false; } else if (strcmp(get_str, "geo:46.4707,-80.9961")) { printf("FAIL (got '%s', expected 'geo:46.4707,-80.9961')\n", get_str); pass = false; } else puts("PASS"); fputs("api: papplPrinterGet/SetGeoLocation('bad-value'): ", stdout); papplPrinterSetGeoLocation(printer, "bad-value"); if (!papplPrinterGetGeoLocation(printer, get_str, sizeof(get_str))) { puts("FAIL (got NULL, expected 'geo:46.4707,-80.9961')"); pass = false; } else if (strcmp(get_str, "geo:46.4707,-80.9961")) { printf("FAIL (got '%s', expected 'geo:46.4707,-80.9961')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < (int)(sizeof(set_locations) / sizeof(set_locations[0])); i ++) { printf("api: papplPrinterGet/SetGeoLocation('%s'): ", set_locations[i][1]); papplPrinterSetGeoLocation(printer, set_locations[i][1]); if (!papplPrinterGetGeoLocation(printer, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_locations[i][1]); pass = false; } else if (strcmp(get_str, set_locations[i][1])) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_locations[i][1]); pass = false; } else puts("PASS"); } fputs("api: papplPrinterGet/SetGeoLocation(NULL): ", stdout); papplPrinterSetGeoLocation(printer, NULL); if (papplPrinterGetGeoLocation(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplPrinterGet/SetLocation fputs("api: papplPrinterGetLocation: ", stdout); if (!papplPrinterGetLocation(printer, get_str, sizeof(get_str))) { fputs("FAIL (got NULL, expected 'Test Lab 42')\n", stdout); pass = false; } else if (strcmp(get_str, "Test Lab 42")) { printf("FAIL (got '%s', expected 'Test Lab 42')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < (int)(sizeof(set_locations) / sizeof(set_locations[0])); i ++) { printf("api: papplPrinterGet/SetLocation('%s'): ", set_locations[i][0]); papplPrinterSetLocation(printer, set_locations[i][0]); if (!papplPrinterGetLocation(printer, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_locations[i][0]); pass = false; } else if (strcmp(get_str, set_locations[i][0])) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_locations[i][0]); pass = false; } else puts("PASS"); } fputs("api: papplPrinterGet/SetLocation(NULL): ", stdout); papplPrinterSetLocation(printer, NULL); if (papplPrinterGetLocation(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplPrinterGet/SetNextJobID fputs("api: papplPrinterGetNextJobID: ", stdout); if ((get_int = papplPrinterGetNextJobID(printer)) != 1) { printf("FAIL (got %d, expected 1)\n", get_int); pass = false; } else puts("PASS"); set_int = (TESTRAND % 1000000) + 2; printf("api: papplPrinterSetNextJobID(%d): ", set_int); papplPrinterSetNextJobID(printer, set_int); if ((get_int = papplPrinterGetNextJobID(printer)) != set_int) { printf("FAIL (got %d, expected %d)\n", get_int, set_int); pass = false; } else puts("PASS"); // papplPrinterGet/SetOrganization fputs("api: papplPrinterGetOrganization: ", stdout); if (!papplPrinterGetOrganization(printer, get_str, sizeof(get_str))) { puts("FAIL (got NULL, expected 'Lakeside Robotics')"); pass = false; } else if (strcmp(get_str, "Lakeside Robotics")) { printf("FAIL (got '%s', expected 'Lakeside Robotics')\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "Organization %c", i + 'A'); printf("api: papplPrinterGet/SetOrganization('%s'): ", set_str); papplPrinterSetOrganization(printer, set_str); if (!papplPrinterGetOrganization(printer, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplPrinterGet/SetOrganization(NULL): ", stdout); papplPrinterSetOrganization(printer, NULL); if (papplPrinterGetOrganization(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); // papplPrinterGet/SetOrganizationalUnit fputs("api: papplPrinterGetOrganizationalUnit: ", stdout); if (papplPrinterGetOrganizationalUnit(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); for (i = 0; i < 10; i ++) { snprintf(set_str, sizeof(set_str), "%c Team", i + 'A'); printf("api: papplPrinterGet/SetOrganizationalUnit('%s'): ", set_str); papplPrinterSetOrganizationalUnit(printer, set_str); if (!papplPrinterGetOrganizationalUnit(printer, get_str, sizeof(get_str))) { printf("FAIL (got NULL, expected '%s')\n", set_str); pass = false; } else if (strcmp(get_str, set_str)) { printf("FAIL (got '%s', expected '%s')\n", get_str, set_str); pass = false; } else puts("PASS"); } fputs("api: papplPrinterGet/SetOrganizationalUnit(NULL): ", stdout); papplPrinterSetOrganizationalUnit(printer, NULL); if (papplPrinterGetOrganizationalUnit(printer, get_str, sizeof(get_str))) { printf("FAIL (got '%s', expected NULL)\n", get_str); pass = false; } else puts("PASS"); return (pass); } // // 'test_api_printer_cb()' - Iterator callback for testing printers. // static bool // O - `true` to continue test_api_printer_cb( pappl_printer_t *printer, // I - Printer _pappl_testprinter_t *tp) // I - Printer test data { tp->count ++; if (!printer) tp->pass = false; else if (!papplPrinterGetName(printer)) tp->pass = false; else { char get_str[128]; // Location string papplPrinterSetLocation(printer, "Nowhere"); if (!papplPrinterGetLocation(printer, get_str, sizeof(get_str)) || strcmp(get_str, "Nowhere")) tp->pass = false; } return (true); } // // 'test_client()' - Run simulated client tests. // static bool // O - `true` on success, `false` on failure test_client(pappl_system_t *system) // I - System { http_t *http; // HTTP connection char uri[1024]; // "printer-uri" value ipp_t *request, // Request *response; // Response int i; // Looping var static const char * const pattrs[] = // Printer attributes { "printer-contact-col", "printer-current-time", "printer-geo-location", "printer-location", "printer-name", "printer-state", "printer-state-reasons", "printer-uuid", "printer-uri-supported" }; static const char * const sattrs[] = // System attributes { "system-contact-col", "system-current-time", "system-geo-location", "system-location", "system-name", "system-state", "system-state-reasons", "system-uuid", "system-xri-supported" }; // Connect to system... if ((http = connect_to_printer(system, uri, sizeof(uri))) == NULL) { printf("FAIL (Unable to connect: %s)\n", cupsLastErrorString()); return (false); } // Test Get-System-Attributes fputs("Get-System-Attributes ", stdout); request = ippNewRequest(IPP_OP_GET_SYSTEM_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, "/ipp/system"); if (cupsLastError() != IPP_STATUS_OK) { printf("FAIL (%s)\n", cupsLastErrorString()); httpClose(http); ippDelete(response); return (false); } else { for (i = 0; i < (int)(sizeof(sattrs) / sizeof(sattrs[0])); i ++) { if (!ippFindAttribute(response, sattrs[i], IPP_TAG_ZERO)) { printf("FAIL (Missing required '%s' attribute in response)\n", sattrs[i]); httpClose(http); ippDelete(response); return (false); } } ippDelete(response); } // Test Get-Printers fputs("\nclient: Get-Printers ", stdout); request = ippNewRequest(IPP_OP_GET_PRINTERS); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "system-uri", NULL, "ipp://localhost/ipp/system"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, "/ipp/system"); if (cupsLastError() != IPP_STATUS_OK) { printf("FAIL (%s)\n", cupsLastErrorString()); httpClose(http); ippDelete(response); return (false); } else { for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++) { if (!ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) { printf("FAIL (Missing required '%s' attribute in response)\n", sattrs[i]); httpClose(http); ippDelete(response); return (false); } } ippDelete(response); } // Test Get-Printer-Attributes on / fputs("\nclient: Get-Printer-Attributes=/ ", stdout); request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "ipp://localhost/"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, "/"); if (cupsLastError() != IPP_STATUS_OK) { printf("FAIL (%s)\n", cupsLastErrorString()); httpClose(http); ippDelete(response); return (false); } else { for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++) { if (!ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) { printf("FAIL (Missing required '%s' attribute in response)\n", pattrs[i]); httpClose(http); ippDelete(response); return (false); } } ippDelete(response); } // Test Get-Printer-Attributes on /ipp/print fputs("\nclient: Get-Printer-Attributes=/ipp/print ", stdout); request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "ipp://localhost/ipp/print"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, "/ipp/print"); if (cupsLastError() != IPP_STATUS_OK) { printf("FAIL (%s)\n", cupsLastErrorString()); httpClose(http); ippDelete(response); return (false); } else { for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++) { if (!ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) { printf("FAIL (Missing required '%s' attribute in response)\n", pattrs[i]); httpClose(http); ippDelete(response); return (false); } } ippDelete(response); } httpClose(http); return (true); } #if defined(HAVE_LIBJPEG) || defined(HAVE_LIBPNG) // // 'test_image_files()' - Run image file tests. // static bool // O - `true` on success, `false` on failure test_image_files( pappl_system_t *system, // I - System const char *prompt, // I - Prompt for files const char *format, // I - MIME media type of files int num_files, // I - Number of files to print const char * const * files) // I - Files to print { int i, j, k, m; // Looping vars http_t *http; // HTTP connection char uri[1024], // "printer-uri" value filename[1024], // Print file job_name[1024]; // "job_name" value ipp_t *request, // Request *response; // Response int job_id; // "job-id" value ipp_jstate_t job_state; // "job-state" value static const int orients[] = // "orientation-requested" values { IPP_ORIENT_NONE, IPP_ORIENT_PORTRAIT, IPP_ORIENT_LANDSCAPE, IPP_ORIENT_REVERSE_PORTRAIT, IPP_ORIENT_REVERSE_LANDSCAPE }; static const char * const modes[] = // "print-color-mode" values { "auto", "color", "monochrome" }; static const char * const scalings[] =// "print-scaling" values { "auto", "auto-fit", "fill", "fit", "none" }; // Connect to system... if ((http = connect_to_printer(system, uri, sizeof(uri))) == NULL) { printf("FAIL (Unable to connect: %s)\n", cupsLastErrorString()); return (false); } // Print files... for (i = 0; i < num_files; i ++) { if (access(files[i], R_OK)) snprintf(filename, sizeof(filename), "testsuite/%s", files[i]); else strlcpy(filename, files[i], sizeof(filename)); for (j = 0; j < (int)(sizeof(orients) / sizeof(orients[0])); j ++) { for (k = 0; k < (int)(sizeof(modes) / sizeof(modes[0])); k ++) { for (m = 0; m < (int)(sizeof(scalings) / sizeof(scalings[0])); m ++) { // Stop the test if the system is shutdown (e.g. CTRL+C) if (papplSystemIsShutdown(system)) return (false); // Print the job... snprintf(job_name, sizeof(job_name), "%s+%s+%s+%s", files[i], ippEnumString("orientation-requested", orients[j]), modes[k], scalings[m]); request = ippNewRequest(IPP_OP_PRINT_JOB); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, job_name); ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "orientation-requested", orients[j]); ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "print-color-mode", NULL, modes[k]); ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "print-scaling", NULL, scalings[m]); response = cupsDoFileRequest(http, request, "/ipp/print", filename); if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST) { printf("FAIL (Unable to print %s: %s)\n", job_name, cupsLastErrorString()); ippDelete(response); httpClose(http); return (false); } job_id = ippGetInteger(ippFindAttribute(response, "job-id", IPP_TAG_INTEGER), 0); ippDelete(response); printf("%s (job-id=%d)\n%s: ", job_name, job_id, prompt); fflush(stdout); // Poll job status until completed... do { sleep(1); request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, "/ipp/print"); if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST) { printf("FAIL (Unable to get job state for '%s': %s)\n", job_name, cupsLastErrorString()); httpClose(http); ippDelete(response); return (false); } job_state = (ipp_jstate_t)ippGetInteger(ippFindAttribute(response, "job-state", IPP_TAG_ENUM), 0); ippDelete(response); } while (job_state < IPP_JSTATE_CANCELED); } } } } httpClose(http); return (true); } #endif // HAVE_LIBJPEG || HAVE_LIBPNG // // 'test_pwg_raster()' - Run PWG Raster tests. // static bool // O - `true` on success, `false` on failure test_pwg_raster(pappl_system_t *system) // I - System { bool ret = false; // Return value http_t *http = NULL; // HTTP connection char uri[1024], // "printer-uri" value filename[1024] = "", // Print file job_name[1024]; // "job_name" value ipp_t *request, // IPP request *response, // IPP response *supported = NULL; // Supported attributes ipp_attribute_t *mode_supported; // "print-color-mode-supported" attribute int i; // Looping var int job_id; // "job-id" value ipp_jstate_t job_state; // "job-state" value static const char * const modes[] = // "print-color-mode" values { "auto", "auto-monochrome", "color", "monochrome" }; // Connect to system... if ((http = connect_to_printer(system, uri, sizeof(uri))) == NULL) { printf("FAIL (Unable to connect: %s)\n", cupsLastErrorString()); return (false); } // Get printer capabilities fputs("Get-Printer-Attributes: ", stdout); request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "ipp://localhost/ipp/print"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); supported = cupsDoRequest(http, request, "/ipp/print"); if (cupsLastError() != IPP_STATUS_OK) { printf("FAIL (%s)\n", cupsLastErrorString()); goto done; } if ((mode_supported = ippFindAttribute(supported, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL) { puts("FAIL (Missing required 'print-color-mode-supported' attribute in response)"); goto done; } // Loop through the supported print-color-mode values... for (i = 0; i < (int)(sizeof(modes) / sizeof(modes[0])); i ++) { // Make raster data for this mode... printf("\npwg-raster: %s: ", modes[i]); fflush(stdout); if (!ippContainsString(mode_supported, modes[i])) continue; // Not supported, skip if (!make_raster_file(supported, strstr(modes[i], "monochrome") != NULL, filename, sizeof(filename))) break; // Error // Print the file... snprintf(job_name, sizeof(job_name), "pwg-raster-%s", modes[i]); request = ippNewRequest(IPP_OP_PRINT_JOB); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddString(request, IPP_TAG_OPERATION, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format", NULL, "image/pwg-raster"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, job_name); ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "print-color-mode", NULL, modes[i]); response = cupsDoFileRequest(http, request, "/ipp/print", filename); if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST) { printf("FAIL (Unable to print %s: %s)\n", job_name, cupsLastErrorString()); goto done; } job_id = ippGetInteger(ippFindAttribute(response, "job-id", IPP_TAG_INTEGER), 0); ippDelete(response); printf("job-id=%d ", job_id); fflush(stdout); // Poll job status until completed... do { sleep(1); request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); response = cupsDoRequest(http, request, "/ipp/print"); if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST) { printf("FAIL (Unable to get job state for '%s': %s)\n", job_name, cupsLastErrorString()); goto done; } job_state = (ipp_jstate_t)ippGetInteger(ippFindAttribute(response, "job-state", IPP_TAG_ENUM), 0); ippDelete(response); } while (job_state < IPP_JSTATE_CANCELED); // Cleanup... unlink(filename); } // If we complete the loop without errors, it is a successful run... ret = true; done: if (filename[0]) unlink(filename); httpClose(http); ippDelete(supported); return (ret); } // // 'usage()' - Show usage. // static int // O - Exit status usage(int status) // I - Exit status { puts("Usage: testpappl [OPTIONS] [\"SERVER NAME\"]"); puts("Options:"); puts(" --help Show help"); puts(" --list List devices"); puts(" --list-TYPE Lists devices of TYPE (dns-sd, local, network, usb)"); puts(" --version Show version"); puts(" -1 Single queue"); puts(" -A PAM-SERVICE Enable authentication using PAM service"); puts(" -c Do a clean run (no loading of state)"); puts(" -d SPOOL-DIRECTORY Set the spool directory"); puts(" -l LOG-FILE Set the log file"); puts(" -L LOG-LEVEL Set the log level (fatal, error, warn, info, debug)"); puts(" -m DRIVER-NAME Add a printer with the named driver"); puts(" -o OUTPUT-DIRECTORY Set the output directory (default '.')"); puts(" -p PORT Set the listen port (default auto)"); puts(" -t TEST-NAME Run the named test (see below)"); puts(" -T Enable TLS-only mode"); puts(" -U Enable USB printer gadget"); puts(""); puts("Tests:"); puts(" all All of the following tests"); puts(" client Simulated client tests"); puts(" jpeg JPEG image tests"); puts(" png PNG image tests"); puts(" pwg-raster PWG Raster tests"); return (status); } pappl-1.0.3/testsuite/testpappl.h000066400000000000000000000033261403603036100170450ustar00rootroot00000000000000// // Test suite header file for the Printer Application Framework // // Copyright © 2020 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // #ifndef _TESTPAPPL_H_ # define _TESTPAPPL_H_ // // Include necessary headers... // #include // // Globals... // extern pappl_pr_driver_t pwg_drivers[11]; #ifdef PWG_DRIVER pappl_pr_driver_t pwg_drivers[11] = { // Drivers { "pwg_2inch-203dpi-black_1", "PWG 2inch Label 203DPI Black", NULL, NULL }, { "pwg_2inch-300dpi-black_1", "PWG 2inch Label 300DPI Black", NULL, NULL }, { "pwg_4inch-203dpi-black_1", "PWG 4inch Label 203DPI Black", NULL, NULL }, { "pwg_4inch-300dpi-black_1", "PWG 4inch Label 300DPI Black", NULL, NULL }, { "pwg_common-300dpi-black_1", "PWG Office 300DPI Black", NULL, NULL }, { "pwg_common-300dpi-sgray_8", "PWG Office 300DPI sGray 8-bit", NULL, NULL }, { "pwg_common-300dpi-srgb_8", "PWG Office 300DPI sRGB 8-bit", NULL, NULL }, { "pwg_common-300dpi-600dpi-black_1", "PWG Office 300DPI 600DPI Black", NULL, NULL }, { "pwg_common-300dpi-600dpi-sgray_8", "PWG Office 300DPI 600DPI sGray 8-bit", NULL, NULL }, { "pwg_common-300dpi-600dpi-srgb_8", "PWG Office 300DPI 600DPI sRGB 8-bit", NULL, NULL }, { "pwg_fail-300dpi-black_1", "PWG Always Fails 300DPI Black", NULL, NULL } }; #endif // PWG_DRIVER // // Functions.. // extern const char *pwg_autoadd(const char *device_info, const char *device_uri, const char *device_id, void *data); extern bool pwg_callback(pappl_system_t *system, const char *driver_name, const char *device_uri, const char *device_id, pappl_pr_driver_data_t *driver_data, ipp_t **driver_attrs, void *data); #endif // !_TESTPAPPL_H_ pappl-1.0.3/xcode/000077500000000000000000000000001403603036100137255ustar00rootroot00000000000000pappl-1.0.3/xcode/Info.plist000066400000000000000000000015711403603036100157010ustar00rootroot00000000000000 CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString $(CURRENT_PROJECT_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright Copyright © 2019-2020 Michael Sweet. All rights reserved. pappl-1.0.3/xcode/config.h000066400000000000000000000016301403603036100153430ustar00rootroot00000000000000// // Xcode configuration header file for the Printer Application Framework // // Copyright © 2019-2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // // Version numbers #define PAPPL_VERSION "1.0.3" #define PAPPL_VERSION_MAJOR 1 #define PAPPL_VERSION_MINOR 0 // Location of CUPS config files #define CUPS_SERVERROOT "/private/etc/cups" // DNS-SD (mDNSResponder or Avahi) #define HAVE_DNSSD 1 /* #undef HAVE_AVAHI */ // GNU TLS /* #undef HAVE_GNUTLS */ // libjpeg #define HAVE_LIBJPEG 1 // libpng #define HAVE_LIBPNG 1 // libusb #define HAVE_LIBUSB 1 // libpam #define HAVE_LIBPAM 1 #define HAVE_SECURITY_PAM_APPL_H 1 /* #undef HAVE_PAM_PAM_APPL_H */ // String functions #define HAVE_STRLCPY 1 // Random number support #define HAVE_SYS_RANDOM_H 1 #define HAVE_ARC4RANDOM 1 /* #undef HAVE_GETRANDOM */ /* #undef HAVE_GNUTLS_RND */ pappl-1.0.3/xcode/pappl.xcodeproj/000077500000000000000000000000001403603036100170355ustar00rootroot00000000000000pappl-1.0.3/xcode/pappl.xcodeproj/project.pbxproj000066400000000000000000002555761403603036100221350ustar00rootroot00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 53; objects = { /* Begin PBXAggregateTarget section */ 27FFF2FA24328E24003C0B8F /* All */ = { isa = PBXAggregateTarget; buildConfigurationList = 27FFF2FB24328E24003C0B8F /* Build configuration list for PBXAggregateTarget "All" */; buildPhases = ( ); dependencies = ( 27FFF39E24329D3B003C0B8F /* PBXTargetDependency */, 27FFF3A024329D3B003C0B8F /* PBXTargetDependency */, 274AD665253F52C90033CAAB /* PBXTargetDependency */, 27FFF30124328E31003C0B8F /* PBXTargetDependency */, ); name = All; productName = All; }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ 27134E6C2548D1CD004D9027 /* system-printer.c in Sources */ = {isa = PBXBuildFile; fileRef = 27134E6B2548D1CD004D9027 /* system-printer.c */; }; 27134E6D2548D1CD004D9027 /* system-printer.c in Sources */ = {isa = PBXBuildFile; fileRef = 27134E6B2548D1CD004D9027 /* system-printer.c */; }; 2719D1B524732B1800299DA1 /* dnssd-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2719D1B424732B1700299DA1 /* dnssd-private.h */; }; 2719D1B624732B1800299DA1 /* dnssd-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2719D1B424732B1700299DA1 /* dnssd-private.h */; }; 27214FA524ED72B400E36FFC /* device-network.c in Sources */ = {isa = PBXBuildFile; fileRef = 27214FA324ED72B300E36FFC /* device-network.c */; }; 27214FA624ED72B400E36FFC /* device-network.c in Sources */ = {isa = PBXBuildFile; fileRef = 27214FA324ED72B300E36FFC /* device-network.c */; }; 27214FA724ED72B400E36FFC /* device-usb.c in Sources */ = {isa = PBXBuildFile; fileRef = 27214FA424ED72B400E36FFC /* device-usb.c */; }; 27214FA824ED72B400E36FFC /* device-usb.c in Sources */ = {isa = PBXBuildFile; fileRef = 27214FA424ED72B400E36FFC /* device-usb.c */; }; 27214FAA24ED72D600E36FFC /* device-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27214FA924ED72D600E36FFC /* device-private.h */; }; 27214FAB24ED72D600E36FFC /* device-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27214FA924ED72D600E36FFC /* device-private.h */; }; 2725631A243D629000A38E9F /* system-loadsave.c in Sources */ = {isa = PBXBuildFile; fileRef = 27256319243D628F00A38E9F /* system-loadsave.c */; }; 2725631B243D629000A38E9F /* system-loadsave.c in Sources */ = {isa = PBXBuildFile; fileRef = 27256319243D628F00A38E9F /* system-loadsave.c */; }; 2737B04824B3598400E6F38C /* log-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2737B04624B3598400E6F38C /* log-private.h */; }; 2737B04924B3598400E6F38C /* log-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2737B04624B3598400E6F38C /* log-private.h */; }; 2737B04A24B3598400E6F38C /* resource-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2737B04724B3598400E6F38C /* resource-private.h */; }; 2737B04B24B3598400E6F38C /* resource-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2737B04724B3598400E6F38C /* resource-private.h */; }; 274A1EDB242E7E1400DE387E /* pwg-driver.c in Sources */ = {isa = PBXBuildFile; fileRef = 274A1ED7242E7E1300DE387E /* pwg-driver.c */; }; 274A1EDC242E7E1400DE387E /* testpappl.c in Sources */ = {isa = PBXBuildFile; fileRef = 274A1ED8242E7E1300DE387E /* testpappl.c */; }; 274AD642253F52B10033CAAB /* pwg-driver.c in Sources */ = {isa = PBXBuildFile; fileRef = 274A1ED7242E7E1300DE387E /* pwg-driver.c */; }; 274AD645253F52B10033CAAB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DB2415EB740082CEA3 /* CoreFoundation.framework */; }; 274AD646253F52B10033CAAB /* libusb-1.0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E52415EBD70082CEA3 /* libusb-1.0.a */; }; 274AD647253F52B10033CAAB /* libpng16.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27FFF31824329B4A003C0B8F /* libpng16.a */; }; 274AD648253F52B10033CAAB /* libjpeg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E62415EBD70082CEA3 /* libjpeg.a */; }; 274AD649253F52B10033CAAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27FFF31324329B2D003C0B8F /* Security.framework */; }; 274AD64A253F52B10033CAAB /* libpappl_static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27FFF30824329AAF003C0B8F /* libpappl_static.a */; }; 274AD64B253F52B10033CAAB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E32415EBA80082CEA3 /* IOKit.framework */; }; 274AD64C253F52B10033CAAB /* libpam.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5F5241E72AE0082CEA3 /* libpam.tbd */; }; 274AD64D253F52B10033CAAB /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DD2415EB8A0082CEA3 /* libz.tbd */; }; 274AD64E253F52B10033CAAB /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5D92415EB6C0082CEA3 /* SystemConfiguration.framework */; }; 274AD64F253F52B10033CAAB /* libcups.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DF2415EB980082CEA3 /* libcups.tbd */; }; 274AD667253F52E30033CAAB /* testmainloop.c in Sources */ = {isa = PBXBuildFile; fileRef = 274AD666253F52E30033CAAB /* testmainloop.c */; }; 2763648425223F3200949C0B /* printer-usb.c in Sources */ = {isa = PBXBuildFile; fileRef = 2763648325223F3200949C0B /* printer-usb.c */; }; 2763648525223F3200949C0B /* printer-usb.c in Sources */ = {isa = PBXBuildFile; fileRef = 2763648325223F3200949C0B /* printer-usb.c */; }; 27A56492256769A9009501BD /* printer-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A56490256769A9009501BD /* printer-ipp.c */; }; 27A56493256769A9009501BD /* printer-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A56490256769A9009501BD /* printer-ipp.c */; }; 27A56494256769A9009501BD /* system-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A56491256769A9009501BD /* system-ipp.c */; }; 27A56495256769A9009501BD /* system-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A56491256769A9009501BD /* system-ipp.c */; }; 27A564A625676AE3009501BD /* client-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A564A525676AE3009501BD /* client-ipp.c */; }; 27A564A725676AE3009501BD /* client-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A564A525676AE3009501BD /* client-ipp.c */; }; 27A564B325677057009501BD /* job-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A564B225677057009501BD /* job-ipp.c */; }; 27A564B425677057009501BD /* job-ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27A564B225677057009501BD /* job-ipp.c */; }; 27AB72B424740B3400691FE7 /* link.c in Sources */ = {isa = PBXBuildFile; fileRef = 27AB72B324740B3300691FE7 /* link.c */; }; 27AB72B524740B3400691FE7 /* link.c in Sources */ = {isa = PBXBuildFile; fileRef = 27AB72B324740B3300691FE7 /* link.c */; }; 27D676232493D73E008F734C /* mainloop-subcommands.c in Sources */ = {isa = PBXBuildFile; fileRef = 27D6761F2493D73D008F734C /* mainloop-subcommands.c */; }; 27D676242493D73E008F734C /* mainloop-subcommands.c in Sources */ = {isa = PBXBuildFile; fileRef = 27D6761F2493D73D008F734C /* mainloop-subcommands.c */; }; 27D676252493D73E008F734C /* mainloop.c in Sources */ = {isa = PBXBuildFile; fileRef = 27D676202493D73E008F734C /* mainloop.c */; }; 27D676262493D73E008F734C /* mainloop.c in Sources */ = {isa = PBXBuildFile; fileRef = 27D676202493D73E008F734C /* mainloop.c */; }; 27D676272493D73E008F734C /* mainloop.h in Headers */ = {isa = PBXBuildFile; fileRef = 27D676212493D73E008F734C /* mainloop.h */; }; 27D676282493D73E008F734C /* mainloop.h in Headers */ = {isa = PBXBuildFile; fileRef = 27D676212493D73E008F734C /* mainloop.h */; }; 27D676292493D73E008F734C /* mainloop-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27D676222493D73E008F734C /* mainloop-private.h */; }; 27D6762A2493D73E008F734C /* mainloop-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27D676222493D73E008F734C /* mainloop-private.h */; }; 27D6762C2493EF96008F734C /* mainloop-support.c in Sources */ = {isa = PBXBuildFile; fileRef = 27D6762B2493EF95008F734C /* mainloop-support.c */; }; 27D6762D2493EF96008F734C /* mainloop-support.c in Sources */ = {isa = PBXBuildFile; fileRef = 27D6762B2493EF95008F734C /* mainloop-support.c */; }; 27DF62F12450992D00501447 /* job-filter.c in Sources */ = {isa = PBXBuildFile; fileRef = 27DF62F02450992D00501447 /* job-filter.c */; }; 27DF62F22450992D00501447 /* job-filter.c in Sources */ = {isa = PBXBuildFile; fileRef = 27DF62F02450992D00501447 /* job-filter.c */; }; 27E5AEA3246B6A4800FFD958 /* printer-raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 27E5AEA2246B6A4700FFD958 /* printer-raw.c */; }; 27E5AEA4246B6A4800FFD958 /* printer-raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 27E5AEA2246B6A4700FFD958 /* printer-raw.c */; }; 27F4285824F4080600C7ADCE /* device-file.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F4285724F4080600C7ADCE /* device-file.c */; }; 27F4285924F4080600C7ADCE /* device-file.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F4285724F4080600C7ADCE /* device-file.c */; }; 27FFF3032432901A003C0B8F /* libcups.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DF2415EB980082CEA3 /* libcups.tbd */; }; 27FFF30E24329B2D003C0B8F /* libpappl_static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27FFF30824329AAF003C0B8F /* libpappl_static.a */; }; 27FFF30F24329B2D003C0B8F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E32415EBA80082CEA3 /* IOKit.framework */; }; 27FFF31024329B2D003C0B8F /* libpam.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5F5241E72AE0082CEA3 /* libpam.tbd */; }; 27FFF31124329B2D003C0B8F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DD2415EB8A0082CEA3 /* libz.tbd */; }; 27FFF31224329B2D003C0B8F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5D92415EB6C0082CEA3 /* SystemConfiguration.framework */; }; 27FFF31524329B2D003C0B8F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27FFF31324329B2D003C0B8F /* Security.framework */; }; 27FFF31724329B4A003C0B8F /* libjpeg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E62415EBD70082CEA3 /* libjpeg.a */; }; 27FFF31924329B4A003C0B8F /* libpng16.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27FFF31824329B4A003C0B8F /* libpng16.a */; }; 27FFF31A24329B61003C0B8F /* base.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C66240D8896001D2A90 /* base.h */; }; 27FFF31B24329B61003C0B8F /* base-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5F3241E72910082CEA3 /* base-private.h */; }; 27FFF31C24329B61003C0B8F /* client.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6E240D8896001D2A90 /* client.h */; }; 27FFF31D24329B61003C0B8F /* client-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8D240D9067001D2A90 /* client-private.h */; }; 27FFF31E24329B61003C0B8F /* client.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6C240D8896001D2A90 /* client.c */; }; 27FFF31F24329B61003C0B8F /* client-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377224119E39008AECA4 /* client-accessors.c */; }; 27FFF32024329B61003C0B8F /* client-auth.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6F240D8896001D2A90 /* client-auth.c */; }; 27FFF32124329B61003C0B8F /* client-webif.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EE39CE242AE7D800179844 /* client-webif.c */; }; 27FFF32224329B61003C0B8F /* config.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C87240D8E69001D2A90 /* config.h */; }; 27FFF32324329B61003C0B8F /* contact.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F656E72430DBFC00055A4D /* contact.c */; }; 27FFF32424329B61003C0B8F /* device.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C65240D8896001D2A90 /* device.h */; }; 27FFF32524329B61003C0B8F /* device.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6B240D8896001D2A90 /* device.c */; }; 27FFF32624329B61003C0B8F /* dnssd.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C73240D8896001D2A90 /* dnssd.c */; }; 27FFF32924329B61003C0B8F /* job.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C70240D8896001D2A90 /* job.h */; }; 27FFF32A24329B61003C0B8F /* job-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8C240D9067001D2A90 /* job-private.h */; }; 27FFF32B24329B61003C0B8F /* job.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C64240D8896001D2A90 /* job.c */; }; 27FFF32C24329B61003C0B8F /* job-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377524119E3A008AECA4 /* job-accessors.c */; }; 27FFF32D24329B61003C0B8F /* job-process.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C74240D8896001D2A90 /* job-process.c */; }; 27FFF32E24329B61003C0B8F /* log.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8A240D9066001D2A90 /* log.h */; }; 27FFF32F24329B61003C0B8F /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C72240D8896001D2A90 /* log.c */; }; 27FFF33024329B61003C0B8F /* lookup.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5ED241C85DF0082CEA3 /* lookup.c */; }; 27FFF33124329B61003C0B8F /* pappl.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C68240D8896001D2A90 /* pappl.h */; }; 27FFF33224329B61003C0B8F /* pappl-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C69240D8896001D2A90 /* pappl-private.h */; }; 27FFF33324329B61003C0B8F /* printer.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C63240D8896001D2A90 /* printer.h */; }; 27FFF33424329B61003C0B8F /* printer-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8B240D9067001D2A90 /* printer-private.h */; }; 27FFF33524329B61003C0B8F /* printer.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6D240D8896001D2A90 /* printer.c */; }; 27FFF33624329B61003C0B8F /* printer-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377124119E37008AECA4 /* printer-accessors.c */; }; 27FFF33724329B61003C0B8F /* printer-driver.c in Sources */ = {isa = PBXBuildFile; fileRef = 27910A192421626800D01A3F /* printer-driver.c */; }; 27FFF33824329B61003C0B8F /* printer-support.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377424119E3A008AECA4 /* printer-support.c */; }; 27FFF33924329B61003C0B8F /* printer-webif.c in Sources */ = {isa = PBXBuildFile; fileRef = 273FA876240FED97007982BE /* printer-webif.c */; }; 27FFF33A24329B61003C0B8F /* resource.c in Sources */ = {isa = PBXBuildFile; fileRef = 273FA875240FED96007982BE /* resource.c */; }; 27FFF33B24329B61003C0B8F /* snmp-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5EF241DB8380082CEA3 /* snmp-private.h */; }; 27FFF33C24329B61003C0B8F /* snmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5F0241DB8390082CEA3 /* snmp.c */; }; 27FFF33D24329B61003C0B8F /* system.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6A240D8896001D2A90 /* system.h */; }; 27FFF33E24329B61003C0B8F /* system-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C89240D9066001D2A90 /* system-private.h */; }; 27FFF33F24329B61003C0B8F /* system.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C67240D8896001D2A90 /* system.c */; }; 27FFF34024329B61003C0B8F /* system-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377324119E39008AECA4 /* system-accessors.c */; }; 27FFF34124329B61003C0B8F /* system-webif.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EE39CF242AE7D900179844 /* system-webif.c */; }; 27FFF34224329B61003C0B8F /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F656E52430DB8D00055A4D /* util.c */; }; 27FFF34324329B82003C0B8F /* base.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C66240D8896001D2A90 /* base.h */; }; 27FFF34424329B82003C0B8F /* base-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27EFC5F3241E72910082CEA3 /* base-private.h */; }; 27FFF34524329B82003C0B8F /* client.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C6E240D8896001D2A90 /* client.h */; }; 27FFF34624329B82003C0B8F /* client-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8D240D9067001D2A90 /* client-private.h */; }; 27FFF34724329B82003C0B8F /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C87240D8E69001D2A90 /* config.h */; }; 27FFF34824329B82003C0B8F /* device.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C65240D8896001D2A90 /* device.h */; }; 27FFF34924329B83003C0B8F /* job.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C70240D8896001D2A90 /* job.h */; }; 27FFF34A24329B83003C0B8F /* job-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8C240D9067001D2A90 /* job-private.h */; }; 27FFF34B24329B83003C0B8F /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8A240D9066001D2A90 /* log.h */; }; 27FFF34C24329B83003C0B8F /* pappl.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C68240D8896001D2A90 /* pappl.h */; }; 27FFF34D24329B83003C0B8F /* pappl-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C69240D8896001D2A90 /* pappl-private.h */; }; 27FFF34E24329B83003C0B8F /* printer.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C63240D8896001D2A90 /* printer.h */; }; 27FFF34F24329B83003C0B8F /* printer-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8B240D9067001D2A90 /* printer-private.h */; }; 27FFF35024329B83003C0B8F /* snmp-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27EFC5EF241DB8380082CEA3 /* snmp-private.h */; }; 27FFF35124329B83003C0B8F /* system.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C6A240D8896001D2A90 /* system.h */; }; 27FFF35224329B83003C0B8F /* system-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C89240D9066001D2A90 /* system-private.h */; }; 27FFF35524329C9E003C0B8F /* base.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C66240D8896001D2A90 /* base.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35624329C9E003C0B8F /* client.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C6E240D8896001D2A90 /* client.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35724329C9E003C0B8F /* device.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C65240D8896001D2A90 /* device.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35824329C9E003C0B8F /* job.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C70240D8896001D2A90 /* job.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35924329C9E003C0B8F /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8A240D9066001D2A90 /* log.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35A24329C9E003C0B8F /* pappl.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C68240D8896001D2A90 /* pappl.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35B24329C9E003C0B8F /* printer.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C63240D8896001D2A90 /* printer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35C24329C9E003C0B8F /* system.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C6A240D8896001D2A90 /* system.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27FFF35D24329C9E003C0B8F /* base-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27EFC5F3241E72910082CEA3 /* base-private.h */; }; 27FFF35E24329C9E003C0B8F /* client-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8D240D9067001D2A90 /* client-private.h */; }; 27FFF35F24329C9E003C0B8F /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C87240D8E69001D2A90 /* config.h */; }; 27FFF36024329C9E003C0B8F /* job-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8C240D9067001D2A90 /* job-private.h */; }; 27FFF36124329C9E003C0B8F /* pappl-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C69240D8896001D2A90 /* pappl-private.h */; }; 27FFF36224329C9E003C0B8F /* printer-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C8B240D9067001D2A90 /* printer-private.h */; }; 27FFF36324329C9E003C0B8F /* snmp-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27EFC5EF241DB8380082CEA3 /* snmp-private.h */; }; 27FFF36424329C9E003C0B8F /* system-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 27905C89240D9066001D2A90 /* system-private.h */; }; 27FFF36624329C9E003C0B8F /* base.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C66240D8896001D2A90 /* base.h */; }; 27FFF36724329C9E003C0B8F /* base-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5F3241E72910082CEA3 /* base-private.h */; }; 27FFF36824329C9E003C0B8F /* client.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6E240D8896001D2A90 /* client.h */; }; 27FFF36924329C9E003C0B8F /* client-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8D240D9067001D2A90 /* client-private.h */; }; 27FFF36A24329C9E003C0B8F /* client.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6C240D8896001D2A90 /* client.c */; }; 27FFF36B24329C9E003C0B8F /* client-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377224119E39008AECA4 /* client-accessors.c */; }; 27FFF36C24329C9E003C0B8F /* client-auth.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6F240D8896001D2A90 /* client-auth.c */; }; 27FFF36D24329C9E003C0B8F /* client-webif.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EE39CE242AE7D800179844 /* client-webif.c */; }; 27FFF36E24329C9E003C0B8F /* config.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C87240D8E69001D2A90 /* config.h */; }; 27FFF36F24329C9E003C0B8F /* contact.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F656E72430DBFC00055A4D /* contact.c */; }; 27FFF37024329C9E003C0B8F /* device.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C65240D8896001D2A90 /* device.h */; }; 27FFF37124329C9E003C0B8F /* device.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6B240D8896001D2A90 /* device.c */; }; 27FFF37224329C9E003C0B8F /* dnssd.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C73240D8896001D2A90 /* dnssd.c */; }; 27FFF37524329C9E003C0B8F /* job.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C70240D8896001D2A90 /* job.h */; }; 27FFF37624329C9E003C0B8F /* job-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8C240D9067001D2A90 /* job-private.h */; }; 27FFF37724329C9E003C0B8F /* job.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C64240D8896001D2A90 /* job.c */; }; 27FFF37824329C9E003C0B8F /* job-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377524119E3A008AECA4 /* job-accessors.c */; }; 27FFF37924329C9E003C0B8F /* job-process.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C74240D8896001D2A90 /* job-process.c */; }; 27FFF37A24329C9E003C0B8F /* log.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8A240D9066001D2A90 /* log.h */; }; 27FFF37B24329C9E003C0B8F /* log.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C72240D8896001D2A90 /* log.c */; }; 27FFF37C24329C9E003C0B8F /* lookup.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5ED241C85DF0082CEA3 /* lookup.c */; }; 27FFF37D24329C9E003C0B8F /* pappl.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C68240D8896001D2A90 /* pappl.h */; }; 27FFF37E24329C9E003C0B8F /* pappl-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C69240D8896001D2A90 /* pappl-private.h */; }; 27FFF37F24329C9E003C0B8F /* printer.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C63240D8896001D2A90 /* printer.h */; }; 27FFF38024329C9E003C0B8F /* printer-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C8B240D9067001D2A90 /* printer-private.h */; }; 27FFF38124329C9E003C0B8F /* printer.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6D240D8896001D2A90 /* printer.c */; }; 27FFF38224329C9E003C0B8F /* printer-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377124119E37008AECA4 /* printer-accessors.c */; }; 27FFF38324329C9E003C0B8F /* printer-driver.c in Sources */ = {isa = PBXBuildFile; fileRef = 27910A192421626800D01A3F /* printer-driver.c */; }; 27FFF38424329C9E003C0B8F /* printer-support.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377424119E3A008AECA4 /* printer-support.c */; }; 27FFF38524329C9E003C0B8F /* printer-webif.c in Sources */ = {isa = PBXBuildFile; fileRef = 273FA876240FED97007982BE /* printer-webif.c */; }; 27FFF38624329C9E003C0B8F /* resource.c in Sources */ = {isa = PBXBuildFile; fileRef = 273FA875240FED96007982BE /* resource.c */; }; 27FFF38724329C9E003C0B8F /* snmp-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5EF241DB8380082CEA3 /* snmp-private.h */; }; 27FFF38824329C9E003C0B8F /* snmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EFC5F0241DB8390082CEA3 /* snmp.c */; }; 27FFF38924329C9E003C0B8F /* system.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C6A240D8896001D2A90 /* system.h */; }; 27FFF38A24329C9E003C0B8F /* system-private.h in Sources */ = {isa = PBXBuildFile; fileRef = 27905C89240D9066001D2A90 /* system-private.h */; }; 27FFF38B24329C9E003C0B8F /* system.c in Sources */ = {isa = PBXBuildFile; fileRef = 27905C67240D8896001D2A90 /* system.c */; }; 27FFF38C24329C9E003C0B8F /* system-accessors.c in Sources */ = {isa = PBXBuildFile; fileRef = 279D377324119E39008AECA4 /* system-accessors.c */; }; 27FFF38D24329C9E003C0B8F /* system-webif.c in Sources */ = {isa = PBXBuildFile; fileRef = 27EE39CF242AE7D900179844 /* system-webif.c */; }; 27FFF38E24329C9E003C0B8F /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F656E52430DB8D00055A4D /* util.c */; }; 27FFF39424329D16003C0B8F /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DB2415EB740082CEA3 /* CoreFoundation.framework */; }; 27FFF39524329D16003C0B8F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E32415EBA80082CEA3 /* IOKit.framework */; }; 27FFF39624329D16003C0B8F /* libcups.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DF2415EB980082CEA3 /* libcups.tbd */; }; 27FFF39724329D16003C0B8F /* libpam.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5F5241E72AE0082CEA3 /* libpam.tbd */; }; 27FFF39824329D16003C0B8F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DD2415EB8A0082CEA3 /* libz.tbd */; }; 27FFF39924329D16003C0B8F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5D92415EB6C0082CEA3 /* SystemConfiguration.framework */; }; 27FFF39A24329D30003C0B8F /* libjpeg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E62415EBD70082CEA3 /* libjpeg.a */; }; 27FFF39B24329D30003C0B8F /* libpng16.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27FFF31824329B4A003C0B8F /* libpng16.a */; }; 27FFF39C24329D30003C0B8F /* libusb-1.0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E52415EBD70082CEA3 /* libusb-1.0.a */; }; 27FFF3A124329E59003C0B8F /* libusb-1.0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5E52415EBD70082CEA3 /* libusb-1.0.a */; }; 27FFF3A224329E6F003C0B8F /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27EFC5DB2415EB740082CEA3 /* CoreFoundation.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 274AD640253F52B10033CAAB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 273C6EEC240D8729000F85E7 /* Project object */; proxyType = 1; remoteGlobalIDString = 27FFF30724329AAF003C0B8F; remoteInfo = libpappl; }; 274AD664253F52C90033CAAB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 273C6EEC240D8729000F85E7 /* Project object */; proxyType = 1; remoteGlobalIDString = 274AD63E253F52B10033CAAB; remoteInfo = testmainloop; }; 27FFF30024328E31003C0B8F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 273C6EEC240D8729000F85E7 /* Project object */; proxyType = 1; remoteGlobalIDString = 274A1ECF242E7DEA00DE387E; remoteInfo = testsuite; }; 27FFF30C24329AC7003C0B8F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 273C6EEC240D8729000F85E7 /* Project object */; proxyType = 1; remoteGlobalIDString = 27FFF30724329AAF003C0B8F; remoteInfo = libpappl; }; 27FFF39D24329D3B003C0B8F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 273C6EEC240D8729000F85E7 /* Project object */; proxyType = 1; remoteGlobalIDString = 27FFF35324329C9E003C0B8F; remoteInfo = libpappl; }; 27FFF39F24329D3B003C0B8F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 273C6EEC240D8729000F85E7 /* Project object */; proxyType = 1; remoteGlobalIDString = 27FFF30724329AAF003C0B8F; remoteInfo = libpappl_static; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 274A1ECE242E7DEA00DE387E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; 274AD650253F52B10033CAAB /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 27134E6B2548D1CD004D9027 /* system-printer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "system-printer.c"; path = "../pappl/system-printer.c"; sourceTree = ""; }; 2719D1B424732B1700299DA1 /* dnssd-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "dnssd-private.h"; path = "../pappl/dnssd-private.h"; sourceTree = ""; }; 27214FA324ED72B300E36FFC /* device-network.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "device-network.c"; path = "../pappl/device-network.c"; sourceTree = ""; }; 27214FA424ED72B400E36FFC /* device-usb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "device-usb.c"; path = "../pappl/device-usb.c"; sourceTree = ""; }; 27214FA924ED72D600E36FFC /* device-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "device-private.h"; path = "../pappl/device-private.h"; sourceTree = ""; }; 27256319243D628F00A38E9F /* system-loadsave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "system-loadsave.c"; path = "../pappl/system-loadsave.c"; sourceTree = ""; }; 2737B04624B3598400E6F38C /* log-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "log-private.h"; path = "../pappl/log-private.h"; sourceTree = ""; }; 2737B04724B3598400E6F38C /* resource-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "resource-private.h"; path = "../pappl/resource-private.h"; sourceTree = ""; }; 273C6EF9240D8729000F85E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 273FA875240FED96007982BE /* resource.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = resource.c; path = ../pappl/resource.c; sourceTree = ""; }; 273FA876240FED97007982BE /* printer-webif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "printer-webif.c"; path = "../pappl/printer-webif.c"; sourceTree = ""; }; 274A1ED0242E7DEA00DE387E /* testpappl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testpappl; sourceTree = BUILT_PRODUCTS_DIR; }; 274A1ED7242E7E1300DE387E /* pwg-driver.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "pwg-driver.c"; path = "../testsuite/pwg-driver.c"; sourceTree = ""; }; 274A1ED8242E7E1300DE387E /* testpappl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testpappl.c; path = ../testsuite/testpappl.c; sourceTree = ""; }; 274A1ED9242E7E1400DE387E /* testpappl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = testpappl.h; path = ../testsuite/testpappl.h; sourceTree = ""; }; 274AD654253F52B10033CAAB /* testmainloop */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testmainloop; sourceTree = BUILT_PRODUCTS_DIR; }; 274AD666253F52E30033CAAB /* testmainloop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testmainloop.c; path = ../testsuite/testmainloop.c; sourceTree = ""; }; 2763648325223F3200949C0B /* printer-usb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "printer-usb.c"; path = "../pappl/printer-usb.c"; sourceTree = ""; }; 276EE2672550620200429BCE /* pappl-client-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-client-body.man"; path = "../man/pappl-client-body.man"; sourceTree = ""; }; 276EE2682550620200429BCE /* pappl-printer-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-printer-body.man"; path = "../man/pappl-printer-body.man"; sourceTree = ""; }; 276EE2692550620200429BCE /* pappl-footer.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-footer.man"; path = "../man/pappl-footer.man"; sourceTree = ""; }; 276EE26A2550620200429BCE /* pappl.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = pappl.1; path = ../man/pappl.1; sourceTree = ""; }; 276EE26B2550620200429BCE /* pappl-resource-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-resource-body.man"; path = "../man/pappl-resource-body.man"; sourceTree = ""; }; 276EE26C2550620200429BCE /* pappl-printer.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-printer.3"; path = "../man/pappl-printer.3"; sourceTree = ""; }; 276EE26D2550620200429BCE /* pappl-mainloop-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-mainloop-body.man"; path = "../man/pappl-mainloop-body.man"; sourceTree = ""; }; 276EE26E2550620200429BCE /* pappl-client.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-client.3"; path = "../man/pappl-client.3"; sourceTree = ""; }; 276EE26F2550620200429BCE /* pappl-log.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-log.3"; path = "../man/pappl-log.3"; sourceTree = ""; }; 276EE2702550620200429BCE /* pappl-job.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-job.3"; path = "../man/pappl-job.3"; sourceTree = ""; }; 276EE2712550620200429BCE /* pappl-device-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-device-body.man"; path = "../man/pappl-device-body.man"; sourceTree = ""; }; 276EE2722550620200429BCE /* pappl-makeresheader.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = "pappl-makeresheader.1"; path = "../man/pappl-makeresheader.1"; sourceTree = ""; }; 276EE2732550620200429BCE /* pappl-job-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-job-body.man"; path = "../man/pappl-job-body.man"; sourceTree = ""; }; 276EE2742550620200429BCE /* pappl-system.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-system.3"; path = "../man/pappl-system.3"; sourceTree = ""; }; 276EE2752550620200429BCE /* pappl-system-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-system-body.man"; path = "../man/pappl-system-body.man"; sourceTree = ""; }; 276EE2762550620200429BCE /* pappl-log-body.man */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-log-body.man"; path = "../man/pappl-log-body.man"; sourceTree = ""; }; 276EE2772550620200429BCE /* pappl-device.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-device.3"; path = "../man/pappl-device.3"; sourceTree = ""; }; 276EE2782550620200429BCE /* pappl-mainloop.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-mainloop.3"; path = "../man/pappl-mainloop.3"; sourceTree = ""; }; 276EE2792550620300429BCE /* pappl-resource.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "pappl-resource.3"; path = "../man/pappl-resource.3"; sourceTree = ""; }; 276EE27A2550621400429BCE /* pappl-body.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = "pappl-body.md"; path = "../doc/pappl-body.md"; sourceTree = ""; }; 276EE27B2550621400429BCE /* pappl.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = pappl.html; path = ../doc/pappl.html; sourceTree = ""; }; 276EE27C2550621400429BCE /* pappl-512.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "pappl-512.png"; path = "../doc/pappl-512.png"; sourceTree = ""; }; 27905C63240D8896001D2A90 /* printer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = printer.h; path = ../pappl/printer.h; sourceTree = ""; }; 27905C64240D8896001D2A90 /* job.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = job.c; path = ../pappl/job.c; sourceTree = ""; }; 27905C65240D8896001D2A90 /* device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = device.h; path = ../pappl/device.h; sourceTree = ""; }; 27905C66240D8896001D2A90 /* base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base.h; path = ../pappl/base.h; sourceTree = ""; }; 27905C67240D8896001D2A90 /* system.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = system.c; path = ../pappl/system.c; sourceTree = ""; }; 27905C68240D8896001D2A90 /* pappl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pappl.h; path = ../pappl/pappl.h; sourceTree = ""; }; 27905C69240D8896001D2A90 /* pappl-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "pappl-private.h"; path = "../pappl/pappl-private.h"; sourceTree = ""; }; 27905C6A240D8896001D2A90 /* system.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = system.h; path = ../pappl/system.h; sourceTree = ""; }; 27905C6B240D8896001D2A90 /* device.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = device.c; path = ../pappl/device.c; sourceTree = ""; }; 27905C6C240D8896001D2A90 /* client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = client.c; path = ../pappl/client.c; sourceTree = ""; }; 27905C6D240D8896001D2A90 /* printer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = printer.c; path = ../pappl/printer.c; sourceTree = ""; }; 27905C6E240D8896001D2A90 /* client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = client.h; path = ../pappl/client.h; sourceTree = ""; }; 27905C6F240D8896001D2A90 /* client-auth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "client-auth.c"; path = "../pappl/client-auth.c"; sourceTree = ""; }; 27905C70240D8896001D2A90 /* job.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = job.h; path = ../pappl/job.h; sourceTree = ""; }; 27905C72240D8896001D2A90 /* log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = log.c; path = ../pappl/log.c; sourceTree = ""; }; 27905C73240D8896001D2A90 /* dnssd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssd.c; path = ../pappl/dnssd.c; sourceTree = ""; }; 27905C74240D8896001D2A90 /* job-process.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "job-process.c"; path = "../pappl/job-process.c"; sourceTree = ""; }; 27905C87240D8E69001D2A90 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; 27905C89240D9066001D2A90 /* system-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "system-private.h"; path = "../pappl/system-private.h"; sourceTree = ""; }; 27905C8A240D9066001D2A90 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = log.h; path = ../pappl/log.h; sourceTree = ""; }; 27905C8B240D9067001D2A90 /* printer-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "printer-private.h"; path = "../pappl/printer-private.h"; sourceTree = ""; }; 27905C8C240D9067001D2A90 /* job-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "job-private.h"; path = "../pappl/job-private.h"; sourceTree = ""; }; 27905C8D240D9067001D2A90 /* client-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "client-private.h"; path = "../pappl/client-private.h"; sourceTree = ""; }; 27910A192421626800D01A3F /* printer-driver.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "printer-driver.c"; path = "../pappl/printer-driver.c"; sourceTree = ""; }; 279D377124119E37008AECA4 /* printer-accessors.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "printer-accessors.c"; path = "../pappl/printer-accessors.c"; sourceTree = ""; }; 279D377224119E39008AECA4 /* client-accessors.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "client-accessors.c"; path = "../pappl/client-accessors.c"; sourceTree = ""; }; 279D377324119E39008AECA4 /* system-accessors.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "system-accessors.c"; path = "../pappl/system-accessors.c"; sourceTree = ""; }; 279D377424119E3A008AECA4 /* printer-support.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "printer-support.c"; path = "../pappl/printer-support.c"; sourceTree = ""; }; 279D377524119E3A008AECA4 /* job-accessors.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "job-accessors.c"; path = "../pappl/job-accessors.c"; sourceTree = ""; }; 27A56490256769A9009501BD /* printer-ipp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "printer-ipp.c"; path = "../pappl/printer-ipp.c"; sourceTree = ""; }; 27A56491256769A9009501BD /* system-ipp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "system-ipp.c"; path = "../pappl/system-ipp.c"; sourceTree = ""; }; 27A564A525676AE3009501BD /* client-ipp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "client-ipp.c"; path = "../pappl/client-ipp.c"; sourceTree = ""; }; 27A564B225677057009501BD /* job-ipp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "job-ipp.c"; path = "../pappl/job-ipp.c"; sourceTree = ""; }; 27AB72B324740B3300691FE7 /* link.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = link.c; path = ../pappl/link.c; sourceTree = ""; }; 27BFB1BB240DF06A00CE29F0 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 27D6761F2493D73D008F734C /* mainloop-subcommands.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mainloop-subcommands.c"; path = "../pappl/mainloop-subcommands.c"; sourceTree = ""; }; 27D676202493D73E008F734C /* mainloop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mainloop.c; path = ../pappl/mainloop.c; sourceTree = ""; }; 27D676212493D73E008F734C /* mainloop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mainloop.h; path = ../pappl/mainloop.h; sourceTree = ""; }; 27D676222493D73E008F734C /* mainloop-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mainloop-private.h"; path = "../pappl/mainloop-private.h"; sourceTree = ""; }; 27D6762B2493EF95008F734C /* mainloop-support.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mainloop-support.c"; path = "../pappl/mainloop-support.c"; sourceTree = ""; }; 27DF62F02450992D00501447 /* job-filter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "job-filter.c"; path = "../pappl/job-filter.c"; sourceTree = ""; }; 27E5AEA2246B6A4700FFD958 /* printer-raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "printer-raw.c"; path = "../pappl/printer-raw.c"; sourceTree = ""; }; 27EE39CE242AE7D800179844 /* client-webif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "client-webif.c"; path = "../pappl/client-webif.c"; sourceTree = ""; }; 27EE39CF242AE7D900179844 /* system-webif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "system-webif.c"; path = "../pappl/system-webif.c"; sourceTree = ""; }; 27EFC5D52415EB550082CEA3 /* libcups.2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcups.2.tbd; path = usr/lib/libcups.2.tbd; sourceTree = SDKROOT; }; 27EFC5D72415EB610082CEA3 /* libcupsimage.2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcupsimage.2.tbd; path = usr/lib/libcupsimage.2.tbd; sourceTree = SDKROOT; }; 27EFC5D92415EB6C0082CEA3 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 27EFC5DB2415EB740082CEA3 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 27EFC5DD2415EB8A0082CEA3 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 27EFC5DF2415EB980082CEA3 /* libcups.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcups.tbd; path = usr/lib/libcups.tbd; sourceTree = SDKROOT; }; 27EFC5E02415EB980082CEA3 /* libcupsimage.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcupsimage.tbd; path = usr/lib/libcupsimage.tbd; sourceTree = SDKROOT; }; 27EFC5E32415EBA80082CEA3 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 27EFC5E52415EBD70082CEA3 /* libusb-1.0.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libusb-1.0.a"; path = "../../../../../usr/local/lib/libusb-1.0.a"; sourceTree = ""; }; 27EFC5E62415EBD70082CEA3 /* libjpeg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libjpeg.a; path = ../../../../../usr/local/lib/libjpeg.a; sourceTree = ""; }; 27EFC5E72415EBD70082CEA3 /* libpng16.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng16.a; path = ../../../../../usr/local/Cellar/libpng/1.6.34/lib/libpng16.a; sourceTree = ""; }; 27EFC5ED241C85DF0082CEA3 /* lookup.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lookup.c; path = ../pappl/lookup.c; sourceTree = ""; }; 27EFC5EF241DB8380082CEA3 /* snmp-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "snmp-private.h"; path = "../pappl/snmp-private.h"; sourceTree = ""; }; 27EFC5F0241DB8390082CEA3 /* snmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = snmp.c; path = ../pappl/snmp.c; sourceTree = ""; }; 27EFC5F3241E72910082CEA3 /* base-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "base-private.h"; path = "../pappl/base-private.h"; sourceTree = ""; }; 27EFC5F5241E72AE0082CEA3 /* libpam.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libpam.tbd; path = usr/lib/libpam.tbd; sourceTree = SDKROOT; }; 27F4285724F4080600C7ADCE /* device-file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "device-file.c"; path = "../pappl/device-file.c"; sourceTree = ""; }; 27F656E52430DB8D00055A4D /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = util.c; path = ../pappl/util.c; sourceTree = ""; }; 27F656E72430DBFC00055A4D /* contact.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = contact.c; path = ../pappl/contact.c; sourceTree = ""; }; 27FFF30824329AAF003C0B8F /* libpappl_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libpappl_static.a; sourceTree = BUILT_PRODUCTS_DIR; }; 27FFF31324329B2D003C0B8F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 27FFF31424329B2D003C0B8F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 27FFF31824329B4A003C0B8F /* libpng16.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng16.a; path = ../../../../../usr/local/lib/libpng16.a; sourceTree = ""; }; 27FFF39324329C9E003C0B8F /* libpappl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libpappl.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 274A1ECD242E7DEA00DE387E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 27FFF3A224329E6F003C0B8F /* CoreFoundation.framework in Frameworks */, 27FFF3A124329E59003C0B8F /* libusb-1.0.a in Frameworks */, 27FFF31924329B4A003C0B8F /* libpng16.a in Frameworks */, 27FFF31724329B4A003C0B8F /* libjpeg.a in Frameworks */, 27FFF31524329B2D003C0B8F /* Security.framework in Frameworks */, 27FFF30E24329B2D003C0B8F /* libpappl_static.a in Frameworks */, 27FFF30F24329B2D003C0B8F /* IOKit.framework in Frameworks */, 27FFF31024329B2D003C0B8F /* libpam.tbd in Frameworks */, 27FFF31124329B2D003C0B8F /* libz.tbd in Frameworks */, 27FFF31224329B2D003C0B8F /* SystemConfiguration.framework in Frameworks */, 27FFF3032432901A003C0B8F /* libcups.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 274AD644253F52B10033CAAB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 274AD645253F52B10033CAAB /* CoreFoundation.framework in Frameworks */, 274AD646253F52B10033CAAB /* libusb-1.0.a in Frameworks */, 274AD647253F52B10033CAAB /* libpng16.a in Frameworks */, 274AD648253F52B10033CAAB /* libjpeg.a in Frameworks */, 274AD649253F52B10033CAAB /* Security.framework in Frameworks */, 274AD64A253F52B10033CAAB /* libpappl_static.a in Frameworks */, 274AD64B253F52B10033CAAB /* IOKit.framework in Frameworks */, 274AD64C253F52B10033CAAB /* libpam.tbd in Frameworks */, 274AD64D253F52B10033CAAB /* libz.tbd in Frameworks */, 274AD64E253F52B10033CAAB /* SystemConfiguration.framework in Frameworks */, 274AD64F253F52B10033CAAB /* libcups.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 27FFF30624329AAF003C0B8F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 27FFF38F24329C9E003C0B8F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 27FFF39A24329D30003C0B8F /* libjpeg.a in Frameworks */, 27FFF39B24329D30003C0B8F /* libpng16.a in Frameworks */, 27FFF39C24329D30003C0B8F /* libusb-1.0.a in Frameworks */, 27FFF39424329D16003C0B8F /* CoreFoundation.framework in Frameworks */, 27FFF39524329D16003C0B8F /* IOKit.framework in Frameworks */, 27FFF39624329D16003C0B8F /* libcups.tbd in Frameworks */, 27FFF39724329D16003C0B8F /* libpam.tbd in Frameworks */, 27FFF39824329D16003C0B8F /* libz.tbd in Frameworks */, 27FFF39924329D16003C0B8F /* SystemConfiguration.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 273C6EEB240D8729000F85E7 = { isa = PBXGroup; children = ( 27BFB1BB240DF06A00CE29F0 /* README.md */, 276EE266255061D300429BCE /* Documentation */, 27905C62240D8861001D2A90 /* pappl */, 274A1ECB242E7DAD00DE387E /* testsuite */, 273C6EF6240D8729000F85E7 /* Products */, 27EFC5D42415EB550082CEA3 /* Frameworks */, ); sourceTree = ""; }; 273C6EF6240D8729000F85E7 /* Products */ = { isa = PBXGroup; children = ( 274A1ED0242E7DEA00DE387E /* testpappl */, 27FFF30824329AAF003C0B8F /* libpappl_static.a */, 27FFF39324329C9E003C0B8F /* libpappl.a */, 274AD654253F52B10033CAAB /* testmainloop */, ); name = Products; sourceTree = ""; }; 274A1ECB242E7DAD00DE387E /* testsuite */ = { isa = PBXGroup; children = ( 274A1ED7242E7E1300DE387E /* pwg-driver.c */, 274AD666253F52E30033CAAB /* testmainloop.c */, 274A1ED8242E7E1300DE387E /* testpappl.c */, 274A1ED9242E7E1400DE387E /* testpappl.h */, ); name = testsuite; sourceTree = ""; }; 276EE266255061D300429BCE /* Documentation */ = { isa = PBXGroup; children = ( 276EE27C2550621400429BCE /* pappl-512.png */, 276EE27A2550621400429BCE /* pappl-body.md */, 276EE2672550620200429BCE /* pappl-client-body.man */, 276EE26E2550620200429BCE /* pappl-client.3 */, 276EE2712550620200429BCE /* pappl-device-body.man */, 276EE2772550620200429BCE /* pappl-device.3 */, 276EE2692550620200429BCE /* pappl-footer.man */, 276EE2732550620200429BCE /* pappl-job-body.man */, 276EE2702550620200429BCE /* pappl-job.3 */, 276EE2762550620200429BCE /* pappl-log-body.man */, 276EE26F2550620200429BCE /* pappl-log.3 */, 276EE26D2550620200429BCE /* pappl-mainloop-body.man */, 276EE2782550620200429BCE /* pappl-mainloop.3 */, 276EE2722550620200429BCE /* pappl-makeresheader.1 */, 276EE2682550620200429BCE /* pappl-printer-body.man */, 276EE26C2550620200429BCE /* pappl-printer.3 */, 276EE26B2550620200429BCE /* pappl-resource-body.man */, 276EE2792550620300429BCE /* pappl-resource.3 */, 276EE2752550620200429BCE /* pappl-system-body.man */, 276EE2742550620200429BCE /* pappl-system.3 */, 276EE26A2550620200429BCE /* pappl.1 */, 276EE27B2550621400429BCE /* pappl.html */, ); name = Documentation; sourceTree = ""; }; 27905C62240D8861001D2A90 /* pappl */ = { isa = PBXGroup; children = ( 27905C66240D8896001D2A90 /* base.h */, 27EFC5F3241E72910082CEA3 /* base-private.h */, 27905C6C240D8896001D2A90 /* client.c */, 27905C6E240D8896001D2A90 /* client.h */, 279D377224119E39008AECA4 /* client-accessors.c */, 27905C6F240D8896001D2A90 /* client-auth.c */, 27A564A525676AE3009501BD /* client-ipp.c */, 27905C8D240D9067001D2A90 /* client-private.h */, 27EE39CE242AE7D800179844 /* client-webif.c */, 27905C87240D8E69001D2A90 /* config.h */, 27F656E72430DBFC00055A4D /* contact.c */, 27905C65240D8896001D2A90 /* device.h */, 27214FA924ED72D600E36FFC /* device-private.h */, 27905C6B240D8896001D2A90 /* device.c */, 27F4285724F4080600C7ADCE /* device-file.c */, 27214FA324ED72B300E36FFC /* device-network.c */, 27214FA424ED72B400E36FFC /* device-usb.c */, 2719D1B424732B1700299DA1 /* dnssd-private.h */, 27905C73240D8896001D2A90 /* dnssd.c */, 273C6EF9240D8729000F85E7 /* Info.plist */, 27905C64240D8896001D2A90 /* job.c */, 27905C70240D8896001D2A90 /* job.h */, 279D377524119E3A008AECA4 /* job-accessors.c */, 27DF62F02450992D00501447 /* job-filter.c */, 27A564B225677057009501BD /* job-ipp.c */, 27905C8C240D9067001D2A90 /* job-private.h */, 27905C74240D8896001D2A90 /* job-process.c */, 27AB72B324740B3300691FE7 /* link.c */, 27905C72240D8896001D2A90 /* log.c */, 27905C8A240D9066001D2A90 /* log.h */, 2737B04624B3598400E6F38C /* log-private.h */, 27EFC5ED241C85DF0082CEA3 /* lookup.c */, 27D676202493D73E008F734C /* mainloop.c */, 27D676212493D73E008F734C /* mainloop.h */, 27D676222493D73E008F734C /* mainloop-private.h */, 27D6761F2493D73D008F734C /* mainloop-subcommands.c */, 27D6762B2493EF95008F734C /* mainloop-support.c */, 27905C68240D8896001D2A90 /* pappl.h */, 27905C69240D8896001D2A90 /* pappl-private.h */, 27905C6D240D8896001D2A90 /* printer.c */, 27905C63240D8896001D2A90 /* printer.h */, 279D377124119E37008AECA4 /* printer-accessors.c */, 27910A192421626800D01A3F /* printer-driver.c */, 27A56490256769A9009501BD /* printer-ipp.c */, 27905C8B240D9067001D2A90 /* printer-private.h */, 27E5AEA2246B6A4700FFD958 /* printer-raw.c */, 279D377424119E3A008AECA4 /* printer-support.c */, 2763648325223F3200949C0B /* printer-usb.c */, 273FA876240FED97007982BE /* printer-webif.c */, 273FA875240FED96007982BE /* resource.c */, 2737B04724B3598400E6F38C /* resource-private.h */, 27EFC5F0241DB8390082CEA3 /* snmp.c */, 27EFC5EF241DB8380082CEA3 /* snmp-private.h */, 27905C67240D8896001D2A90 /* system.c */, 27905C6A240D8896001D2A90 /* system.h */, 279D377324119E39008AECA4 /* system-accessors.c */, 27A56491256769A9009501BD /* system-ipp.c */, 27256319243D628F00A38E9F /* system-loadsave.c */, 27134E6B2548D1CD004D9027 /* system-printer.c */, 27905C89240D9066001D2A90 /* system-private.h */, 27EE39CF242AE7D900179844 /* system-webif.c */, 27F656E52430DB8D00055A4D /* util.c */, ); name = pappl; sourceTree = ""; }; 27EFC5D42415EB550082CEA3 /* Frameworks */ = { isa = PBXGroup; children = ( 27FFF31824329B4A003C0B8F /* libpng16.a */, 27FFF31424329B2D003C0B8F /* CoreGraphics.framework */, 27FFF31324329B2D003C0B8F /* Security.framework */, 27EFC5F5241E72AE0082CEA3 /* libpam.tbd */, 27EFC5E62415EBD70082CEA3 /* libjpeg.a */, 27EFC5E72415EBD70082CEA3 /* libpng16.a */, 27EFC5E52415EBD70082CEA3 /* libusb-1.0.a */, 27EFC5E32415EBA80082CEA3 /* IOKit.framework */, 27EFC5DF2415EB980082CEA3 /* libcups.tbd */, 27EFC5E02415EB980082CEA3 /* libcupsimage.tbd */, 27EFC5DD2415EB8A0082CEA3 /* libz.tbd */, 27EFC5DB2415EB740082CEA3 /* CoreFoundation.framework */, 27EFC5D92415EB6C0082CEA3 /* SystemConfiguration.framework */, 27EFC5D72415EB610082CEA3 /* libcupsimage.2.tbd */, 27EFC5D52415EB550082CEA3 /* libcups.2.tbd */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 27FFF30424329AAF003C0B8F /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 27FFF34324329B82003C0B8F /* base.h in Headers */, 2737B04924B3598400E6F38C /* log-private.h in Headers */, 27214FAB24ED72D600E36FFC /* device-private.h in Headers */, 27FFF34524329B82003C0B8F /* client.h in Headers */, 27FFF34824329B82003C0B8F /* device.h in Headers */, 27FFF34924329B83003C0B8F /* job.h in Headers */, 27FFF34B24329B83003C0B8F /* log.h in Headers */, 27FFF34C24329B83003C0B8F /* pappl.h in Headers */, 27FFF34E24329B83003C0B8F /* printer.h in Headers */, 2719D1B624732B1800299DA1 /* dnssd-private.h in Headers */, 27FFF35124329B83003C0B8F /* system.h in Headers */, 27FFF34424329B82003C0B8F /* base-private.h in Headers */, 27FFF34624329B82003C0B8F /* client-private.h in Headers */, 27D6762A2493D73E008F734C /* mainloop-private.h in Headers */, 27FFF34724329B82003C0B8F /* config.h in Headers */, 27FFF34A24329B83003C0B8F /* job-private.h in Headers */, 27FFF34D24329B83003C0B8F /* pappl-private.h in Headers */, 27FFF34F24329B83003C0B8F /* printer-private.h in Headers */, 2737B04B24B3598400E6F38C /* resource-private.h in Headers */, 27D676282493D73E008F734C /* mainloop.h in Headers */, 27FFF35024329B83003C0B8F /* snmp-private.h in Headers */, 27FFF35224329B83003C0B8F /* system-private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 27FFF35424329C9E003C0B8F /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 27FFF35524329C9E003C0B8F /* base.h in Headers */, 2737B04824B3598400E6F38C /* log-private.h in Headers */, 27214FAA24ED72D600E36FFC /* device-private.h in Headers */, 27FFF35624329C9E003C0B8F /* client.h in Headers */, 27FFF35724329C9E003C0B8F /* device.h in Headers */, 27FFF35824329C9E003C0B8F /* job.h in Headers */, 27FFF35924329C9E003C0B8F /* log.h in Headers */, 27FFF35A24329C9E003C0B8F /* pappl.h in Headers */, 27FFF35B24329C9E003C0B8F /* printer.h in Headers */, 2719D1B524732B1800299DA1 /* dnssd-private.h in Headers */, 27FFF35C24329C9E003C0B8F /* system.h in Headers */, 27FFF35D24329C9E003C0B8F /* base-private.h in Headers */, 27FFF35E24329C9E003C0B8F /* client-private.h in Headers */, 27D676292493D73E008F734C /* mainloop-private.h in Headers */, 27FFF35F24329C9E003C0B8F /* config.h in Headers */, 27FFF36024329C9E003C0B8F /* job-private.h in Headers */, 27FFF36124329C9E003C0B8F /* pappl-private.h in Headers */, 27FFF36224329C9E003C0B8F /* printer-private.h in Headers */, 2737B04A24B3598400E6F38C /* resource-private.h in Headers */, 27D676272493D73E008F734C /* mainloop.h in Headers */, 27FFF36324329C9E003C0B8F /* snmp-private.h in Headers */, 27FFF36424329C9E003C0B8F /* system-private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 274A1ECF242E7DEA00DE387E /* testpappl */ = { isa = PBXNativeTarget; buildConfigurationList = 274A1ED4242E7DEA00DE387E /* Build configuration list for PBXNativeTarget "testpappl" */; buildPhases = ( 274A1ECC242E7DEA00DE387E /* Sources */, 274A1ECD242E7DEA00DE387E /* Frameworks */, 274A1ECE242E7DEA00DE387E /* CopyFiles */, ); buildRules = ( ); dependencies = ( 27FFF30D24329AC7003C0B8F /* PBXTargetDependency */, ); name = testpappl; productName = testsuite; productReference = 274A1ED0242E7DEA00DE387E /* testpappl */; productType = "com.apple.product-type.tool"; }; 274AD63E253F52B10033CAAB /* testmainloop */ = { isa = PBXNativeTarget; buildConfigurationList = 274AD651253F52B10033CAAB /* Build configuration list for PBXNativeTarget "testmainloop" */; buildPhases = ( 274AD641253F52B10033CAAB /* Sources */, 274AD644253F52B10033CAAB /* Frameworks */, 274AD650253F52B10033CAAB /* CopyFiles */, ); buildRules = ( ); dependencies = ( 274AD63F253F52B10033CAAB /* PBXTargetDependency */, ); name = testmainloop; productName = testsuite; productReference = 274AD654253F52B10033CAAB /* testmainloop */; productType = "com.apple.product-type.tool"; }; 27FFF30724329AAF003C0B8F /* libpappl_static */ = { isa = PBXNativeTarget; buildConfigurationList = 27FFF30B24329AAF003C0B8F /* Build configuration list for PBXNativeTarget "libpappl_static" */; buildPhases = ( 27FFF30424329AAF003C0B8F /* Headers */, 27FFF30524329AAF003C0B8F /* Sources */, 27FFF30624329AAF003C0B8F /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = libpappl_static; productName = libpappl; productReference = 27FFF30824329AAF003C0B8F /* libpappl_static.a */; productType = "com.apple.product-type.library.static"; }; 27FFF35324329C9E003C0B8F /* libpappl */ = { isa = PBXNativeTarget; buildConfigurationList = 27FFF39024329C9E003C0B8F /* Build configuration list for PBXNativeTarget "libpappl" */; buildPhases = ( 27FFF35424329C9E003C0B8F /* Headers */, 27FFF36524329C9E003C0B8F /* Sources */, 27FFF38F24329C9E003C0B8F /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = libpappl; productName = libpappl; productReference = 27FFF39324329C9E003C0B8F /* libpappl.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 273C6EEC240D8729000F85E7 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1240; ORGANIZATIONNAME = "Michael Sweet"; TargetAttributes = { 274A1ECF242E7DEA00DE387E = { CreatedOnToolsVersion = 11.4; }; 27FFF2FA24328E24003C0B8F = { CreatedOnToolsVersion = 11.4; }; 27FFF30724329AAF003C0B8F = { CreatedOnToolsVersion = 11.4; }; }; }; buildConfigurationList = 273C6EEF240D8729000F85E7 /* Build configuration list for PBXProject "pappl" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 273C6EEB240D8729000F85E7; productRefGroup = 273C6EF6240D8729000F85E7 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 27FFF2FA24328E24003C0B8F /* All */, 27FFF35324329C9E003C0B8F /* libpappl */, 27FFF30724329AAF003C0B8F /* libpappl_static */, 274AD63E253F52B10033CAAB /* testmainloop */, 274A1ECF242E7DEA00DE387E /* testpappl */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 274A1ECC242E7DEA00DE387E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 274A1EDB242E7E1400DE387E /* pwg-driver.c in Sources */, 274A1EDC242E7E1400DE387E /* testpappl.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 274AD641253F52B10033CAAB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 274AD642253F52B10033CAAB /* pwg-driver.c in Sources */, 274AD667253F52E30033CAAB /* testmainloop.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 27FFF30524329AAF003C0B8F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 27FFF31A24329B61003C0B8F /* base.h in Sources */, 27FFF31B24329B61003C0B8F /* base-private.h in Sources */, 27D6762D2493EF96008F734C /* mainloop-support.c in Sources */, 27FFF31C24329B61003C0B8F /* client.h in Sources */, 27D676242493D73E008F734C /* mainloop-subcommands.c in Sources */, 27D676262493D73E008F734C /* mainloop.c in Sources */, 27A56495256769A9009501BD /* system-ipp.c in Sources */, 27FFF31D24329B61003C0B8F /* client-private.h in Sources */, 27FFF31E24329B61003C0B8F /* client.c in Sources */, 27FFF31F24329B61003C0B8F /* client-accessors.c in Sources */, 27FFF32024329B61003C0B8F /* client-auth.c in Sources */, 27FFF32124329B61003C0B8F /* client-webif.c in Sources */, 27FFF32224329B61003C0B8F /* config.h in Sources */, 27FFF32324329B61003C0B8F /* contact.c in Sources */, 27FFF32424329B61003C0B8F /* device.h in Sources */, 27FFF32524329B61003C0B8F /* device.c in Sources */, 27FFF32624329B61003C0B8F /* dnssd.c in Sources */, 27DF62F22450992D00501447 /* job-filter.c in Sources */, 27FFF32924329B61003C0B8F /* job.h in Sources */, 27FFF32A24329B61003C0B8F /* job-private.h in Sources */, 27FFF32B24329B61003C0B8F /* job.c in Sources */, 27FFF32C24329B61003C0B8F /* job-accessors.c in Sources */, 27FFF32D24329B61003C0B8F /* job-process.c in Sources */, 27FFF32E24329B61003C0B8F /* log.h in Sources */, 27214FA624ED72B400E36FFC /* device-network.c in Sources */, 27AB72B524740B3400691FE7 /* link.c in Sources */, 27FFF32F24329B61003C0B8F /* log.c in Sources */, 27FFF33024329B61003C0B8F /* lookup.c in Sources */, 27FFF33124329B61003C0B8F /* pappl.h in Sources */, 27FFF33224329B61003C0B8F /* pappl-private.h in Sources */, 27FFF33324329B61003C0B8F /* printer.h in Sources */, 27FFF33424329B61003C0B8F /* printer-private.h in Sources */, 27FFF33524329B61003C0B8F /* printer.c in Sources */, 27FFF33624329B61003C0B8F /* printer-accessors.c in Sources */, 27214FA824ED72B400E36FFC /* device-usb.c in Sources */, 27E5AEA4246B6A4800FFD958 /* printer-raw.c in Sources */, 27FFF33724329B61003C0B8F /* printer-driver.c in Sources */, 27FFF33824329B61003C0B8F /* printer-support.c in Sources */, 27FFF33924329B61003C0B8F /* printer-webif.c in Sources */, 27FFF33A24329B61003C0B8F /* resource.c in Sources */, 27FFF33B24329B61003C0B8F /* snmp-private.h in Sources */, 27A564B425677057009501BD /* job-ipp.c in Sources */, 27FFF33C24329B61003C0B8F /* snmp.c in Sources */, 27FFF33D24329B61003C0B8F /* system.h in Sources */, 2763648525223F3200949C0B /* printer-usb.c in Sources */, 27FFF33E24329B61003C0B8F /* system-private.h in Sources */, 27FFF33F24329B61003C0B8F /* system.c in Sources */, 27FFF34024329B61003C0B8F /* system-accessors.c in Sources */, 27134E6D2548D1CD004D9027 /* system-printer.c in Sources */, 27FFF34124329B61003C0B8F /* system-webif.c in Sources */, 2725631B243D629000A38E9F /* system-loadsave.c in Sources */, 27FFF34224329B61003C0B8F /* util.c in Sources */, 27F4285924F4080600C7ADCE /* device-file.c in Sources */, 27A564A725676AE3009501BD /* client-ipp.c in Sources */, 27A56493256769A9009501BD /* printer-ipp.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 27FFF36524329C9E003C0B8F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 27FFF36624329C9E003C0B8F /* base.h in Sources */, 27FFF36724329C9E003C0B8F /* base-private.h in Sources */, 27D6762C2493EF96008F734C /* mainloop-support.c in Sources */, 27FFF36824329C9E003C0B8F /* client.h in Sources */, 27D676232493D73E008F734C /* mainloop-subcommands.c in Sources */, 27D676252493D73E008F734C /* mainloop.c in Sources */, 27A56494256769A9009501BD /* system-ipp.c in Sources */, 27FFF36924329C9E003C0B8F /* client-private.h in Sources */, 27FFF36A24329C9E003C0B8F /* client.c in Sources */, 27FFF36B24329C9E003C0B8F /* client-accessors.c in Sources */, 27FFF36C24329C9E003C0B8F /* client-auth.c in Sources */, 27FFF36D24329C9E003C0B8F /* client-webif.c in Sources */, 27FFF36E24329C9E003C0B8F /* config.h in Sources */, 27FFF36F24329C9E003C0B8F /* contact.c in Sources */, 27FFF37024329C9E003C0B8F /* device.h in Sources */, 27FFF37124329C9E003C0B8F /* device.c in Sources */, 27FFF37224329C9E003C0B8F /* dnssd.c in Sources */, 27DF62F12450992D00501447 /* job-filter.c in Sources */, 27FFF37524329C9E003C0B8F /* job.h in Sources */, 27FFF37624329C9E003C0B8F /* job-private.h in Sources */, 27FFF37724329C9E003C0B8F /* job.c in Sources */, 27FFF37824329C9E003C0B8F /* job-accessors.c in Sources */, 27FFF37924329C9E003C0B8F /* job-process.c in Sources */, 27FFF37A24329C9E003C0B8F /* log.h in Sources */, 27214FA524ED72B400E36FFC /* device-network.c in Sources */, 27AB72B424740B3400691FE7 /* link.c in Sources */, 27FFF37B24329C9E003C0B8F /* log.c in Sources */, 27FFF37C24329C9E003C0B8F /* lookup.c in Sources */, 27FFF37D24329C9E003C0B8F /* pappl.h in Sources */, 27FFF37E24329C9E003C0B8F /* pappl-private.h in Sources */, 27FFF37F24329C9E003C0B8F /* printer.h in Sources */, 27FFF38024329C9E003C0B8F /* printer-private.h in Sources */, 27FFF38124329C9E003C0B8F /* printer.c in Sources */, 27FFF38224329C9E003C0B8F /* printer-accessors.c in Sources */, 27214FA724ED72B400E36FFC /* device-usb.c in Sources */, 27E5AEA3246B6A4800FFD958 /* printer-raw.c in Sources */, 27FFF38324329C9E003C0B8F /* printer-driver.c in Sources */, 27FFF38424329C9E003C0B8F /* printer-support.c in Sources */, 27FFF38524329C9E003C0B8F /* printer-webif.c in Sources */, 27FFF38624329C9E003C0B8F /* resource.c in Sources */, 27FFF38724329C9E003C0B8F /* snmp-private.h in Sources */, 27A564B325677057009501BD /* job-ipp.c in Sources */, 27FFF38824329C9E003C0B8F /* snmp.c in Sources */, 27FFF38924329C9E003C0B8F /* system.h in Sources */, 2763648425223F3200949C0B /* printer-usb.c in Sources */, 27FFF38A24329C9E003C0B8F /* system-private.h in Sources */, 27FFF38B24329C9E003C0B8F /* system.c in Sources */, 27FFF38C24329C9E003C0B8F /* system-accessors.c in Sources */, 27134E6C2548D1CD004D9027 /* system-printer.c in Sources */, 27FFF38D24329C9E003C0B8F /* system-webif.c in Sources */, 2725631A243D629000A38E9F /* system-loadsave.c in Sources */, 27FFF38E24329C9E003C0B8F /* util.c in Sources */, 27F4285824F4080600C7ADCE /* device-file.c in Sources */, 27A564A625676AE3009501BD /* client-ipp.c in Sources */, 27A56492256769A9009501BD /* printer-ipp.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 274AD63F253F52B10033CAAB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 27FFF30724329AAF003C0B8F /* libpappl_static */; targetProxy = 274AD640253F52B10033CAAB /* PBXContainerItemProxy */; }; 274AD665253F52C90033CAAB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 274AD63E253F52B10033CAAB /* testmainloop */; targetProxy = 274AD664253F52C90033CAAB /* PBXContainerItemProxy */; }; 27FFF30124328E31003C0B8F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 274A1ECF242E7DEA00DE387E /* testpappl */; targetProxy = 27FFF30024328E31003C0B8F /* PBXContainerItemProxy */; }; 27FFF30D24329AC7003C0B8F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 27FFF30724329AAF003C0B8F /* libpappl_static */; targetProxy = 27FFF30C24329AC7003C0B8F /* PBXContainerItemProxy */; }; 27FFF39E24329D3B003C0B8F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 27FFF35324329C9E003C0B8F /* libpappl */; targetProxy = 27FFF39D24329D3B003C0B8F /* PBXContainerItemProxy */; }; 27FFF3A024329D3B003C0B8F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 27FFF30724329AAF003C0B8F /* libpappl_static */; targetProxy = 27FFF39F24329D3B003C0B8F /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 273C6EFB240D8729000F85E7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_GCD_PERFORMANCE = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; CLANG_WARN_ASSIGN_ENUM = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_FLOAT_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1.0; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; GCC_WARN_PEDANTIC = YES; GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( ., .., "/usr/local/include/libusb-1.0", /usr/local/include, ); LIBRARY_SEARCH_PATHS = /usr/local/lib; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = "-Wno-format-pedantic"; }; name = Debug; }; 273C6EFC240D8729000F85E7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_GCD_PERFORMANCE = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; CLANG_WARN_ASSIGN_ENUM = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_FLOAT_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "Apple Development"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; GCC_WARN_PEDANTIC = YES; GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( ., .., "/usr/local/include/libusb-1.0", /usr/local/include, ); LIBRARY_SEARCH_PATHS = /usr/local/lib; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = "-Wno-format-pedantic"; }; name = Release; }; 274A1ED5242E7DEA00DE387E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Debug; }; 274A1ED6242E7DEA00DE387E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpappl; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Release; }; 274AD652253F52B10033CAAB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpappl; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Debug; }; 274AD653253F52B10033CAAB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Release; }; 27FFF2FC24328E24003C0B8F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 27FFF2FD24328E24003C0B8F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; 27FFF30924329AAF003C0B8F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 27FFF30A24329AAF003C0B8F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; 27FFF39124329C9E003C0B8F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; ENABLE_HARDENED_RUNTIME = YES; EXECUTABLE_PREFIX = ""; MACH_O_TYPE = mh_dylib; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 27FFF39224329C9E003C0B8F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = RU58A2256H; ENABLE_HARDENED_RUNTIME = YES; EXECUTABLE_PREFIX = ""; MACH_O_TYPE = mh_dylib; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 273C6EEF240D8729000F85E7 /* Build configuration list for PBXProject "pappl" */ = { isa = XCConfigurationList; buildConfigurations = ( 273C6EFB240D8729000F85E7 /* Debug */, 273C6EFC240D8729000F85E7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 274A1ED4242E7DEA00DE387E /* Build configuration list for PBXNativeTarget "testpappl" */ = { isa = XCConfigurationList; buildConfigurations = ( 274A1ED5242E7DEA00DE387E /* Debug */, 274A1ED6242E7DEA00DE387E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 274AD651253F52B10033CAAB /* Build configuration list for PBXNativeTarget "testmainloop" */ = { isa = XCConfigurationList; buildConfigurations = ( 274AD652253F52B10033CAAB /* Debug */, 274AD653253F52B10033CAAB /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 27FFF2FB24328E24003C0B8F /* Build configuration list for PBXAggregateTarget "All" */ = { isa = XCConfigurationList; buildConfigurations = ( 27FFF2FC24328E24003C0B8F /* Debug */, 27FFF2FD24328E24003C0B8F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 27FFF30B24329AAF003C0B8F /* Build configuration list for PBXNativeTarget "libpappl_static" */ = { isa = XCConfigurationList; buildConfigurations = ( 27FFF30924329AAF003C0B8F /* Debug */, 27FFF30A24329AAF003C0B8F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 27FFF39024329C9E003C0B8F /* Build configuration list for PBXNativeTarget "libpappl" */ = { isa = XCConfigurationList; buildConfigurations = ( 27FFF39124329C9E003C0B8F /* Debug */, 27FFF39224329C9E003C0B8F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 273C6EEC240D8729000F85E7 /* Project object */; } pappl-1.0.3/xcode/pappl.xcodeproj/project.xcworkspace/000077500000000000000000000000001403603036100230335ustar00rootroot00000000000000pappl-1.0.3/xcode/pappl.xcodeproj/project.xcworkspace/contents.xcworkspacedata000066400000000000000000000002261403603036100277750ustar00rootroot00000000000000 pappl-1.0.3/xcode/pappl.xcodeproj/project.xcworkspace/xcshareddata/000077500000000000000000000000001403603036100254665ustar00rootroot00000000000000pappl-1.0.3/xcode/pappl.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist000066400000000000000000000003561403603036100321500ustar00rootroot00000000000000 IDEDidComputeMac32BitWarning